Compare commits

..

2 Commits

Author SHA1 Message Date
Trust 2434bab697 Updated for bypass all rule 2024-02-25 16:00:44 -05:00
Trust 7a5a755033 [Bug Fix] Cleanup NPC Mana Tap Logic
Mana Tap rule logic was invalid - Cleaned up and simplified, either we care about npc mana or we dont.
2024-02-25 15:20:58 -05:00
784 changed files with 55857 additions and 113683 deletions
-6
View File
@@ -1,6 +0,0 @@
!Makefile
base/*.sql
base/*.zip
base/db/
base/maps/
!base/expansion/Makefile
@@ -1,9 +0,0 @@
# This is a "dev" image for running eqemu in development, also for dev containers
ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID
FROM --platform=linux/arm64 mcr.microsoft.com/devcontainers/base:debian
RUN sudo apt update && sudo apt install -y --no-install-recommends build-essential libtool cmake curl debconf-utils git libluabind-dev libsodium-dev liblua5.2-0 liblua5.2-dev libmariadb-dev libssl-dev minizip make mariadb-client locales nano open-vm-tools unzip uuid-dev iputils-ping wget libcurl4-openssl-dev gdb libyaml-cpp-dev ccache ninja-build pv mariadb-server libperl-dev libjson-perl libio-stringy-perl liblua5.1-dev libluabind-dev libboost-dev mariadb-server valgrind telnet libgoogle-perftools-dev google-perftools
USER $USERNAME
-9
View File
@@ -1,9 +0,0 @@
# This is a "dev" image for running eqemu in development, also for dev containers
ARG USERNAME=user-name-goes-here
ARG USER_UID=1000
ARG USER_GID=$USER_UID
FROM mcr.microsoft.com/devcontainers/base:debian
RUN sudo apt update && sudo apt install -y --no-install-recommends build-essential libtool cmake curl debconf-utils git libluabind-dev libsodium-dev liblua5.2-0 liblua5.2-dev libmariadb-dev libssl-dev minizip make mariadb-client locales nano open-vm-tools unzip uuid-dev iputils-ping wget libcurl4-openssl-dev gdb libyaml-cpp-dev ccache ninja-build pv mariadb-server libperl-dev libjson-perl libio-stringy-perl liblua5.1-dev libluabind-dev libboost-dev mariadb-server valgrind telnet libgoogle-perftools-dev google-perftools
USER $USERNAME
-284
View File
@@ -1,284 +0,0 @@
NAME := eqemu-server
.ONESHELL:
DOCKER_ARGS := --rm --name ${NAME} -v $$PWD:/src -w /src ${NAME}
DOCKER_ARM64_ARGS := --rm --platform linux/arm64 --name ${NAME}-arm64 -v $$PWD:/src -w /src ${NAME}-arm64
.PHONY: build
build:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile build --no-print-directory
exit
endif
cd build$$BUILD_SUFFIX && cmake --build . --config Release --target all --
.PHONY: cmake
cmake:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile cmake --no-print-directory
exit
endif
@echo "working directory: $$PWD"
mkdir -p build$$BUILD_SUFFIX
@cd build$$BUILD_SUFFIX && cmake -DEQEMU_BUILD_LOGIN=ON \
-DEQEMU_BUILD_TESTS=ON \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -G Ninja ..
clean:
ifneq (,$(findstring .devcontainer,$$PWD))
@make -C ../ -f .devcontainer/Makefile clean --no-print-directory
endif
rm -rf build
docker-cmake:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile docker-cmake --no-print-directory
exit
endif
@echo "working directory: $$PWD"
git submodule update --init --recursive
docker run ${DOCKER_ARGS} make cmake
docker-build:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile docker-build --no-print-directory
exit
endif
docker run ${DOCKER_ARGS} make build
# Build image if it doesn't exist
docker-image-build:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile docker-image-build --no-print-directory
exit
endif
ifeq ($(shell docker images -q ${NAME} 2> /dev/null),)
@echo "Docker image not found. Building..."
docker build -f Dockerfile.debian.dev -t ${NAME} .
endif
docker-arm-cmake: docker-arm-image-build
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile docker-arm-cmake --no-print-directory
exit
endif
git submodule update --init --recursive
docker run ${DOCKER_ARM64_ARGS} make cmake BUILD_SUFFIX=arm64
docker-arm-build: docker-arm-image-build
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile docker-arm-build --no-print-directory
exit
endif
docker run ${DOCKER_ARM64_ARGS} make build BUILD_SUFFIX=arm64
docker-arm-image-build:
ifeq ($(shell docker images -q ${NAME}-arm64 2> /dev/null),)
@echo "Docker image not found. Building..."
docker build -f Dockerfile.debian.arm.dev -t ${NAME}-arm64 .
endif
docker-clean: clean
.PHONY: prep
prep:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile prep --no-print-directory
exit
endif
@echo "Preparing build/bin for usage..."
mkdir -p build/bin/assets/patches
cp -R -u -p .devcontainer/base/eqemu_config.json build/bin/eqemu_config.json
cp -R -u -p .devcontainer/base/login.json build/bin/login.json
cp -R -u -p loginserver/login_util/* build/bin/assets/patches/
mkdir -p build/bin/assets
cp -R -u -p utils/patches build/bin/assets/
-unlink build/bin/lua_modules
cd build/bin && ln -s quests/lua_modules lua_modules
-unlink build/bin/mods
cd build/bin && ln -s quests/mods mods
-unlink build/bin/maps
cd build/bin && ln -s ../../base/maps maps
mkdir -p build/bin/logs
mkdir -p build/bin/shared
@echo "Eqemu is prepared. Edit build/bin/eqemu_config.json to configure."
maps:
@echo "Downloading maps..."
@mkdir -p base/maps
@cd base/maps && wget -nc https://github.com/Akkadius/eqemu-maps/archive/refs/heads/master.zip
@cd base/maps && unzip -o master.zip
@cd base/maps && mv eqemu-maps-master/* .
@cd base/maps && rm -rf eqemu-maps-master
@echo "Maps downloaded."
quests:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile quests --no-print-directory
exit
endif
@cd build/bin && git clone https://github.com/ProjectEQ/projecteqquests.git quests
# Runs tests
.PHONY: test
test:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile test --no-print-directory
exit
endif
cd build/bin && ./tests
# Runs login binary
.PHONY: login
login:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile login --no-print-directory
exit
endif
cd build/bin && ./loginserver
# Runs shared_memory binary
.PHONY: shared
shared:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile shared --no-print-directory
exit
endif
cd build/bin && ./shared_memory
# Runs zone binary
.PHONY: zone
zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile zone --no-print-directory
exit
endif
@-rm build/bin/logs/zone/zone*.log
cd build/bin && ./zone
# Runs world binary
.PHONY: world
world:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile world --no-print-directory
exit
endif
@-rm build/bin/logs/world*.log
cd build/bin && ./world
# Runs ucs binary
.PHONY: ucs
ucs:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile ucs --no-print-directory
exit
endif
@-rm build/bin/logs/ucs*.log
cd build/bin && ./ucs
# Runs queryserv binary
.PHONY: queryserv
queryserv:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile queryserv --no-print-directory
exit
endif
@-rm build/bin/logs/query_server*.log
cd build/bin && ./queryserv
valgrind-%:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile valgrind --no-print-directory
exit
endif
cd build/bin && valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=logs/$*.valgrind.log ./$*
# Start mariaDB standalone
.PHONY: mariadb
mariadb:
@sudo service mariadb start
.PHONY: inject-mariadb
inject-mariadb:
-sudo service mariadb start
-mkdir -p base/db/
-sudo mariadb -e 'DROP DATABASE IF EXISTS peq;'
-sudo mariadb -e 'CREATE DATABASE peq;'
-sudo mariadb -e "CREATE USER 'peq'@'127.0.0.1' IDENTIFIED BY 'peqpass';"
-sudo mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'peq'@'127.0.0.1';"
ifeq (,$(wildcard base/db/db.sql.zip))
@echo "base/db.sql.zip not found. Downloading latest from https://db.projecteq.net/"
wget -nc https://db.projecteq.net/latest -O base/db/db.sql.zip
-cd base/db && unzip db.sql.zip
endif
@echo "Sourcing db may take a while, please wait..."
@cd base/db/peq-dump && sudo mariadb --database peq -e "source create_all_tables.sql"
@echo "MariaDB is now injected."
.PHONY: gm-%
gm-%:
sudo mariadb --database peq -e "UPDATE account SET status=255 WHERE name = '$*';"
@echo "Account $* is now a GM. /camp to have it go into effect."
depends:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile depends --no-print-directory
exit
endif
sudo apt install graphviz pip time
pip3 install graphviz
mkdir -p build/depends
@if [ ! -f "build/depends/dependency_graph.py" ]; then \
wget https://raw.githubusercontent.com/pvigier/dependency-graph/master/dependency_graph.py -O build/depends/dependency_graph.py; \
fi
@echo "Generating dependency graphs (This may take a while)..."
@echo "Login..."
time python3 build/depends/dependency_graph.py -f png login build/depends/login.dot
@echo "World..."
time python3 build/depends/dependency_graph.py -f png world build/depends/world.dot
@echo "Zone..."
time python3 build/depends/dependency_graph.py -f png zone build/depends/zone.dot
@echo "Common..."
time python3 build/depends/dependency_graph.py -f png common build/depends/common.dot
backup:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile backup --no-print-directory
exit
endif
@mkdir -p build/bin/backup
cd build/bin && ./world database:dump --compress --player-tables --state-tables --system-tables --query-serv-tables
cpu-zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile cpu-zone --no-print-directory
exit
endif
@cd build/bin && mkdir -p tmp
cd build/bin && CPUPROFILE=prof.out ./zone
pprof-zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile pprof-zone --no-print-directory
exit
endif
cd build/bin && google-pprof --pdf zone prof.out > prof.pdf
pprof-web-zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile pprof-web-zone --no-print-directory
exit
endif
cd build/bin && google-pprof --web zone prof.out
pprof-gv-zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile pprof-gv-zone --no-print-directory
exit
endif
cd build/bin && google-pprof --gv zone prof.out > prof.gv
heap-zone:
ifeq ($(findstring .devcontainer,$(CURDIR)),.devcontainer)
@make -C ../ -f .devcontainer/Makefile heap-zone --no-print-directory
exit
endif
@cd build/bin && mkdir -p tmp
cd build/bin && HEAPPROFILE=prof.out ./zone
-73
View File
@@ -1,73 +0,0 @@
{
"server": {
"zones": {
"defaultstatus": "0",
"ports": {
"low": "7000",
"high": "7400"
}
},
"qsdatabase": {
"host": "127.0.0.1",
"port": "3306",
"username": "peq",
"password": "peqpass",
"db": "peq"
},
"chatserver": {
"port": "7778",
"host": ""
},
"mailserver": {
"host": "",
"port": "7778"
},
"webinterface": {
"port": "9081"
},
"world": {
"longname": "New Devbox",
"address": "192.168.1.100",
"localaddress": "192.168.1.100",
"loginserver1": {
"account": "",
"password": "",
"legacy": 0,
"host": "login.projecteq.net",
"port": "5998"
},
"tcp": {
"ip": "127.0.0.1",
"port": "9001"
},
"telnet": {
"ip": "0.0.0.0",
"port": "9000",
"enabled": "true"
},
"key": "random-generate-here",
"http": {
"port": "9080",
"enabled": "true",
"mimefile": "mime.types"
},
"shortname": "dev"
},
"database": {
"db": "peq",
"host": "127.0.0.1",
"port": "3306",
"username": "peq",
"password": "peqpass"
},
"files": {
"opcodes": "assets/patches/opcodes.conf",
"mail_opcodes": "assets/patches/mail_opcodes.conf"
},
"directories": {
"patches": "assets/patches/",
"opcodes": "assets/patches/",
"plugins": "quests/plugins/"
}
}
}
-37
View File
@@ -1,37 +0,0 @@
{
"database": {
"host": "127.0.0.1",
"port": "3306",
"db": "peq",
"user": "peq",
"password": "peqpass"
},
"account": {
"auto_create_accounts": true
},
"worldservers": {
"unregistered_allowed": true,
"reject_duplicate_servers": false
},
"web_api": {
"enabled": true,
"port": 6000
},
"security": {
"mode": 14,
"allow_password_login": true,
"allow_token_login": true
},
"logging": {
"trace": false,
"world_trace": false,
"dump_packets_in": false,
"dump_packets_out": false
},
"client_configuration": {
"titanium_port": 5998,
"titanium_opcodes": "assets/patches/login_opcodes.conf",
"sod_port": 5999,
"sod_opcodes": "assets/patches/login_opcodes_sod.conf"
}
}
-49
View File
@@ -1,49 +0,0 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
{
"name": "eqemu",
"build": {
"dockerfile": "Dockerfile.debian.dev"
},
"appPort": [
"5998:5998/udp",
"7000:7000/udp",
"7001:7001/udp",
"7002:7002/udp",
"7003:7003/udp",
"7004:7004/udp",
"7005:7005/udp",
"9000:9000/udp",
"9001:9001/udp"
],
"forwardPorts": [
3306
],
"remoteEnv": {
"LOCALWSF": "${localWorkspaceFolder}",
"CONTAINERWSF": "${containerWorkspaceFolder}"
},
"containerUser": "vscode",
// add ptrace
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined"
],
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools-extension-pack",
"vilicvane.sensitive-replace",
"maattdd.gitless",
"bibhasdn.unique-lines",
"GitHub.copilot",
"xackery.make-magic",
"Gruntfuggly.todo-tree",
"ms-vscode.cmake-tools"
]
}
},
"workspaceFolder": "/src",
"workspaceMount": "source=${localWorkspaceFolder},target=/src,type=bind,consistency=cached"
}
+3 -1
View File
@@ -61,6 +61,9 @@ bin/
compile_flags.txt
.cache/
# vscode generated settings
.vscode/
# Build pipeline
!utils/scripts/build/
!utils/scripts/build/should-release/should-release
@@ -68,4 +71,3 @@ compile_flags.txt
# CMake Files
cmake-build-relwithdebinfo/*
skill-caps.diff
-23
View File
@@ -1,23 +0,0 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${default}",
"${workspaceFolder}/submodules/fmt/include",
"${workspaceFolder}/submodules/cereal/include",
"${workspaceFolder}/submodules/glm",
"${workspaceFolder}/submodules/libuv/include"
],
"defines": [
"LUA_EQEMU=1"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
-173
View File
@@ -1,173 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) attach",
"type": "cppdbg",
"request": "attach",
"program": "${workspaceFolder}/build/bin/world",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) shared_memory",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/shared_memory",
"cwd": "${workspaceFolder}/build/bin",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) world",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/world",
"cwd": "${workspaceFolder}/build/bin",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) zone",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/zone",
"cwd": "${workspaceFolder}/build/bin",
"args": [
"",
],
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) zone neriakb",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/zone",
"cwd": "${workspaceFolder}/build/bin",
"args": [
"neriakb",
],
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) login",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/loginserver",
"cwd": "${workspaceFolder}/build/bin",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) ucs",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/ucs",
"cwd": "${workspaceFolder}/build/bin",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "(gdb) queryserv",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/queryserv",
"cwd": "${workspaceFolder}/build/bin",
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
-136
View File
@@ -1,136 +0,0 @@
{
"editor.detectIndentation": false,
"editor.insertSpaces": false,
"editor.tabSize": 4,
"editor.autoIndent": "full",
"editor.trimAutoWhitespace": true,
"files.trimTrailingWhitespace": true,
//"editor.formatOnSave": true,
"search.exclude": {
"dependencies": false,
},
"C_Cpp.default.includePath": [
"/usr/include/x86_64-linux-gnu",
"/usr/include/lua5.2",
"/usr/include/mariadb",
"${workspaceFolder}/dependencies/curl_x64/include",
"${workspaceFolder}/dependencies/fmt/include",
"${workspaceFolder}/dependencies/glm",
"${workspaceFolder}/dependencies/libuv/include",
"${workspaceFolder}/dependencies/sol2",
"${workspaceFolder}/dependencies/zlibng"
],
"telemetry.enableTelemetry": false,
"cmake.buildDirectory": "${workspaceFolder}/build",
"cmake.configureArgs": [
"-DEQEMU_BUILD_LOGIN=ON",
"-DEQEMU_BUILD_TESTS=ON",
"-DCMAKE_CXX_COMPILER_LAUNCHER=ccache",
"-DEQEMU_ADD_PROFILER=ON",
"Ninja"
],
"cmake.skipConfigureIfCachePresent": true,
"cmake.configureOnOpen": false,
"files.associations": {
"*.ipp": "cpp",
"functional": "cpp",
"string": "cpp",
"iostream": "cpp",
"map": "cpp",
"fstream": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"cstring": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"strstream": "cpp",
"bit": "cpp",
"bitset": "cpp",
"chrono": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"cstdint": "cpp",
"deque": "cpp",
"forward_list": "cpp",
"list": "cpp",
"set": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"regex": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"cfenv": "cpp",
"cinttypes": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"valarray": "cpp",
"variant": "cpp",
"csetjmp": "cpp",
"charconv": "cpp",
"format": "cpp",
"ranges": "cpp",
"span": "cpp"
},
"cmake.statusbar.advanced": {
"kit": {
"visibility": "hidden",
},
"debug": {
"visibility": "hidden",
},
"buildTarget": {
"visibility": "hidden",
},
"launch": {
"visibility": "hidden",
},
"ctest": {
"visibility": "icon",
}
}
}
-1814
View File
File diff suppressed because it is too large Load Diff
-13
View File
@@ -31,18 +31,7 @@ IF (EQEMU_BUILD_STATIC)
ENDIF ()
ENDIF (EQEMU_BUILD_STATIC)
# Requires libgoogle-perftools-dev google-perftools packages for linux (debian)
IF(EQEMU_ADD_PROFILER)
SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--no-as-needed,-lprofiler,--as-needed")
ENDIF(EQEMU_ADD_PROFILER)
IF(USE_MAP_MMFS)
ADD_DEFINITIONS(-DUSE_MAP_MMFS)
ENDIF (USE_MAP_MMFS)
IF(MSVC)
add_compile_options(/bigobj)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS)
ADD_DEFINITIONS(-DNOMINMAX)
ADD_DEFINITIONS(-DCRASH_LOGGING)
@@ -363,8 +352,6 @@ MESSAGE(STATUS "**************************************************")
#setup server libs and headers
SET(SERVER_LIBS common ${DATABASE_LIBRARY_LIBS} ${ZLIB_LIBRARY_LIBS} ${Boost_LIBRARIES} uv_a fmt RecastNavigation::Detour)
set(FMT_HEADER_ONLY OFF)
INCLUDE_DIRECTORIES(SYSTEM "${DATABASE_LIBRARY_INCLUDE}")
INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_LIBRARY_INCLUDE}")
INCLUDE_DIRECTORIES(SYSTEM "${Boost_INCLUDE_DIRS}")
+56 -123
View File
@@ -1,150 +1,83 @@
<h1 align="center">EQEmulator Server Platform</h1>
<p align="center">
<img src="https://github.com/user-attachments/assets/11942e15-b512-402d-a619-0543c7f1151e" style="border-radius: 10px">
</p>
<p align="center">
<b>EQEmulator - A Fan-Made Project Honoring a Legendary MMORPG</b>
</p>
<p align="center">
<a href="https://github.com/eqemu/server/graphs/contributors"><img src="https://img.shields.io/github/contributors/eqemu/server" alt="Contributors"></a>
<a href="https://discord.gg/QHsm7CD"><img src="https://img.shields.io/discord/212663220849213441?label=Discord&amp;logo=discord&amp;color=7289DA" alt="Discord"></a>
<a href="https://docs.eqemu.io"><img src="https://img.shields.io/badge/docs-MkDocs%20Powered-blueviolet" alt="Docs"></a>
<a href="./LICENSE"><img src="https://img.shields.io/github/license/EQEmu/Server" alt="License"></a>
<a href="https://github.com/eqemu/server/releases"><img src="https://img.shields.io/github/v/release/eqemu/server" alt="Latest Release"></a>
<a href="https://github.com/EQEmu/Server/releases"><img src="https://img.shields.io/github/release-date/EQEmu/Server" alt="Release Date"></a>
<img src="https://img.shields.io/github/downloads/eqemu/server/total.svg" alt="Github All Releases"></a>
<a href="http://drone.akkadius.com/EQEmu/Server"><img src="http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg" alt="Build Status"></a>
<img src="https://img.shields.io/github/issues-pr-closed/eqemu/server" alt="GitHub Issues or Pull Requests">
<img src="https://img.shields.io/docker/pulls/akkadius/eqemu-server" alt="Docker Pulls">
<a href="http://drone.akkadius.com/EQEmu/Server"><img src="http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg" alt="Build Status"></a> <img src="https://jb.gg/badges/official-plastic.svg" alt="Official">
</p>
# EQEmulator Core Server
| Drone (Linux x64) | Drone (Windows x64) |
|:---:|:---:|
|[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |[![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) |
***
<p align="center">
EQEmulator is a <b>passion-driven</b>, <b>open source server emulator</b> project dedicated to preserving and celebrating the legacy of a groundbreaking classic MMORPG.
</p>
**EQEmulator is a custom completely from-scratch open source server implementation for EverQuest built mostly on C++**
* MySQL/MariaDB is used as the database engine (over 200+ tables)
* Perl and LUA are both supported scripting languages for NPC/Player/Quest oriented events
* Open source database (Project EQ) has content up to expansion OoW (included in server installs)
* Game server environments and databases can be heavily customized to create all new experiences
* Hundreds of Quests/events created and maintained by Project EQ
<p align="center">
For over two decades and continuing, EQEmulator has served as a <strong>fan tribute</strong>, providing tools and technology that allow players to explore, customize, and experience the legendary game's iconic gameplay in new ways. This project exists solely out of <strong>deep admiration</strong> for the original developers, artists, designers, and visionaries who created one of the most influential online worlds of all time.
</p>
## Server Installs
| |Windows|Linux|
|:---:|:---:|:---:|
|**Install Count**|![Windows Install Count](http://analytics.akkadius.com/?install_count&windows_count)|![Linux Install Count](http://analytics.akkadius.com/?install_count&linux_count)|
### > Windows
<p align="center">
We do not claim ownership of the original game or its assets. <strong>All credit and respect belong to the original creators and Daybreak Game Company</strong>, whose work continues to inspire generations of players and developers alike.
</p>
* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-windows/)
<p align="center">
EQEmulator has for over 20 years and always will be a <strong>fan-based, non-commercial open-source effort</strong> made by players, for players—preserving the legacy of the game while empowering community-driven creativity, learning and joy that the game and its creators has so strongly inspired in us all.
</p>
### > Debian/Ubuntu/CentOS/Fedora
***
* [Install Guide](https://docs.eqemu.io/server/installation/server-installation-linux/)
<h3 align="center">
Technical Overview & Reverse Engineering Effort
</h1>
* You can use curl or wget to kick off the installer (whichever your OS has)
> curl -O https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh install.sh && chmod 755 install.sh && ./install.sh
<p align="center">EQEmulator represents <strong>over two decades of collaborative reverse engineering</strong>, building the server from the ground up without access to the original source code. This effort was achieved entirely through <strong>community-driven analysis, network protocol decoding, and in-game behavioral research</strong>.</p>
> wget --no-check-certificate https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/linux_installer/install.sh -O install.sh && chmod 755 install.sh && ./install.sh
<h1 align="center">
💡 How We Did It
</h1>
<p align="center">
<img src="https://github.com/user-attachments/assets/b6b48cf7-f64a-4497-9750-71f442a3d132" height="300px">
</p>
<p align="center">
<strong>Reverse Engineering</strong>
Every system, packet, opcode, and game mechanic has been reconstructed through countless hours of live packet sniffing, client disassembly, and in-game experimentation by dedicated contributors over the years.
</p>
<p align="center">
No proprietary code or server sources were ever used.
</p>
<p align="center">
All implementations are the result of clean-room engineering.
</p>
<h1 align="center">
🛠️ Technology Stack
</h1>
<p align="center">
<img src="https://github.com/user-attachments/assets/df5ea809-86c5-439d-a8fa-651fb04ba477" style="border-radius: 10px">
</p>
**C++ Core Engine**
* High-performance networking and gameplay logic built in C++
* Cross-platform support for Linux and Windows
**MySQL / MariaDB Backend**
* Fully structured schema with over 200+ tables
* Supports content customization, expansions, and custom worlds
**Scripting Engine**
* Native support for **Perl** and **Lua** scripting
* Powerfully extendable for quests, NPC behaviors, and custom events
**Open Source Content Database**
* Includes ProjectEQs world data up through *Dragons of Norrath*
* 100% customizable to create entirely new game worlds
<h1 align="center">
🚀 Why It Matters
</h1>
<p align="center">🧬 EQEmulator stands as a <strong>technical preservation project</strong>, ensuring that the magic of classic and custom servers lives on for future generations of players, tinkerers, and game designers.
</p>
> We humbly acknowledge and thank the original developers for creating one of the most influential online experiences in gaming history.
<h1 align="center">
🧑‍💻🖥️ Supported Clients
</h1>
## Supported Clients
|Titanium Edition|Secrets of Faydwer|Seeds of Destruction|Underfoot|Rain of Fear|
|:---:|:---:|:---:|:---:|:---:|
|<img src="http://i.imgur.com/hrwDxoM.jpg" height="150">|<img src="http://i.imgur.com/cRDW5tn.png" height="150">|<img src="http://i.imgur.com/V48kuVn.jpg" height="150">|<img src="http://i.imgur.com/IJQ0XMa.jpg" height="150">|<img src="http://i.imgur.com/OMpHkKa.png" height="100">|
## 📚 Resources
## Bug Reports <img src="http://i.imgur.com/daf1Vjw.png" height="20">
* Please use the [issue tracker](https://github.com/EQEmu/Server/issues) provided by GitHub to send us bug
reports or feature requests.
* The [EQEmu Forums](http://www.eqemulator.org/forums/) are also a place to submit and get help with bugs.
| Resource | Badges | Link |
|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| **EQEmulator Docs** | [![Docs](https://img.shields.io/badge/docs-MkDocs%20Powered-blueviolet)](https://docs.eqemu.io) | [docs.eqemu.io](https://docs.eqemu.io/) |
| **Discord Community**| [![Discord](https://img.shields.io/discord/212663220849213441?label=Discord&logo=discord&color=7289DA)](https://discord.gg/QHsm7CD) | [Join Discord](https://discord.gg/QHsm7CD) |
| **Latest Release** | [![Latest Release](https://img.shields.io/github/v/release/eqemu/server)](https://github.com/eqemu/server/releases) <br> [![Release Date](https://img.shields.io/github/release-date/EQEmu/Server)](https://github.com/EQEmu/Server/releases) <br> [![All Releases](https://img.shields.io/github/downloads/eqemu/server/total.svg)](https://github.com/eqemu/server/releases) | [View Releases](https://github.com/eqemu/server/releases) |
| **License** | [![License](https://img.shields.io/github/license/EQEmu/Server)](./LICENSE) | [View License](./LICENSE) |
| **Build Status** | [![Build Status](http://drone.akkadius.com/api/badges/EQEmu/Server/status.svg)](http://drone.akkadius.com/EQEmu/Server) | [View Build Status](http://drone.akkadius.com/EQEmu/Server) |
| **Docker Pulls** | [![Docker Pulls](https://img.shields.io/docker/pulls/akkadius/eqemu-server)](https://hub.docker.com/r/akkadius/eqemu-server) | [Docker Hub](https://hub.docker.com/r/akkadius/eqemu-server) |
| **Contributions** | [![GitHub PRs](https://img.shields.io/github/issues-pr-closed/eqemu/server)](https://github.com/eqemu/server/pulls?q=is%3Apr+is%3Aclosed) | [Closed PRs & Issues](https://github.com/eqemu/server/pulls?q=is%3Apr+is%3Aclosed) |
## Contributions <img src="http://image.flaticon.com/icons/png/512/25/25231.png" width="20">
## 🛠️ Getting Started
* The preferred way to contribute is to fork the repo and submit a pull request on
GitHub. If you need help with your changes, you can always post on the forums or
try Discord. You can also post unified diffs (`git diff` should do the trick) on the
[Server Code Submissions](http://www.eqemulator.org/forums/forumdisplay.php?f=669)
forum, although pull requests will be much quicker and easier on all parties.
If you want to set up your own EQEmulator server, please refer to the current [server installation guides](https://docs.eqemu.io/#server-installation). We've had 100,000s of players and developers use our guides to set up their own servers, and we hope you will too!
## Contact <img src="http://gamerescape.com/wp-content/uploads/2015/06/discord.png" height="20">
## 🗂️ Related Repositories
- Discord Channel: https://discord.gg/QHsm7CD
- **User Discord Channel**: `#general`
- **Developer Discord Channel**: `#eqemucoders`
| Repository | Description |
|--------------------|----------------------------------------------------------------------------------|
| [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests) | Official quests and event scripts for ProjectEQ |
| [Maps](https://github.com/Akkadius/EQEmuMaps) | EQEmu-compatible zone maps |
| [Installer Resources](https://github.com/Akkadius/EQEmuInstall) | Scripts and assets for setting up EQEmu servers |
| [Zone Utilities](https://github.com/EQEmu/zone-utilities) | Utilities for parsing, rendering, and manipulating EQ zone files |
## Resources
- [EQEmulator Forums](http://www.eqemulator.org/forums)
- [EQEmulator Wiki](https://docs.eqemu.io/)
## Related Repositories
* [ProjectEQ Quests](https://github.com/ProjectEQ/projecteqquests)
* [Maps](https://github.com/Akkadius/EQEmuMaps)
* [Installer Resources](https://github.com/Akkadius/EQEmuInstall)
* [Zone Utilities](https://github.com/EQEmu/zone-utilities) - Various utilities and libraries for parsing, rendering and manipulating EQ Zone files.
## Other License Info
* The server code and utilities are released under **GPLv3**
* We also include some small libraries for convienence that may be under different licensing
* SocketLib - GPL LibXML
* zlib - zlib license
* MariaDB/MySQL - GPL
* GPL Perl - GPL / ActiveState (under the assumption that this is a free project)
* CPPUnit - GLP StringUtilities - Apache
* LUA - MIT
## Contributors
<a href="https://github.com/EQEmu/server/graphs/contributors">
<img src="https://contributors-img.firebaseapp.com/image?repo=EQEmu/server" />
</a>
+161 -54
View File
@@ -29,14 +29,11 @@
#include "../../common/content/world_content_service.h"
#include "../../common/zone_store.h"
#include "../../common/path_manager.h"
#include "../../common/repositories/base_data_repository.h"
#include "../../common/repositories/db_str_repository.h"
#include "../../common/repositories/skill_caps_repository.h"
#include "../../common/repositories/spells_new_repository.h"
#include "../../common/file.h"
#include "../../common/events/player_event_logs.h"
#include "../../common/skill_caps.h"
#include "../../common/evolving_items.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
void ExportSpells(SharedDatabase *db);
void ExportSkillCaps(SharedDatabase *db);
@@ -46,10 +43,10 @@ void ExportDBStrings(SharedDatabase *db);
int main(int argc, char **argv)
{
RegisterExecutablePlatform(ExePlatformClientExport);
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
LogSys.LoadLogSettingsDefaults();
set_exception_handler();
PathManager::Instance()->Init();
path.LoadPaths();
LogInfo("Client Files Export Utility");
if (!EQEmuConfig::LoadConfig()) {
@@ -92,27 +89,30 @@ int main(int argc, char **argv)
content_db.SetMySQL(database);
}
EQEmuLogSys::Instance()->SetDatabase(&database)
->SetLogPath(PathManager::Instance()->GetLogPath())
LogSys.SetDatabase(&database)
->SetLogPath(path.GetLogPath())
->LoadLogDatabaseSettings()
->StartFileLogs();
std::string export_type;
std::string arg_1;
if (argv[1]) {
export_type = argv[1];
arg_1 = argv[1];
}
if (Strings::EqualFold(export_type, "spells")) {
if (arg_1 == "spells") {
ExportSpells(&content_db);
return 0;
} else if (Strings::EqualFold(export_type, "skills")) {
}
if (arg_1 == "skills") {
ExportSkillCaps(&content_db);
return 0;
} else if (Strings::EqualFold(export_type, "basedata") || Strings::EqualFold(export_type, "base_data")) {
}
if (arg_1 == "basedata") {
ExportBaseData(&content_db);
return 0;
} else if (Strings::EqualFold(export_type, "dbstr") || Strings::EqualFold(export_type, "dbstring")) {
}
if (arg_1 == "dbstring") {
ExportDBStrings(&database);
return 0;
}
@@ -122,84 +122,191 @@ int main(int argc, char **argv)
ExportBaseData(&content_db);
ExportDBStrings(&database);
EQEmuLogSys::Instance()->CloseFileLogs();
LogSys.CloseFileLogs();
return 0;
}
void ExportSpells(SharedDatabase *db)
{
std::ofstream file(fmt::format("{}/export/spells_us.txt", PathManager::Instance()->GetServerPath()));
if (!file || !file.is_open()) {
LogInfo("Exporting Spells");
std::string file = fmt::format("{}/export/spells_us.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
LogError("Unable to open export/spells_us.txt to write, skipping.");
return;
}
const auto& lines = SpellsNewRepository::GetSpellFileLines(*db);
const std::string query = "SELECT * FROM spells_new ORDER BY id";
auto results = db->QueryDatabase(query);
const std::string& file_string = Strings::Implode("\n", lines);
if (results.Success()) {
for (auto row = results.begin(); row != results.end(); ++row) {
std::string line;
unsigned int fields = results.ColumnCount();
for (unsigned int i = 0; i < fields; ++i) {
if (i != 0) {
line.push_back('^');
}
file << file_string;
if (row[i] != nullptr) {
line += row[i];
}
}
file.close();
fprintf(f, "%s\n", line.c_str());
}
}
else {
}
LogInfo("Exported [{}] Spell{}", lines.size(), lines.size() != 1 ? "s" : "");
fclose(f);
}
bool SkillUsable(SharedDatabase *db, int skill_id, int class_id)
{
bool res = false;
std::string query = StringFormat(
"SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d",
class_id, skill_id
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (results.RowCount() == 0) {
return false;
}
auto row = results.begin();
if (row[0] && Strings::ToInt(row[0]) > 0) {
return true;
}
return false;
}
int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level)
{
std::string query = StringFormat(
"SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d",
class_id, skill_id, level
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return 0;
}
if (results.RowCount() == 0) {
return 0;
}
auto row = results.begin();
return Strings::ToInt(row[0]);
}
void ExportSkillCaps(SharedDatabase *db)
{
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", PathManager::Instance()->GetServerPath()));
if (!file || !file.is_open()) {
LogInfo("Exporting Skill Caps");
std::string file = fmt::format("{}/export/SkillCaps.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
return;
}
const auto& lines = SkillCapsRepository::GetSkillCapFileLines(*db);
for (int cl = 1; cl <= 16; ++cl) {
for (int skill = 0; skill <= 77; ++skill) {
if (SkillUsable(db, skill, cl)) {
int previous_cap = 0;
for (int level = 1; level <= 100; ++level) {
int cap = GetSkill(db, skill, cl, level);
if (cap < previous_cap) {
cap = previous_cap;
}
const std::string& file_string = Strings::Implode("\n", lines);
fprintf(f, "%d^%d^%d^%d^0\n", cl, skill, level, cap);
previous_cap = cap;
}
}
}
}
file << file_string;
file.close();
LogInfo("Exported [{}] Skill Cap{}", lines.size(), lines.size() != 1 ? "s" : "");
fclose(f);
}
void ExportBaseData(SharedDatabase *db)
{
std::ofstream file(fmt::format("{}/export/BaseData.txt", PathManager::Instance()->GetServerPath()));
if (!file || !file.is_open()) {
LogInfo("Exporting Base Data");
std::string file = fmt::format("{}/export/BaseData.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
LogError("Unable to open export/BaseData.txt to write, skipping.");
return;
}
const auto& lines = BaseDataRepository::GetBaseDataFileLines(*db);
const std::string query = "SELECT * FROM base_data ORDER BY level, class";
auto results = db->QueryDatabase(query);
if (results.Success()) {
for (auto row = results.begin(); row != results.end(); ++row) {
std::string line;
unsigned int fields = results.ColumnCount();
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
if (rowIndex != 0) {
line.push_back('^');
}
const std::string& file_string = Strings::Implode("\n", lines);
if (row[rowIndex] != nullptr) {
line += row[rowIndex];
}
}
file << file_string;
fprintf(f, "%s\n", line.c_str());
}
}
file.close();
LogInfo("Exported [{}] Base Data Entr{}", lines.size(), lines.size() != 1 ? "ies" : "y");
fclose(f);
}
void ExportDBStrings(SharedDatabase *db)
{
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", PathManager::Instance()->GetServerPath()));
if (!file || !file.is_open()) {
LogInfo("Exporting DB Strings");
std::string file = fmt::format("{}/export/dbstr_us.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
return;
}
const auto& lines = DbStrRepository::GetDBStrFileLines(*db);
const std::string& file_string = Strings::Implode("\n", lines);
file << file_string;
file.close();
LogInfo("Exported [{}] Database String{}", lines.size(), lines.size() != 1 ? "s" : "");
fprintf(f, "Major^Minor^String(New)\n");
const std::string query = "SELECT * FROM db_str ORDER BY id, type";
auto results = db->QueryDatabase(query);
if (results.Success()) {
for (auto row = results.begin(); row != results.end(); ++row) {
std::string line;
unsigned int fields = results.ColumnCount();
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
if (rowIndex != 0) {
line.push_back('^');
}
if (row[rowIndex] != nullptr) {
line += row[rowIndex];
}
}
fprintf(f, "%s\n", line.c_str());
}
}
fclose(f);
}
+14 -11
View File
@@ -29,8 +29,11 @@
#include "../../common/path_manager.h"
#include "../../common/repositories/base_data_repository.h"
#include "../../common/file.h"
#include "../../common/events/player_event_logs.h"
#include "../../common/evolving_items.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
ZoneStore zone_store;
PathManager path;
void ImportSpells(SharedDatabase *db);
void ImportSkillCaps(SharedDatabase *db);
@@ -39,10 +42,10 @@ void ImportDBStrings(SharedDatabase *db);
int main(int argc, char **argv) {
RegisterExecutablePlatform(ExePlatformClientImport);
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
LogSys.LoadLogSettingsDefaults();
set_exception_handler();
PathManager::Instance()->Init();
path.LoadPaths();
LogInfo("Client Files Import Utility");
if(!EQEmuConfig::LoadConfig()) {
@@ -85,8 +88,8 @@ int main(int argc, char **argv) {
content_db.SetMySQL(database);
}
EQEmuLogSys::Instance()->SetDatabase(&database)
->SetLogPath(PathManager::Instance()->GetLogPath())
LogSys.SetDatabase(&database)
->SetLogPath(path.GetLogPath())
->LoadLogDatabaseSettings()
->StartFileLogs();
@@ -95,7 +98,7 @@ int main(int argc, char **argv) {
ImportBaseData(&content_db);
ImportDBStrings(&database);
EQEmuLogSys::Instance()->CloseFileLogs();
LogSys.CloseFileLogs();
return 0;
}
@@ -131,7 +134,7 @@ bool IsStringField(int i) {
void ImportSpells(SharedDatabase *db) {
LogInfo("Importing Spells");
std::string file = fmt::format("{}/import/spells_us.txt", PathManager::Instance()->GetServerPath());
std::string file = fmt::format("{}/import/spells_us.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "r");
if(!f) {
LogError("Unable to open {} to read, skipping.", file);
@@ -221,7 +224,7 @@ void ImportSpells(SharedDatabase *db) {
void ImportSkillCaps(SharedDatabase *db) {
LogInfo("Importing Skill Caps");
std::string file = fmt::format("{}/import/SkillCaps.txt", PathManager::Instance()->GetServerPath());
std::string file = fmt::format("{}/import/SkillCaps.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "r");
if(!f) {
LogError("Unable to open {} to read, skipping.", file);
@@ -258,7 +261,7 @@ void ImportBaseData(SharedDatabase *db)
{
LogInfo("Importing Base Data");
const std::string& file_name = fmt::format("{}/import/BaseData.txt", PathManager::Instance()->GetServerPath());
const std::string& file_name = fmt::format("{}/import/BaseData.txt", path.GetServerPath());
const auto& file_contents = File::GetContents(file_name);
if (!file_contents.error.empty()) {
@@ -298,7 +301,7 @@ void ImportBaseData(SharedDatabase *db)
void ImportDBStrings(SharedDatabase *db) {
LogInfo("Importing DB Strings");
std::string file = fmt::format("{}/import/dbstr_us.txt", PathManager::Instance()->GetServerPath());
std::string file = fmt::format("{}/import/dbstr_us.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "r");
if(!f) {
LogError("Unable to open {} to read, skipping.", file);
+16 -64
View File
@@ -2,8 +2,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
SET(common_sources
base_packet.cpp
bazaar.cpp
bodytypes.cpp
classes.cpp
cli/eqemu_command_handler.cpp
compression.cpp
@@ -17,13 +15,11 @@ SET(common_sources
database.cpp
database_instances.cpp
database/database_update_manifest.cpp
database/database_update_manifest_custom.cpp
database/database_update_manifest_bots.cpp
database/database_update.cpp
dbcore.cpp
deity.cpp
dynamic_zone_base.cpp
dynamic_zone_lockout.cpp
emu_constants.cpp
emu_limits.cpp
emu_opcodes.cpp
@@ -41,7 +37,7 @@ SET(common_sources
event_sub.cpp
events/player_event_logs.cpp
events/player_event_discord_formatter.cpp
evolving_items.cpp
expedition_lockout_timer.cpp
extprofile.cpp
discord/discord_manager.cpp
faction.cpp
@@ -64,7 +60,6 @@ SET(common_sources
mutex.cpp
mysql_request_result.cpp
mysql_request_row.cpp
mysql_stmt.cpp
opcode_map.cpp
opcodemgr.cpp
packet_dump.cpp
@@ -88,9 +83,7 @@ SET(common_sources
shared_tasks.cpp
shareddb.cpp
skills.cpp
skill_caps.cpp
spdat.cpp
spdat_bot.cpp
strings.cpp
struct_strategy.cpp
textures.cpp
@@ -100,13 +93,12 @@ SET(common_sources
json/json.hpp
json/jsoncpp.cpp
zone_store.cpp
memory/ksm.hpp
net/console_server.cpp
net/console_server_connection.cpp
net/crc32.cpp
net/daybreak_connection.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
@@ -163,7 +155,6 @@ SET(repositories
repositories/base/base_bugs_repository.h
repositories/base/base_bug_reports_repository.h
repositories/base/base_buyer_repository.h
repositories/base/base_buyer_trade_items_repository.h
repositories/base/base_character_activities_repository.h
repositories/base/base_character_alternate_abilities_repository.h
repositories/base/base_character_alt_currency_repository.h
@@ -176,7 +167,6 @@ SET(repositories
repositories/base/base_character_currency_repository.h
repositories/base/base_character_data_repository.h
repositories/base/base_character_disciplines_repository.h
repositories/base/base_character_evolving_items_repository.h
repositories/base/base_character_expedition_lockouts_repository.h
repositories/base/base_character_exp_modifiers_repository.h
repositories/base/base_character_inspect_messages_repository.h
@@ -186,8 +176,6 @@ SET(repositories
repositories/base/base_character_leadership_abilities_repository.h
repositories/base/base_character_material_repository.h
repositories/base/base_character_memmed_spells_repository.h
repositories/base/base_character_parcels_repository.h
repositories/base/base_character_parcels_containers_repository.h
repositories/base/base_character_peqzone_flags_repository.h
repositories/base/base_character_pet_buffs_repository.h
repositories/base/base_character_pet_info_repository.h
@@ -214,9 +202,10 @@ SET(repositories
repositories/base/base_discovered_items_repository.h
repositories/base/base_doors_repository.h
repositories/base/base_dynamic_zones_repository.h
repositories/base/base_dynamic_zone_lockouts_repository.h
repositories/base/base_dynamic_zone_members_repository.h
repositories/base/base_dynamic_zone_templates_repository.h
repositories/base/base_expeditions_repository.h
repositories/base/base_expedition_lockouts_repository.h
repositories/base/base_faction_association_repository.h
repositories/base/base_faction_base_data_repository.h
repositories/base/base_faction_list_repository.h
@@ -244,7 +233,6 @@ SET(repositories
repositories/base/base_inventory_snapshots_repository.h
repositories/base/base_ip_exemptions_repository.h
repositories/base/base_items_repository.h
repositories/base/base_items_evolving_details_repository.h
repositories/base/base_ldon_trap_entries_repository.h
repositories/base/base_ldon_trap_templates_repository.h
repositories/base/base_level_exp_mods_repository.h
@@ -282,20 +270,8 @@ SET(repositories
repositories/base/base_pets_equipmentset_repository.h
repositories/base/base_pets_equipmentset_entries_repository.h
repositories/base/base_player_titlesets_repository.h
repositories/base/base_player_event_aa_purchase_repository.h
repositories/base/base_player_event_killed_npc_repository.h
repositories/base/base_player_event_killed_named_npc_repository.h
repositories/base/base_player_event_killed_raid_npc_repository.h
repositories/base/base_player_event_log_settings_repository.h
repositories/base/base_player_event_logs_repository.h
repositories/base/base_player_event_loot_items_repository.h
repositories/base/base_player_event_merchant_purchase_repository.h
repositories/base/base_player_event_merchant_sell_repository.h
repositories/base/base_player_event_npc_handin_repository.h
repositories/base/base_player_event_npc_handin_entries_repository.h
repositories/base/base_player_event_speech_repository.h
repositories/base/base_player_event_trade_repository.h
repositories/base/base_player_event_trade_entries_repository.h
repositories/base/base_quest_globals_repository.h
repositories/base/base_raid_details_repository.h
repositories/base/base_raid_members_repository.h
@@ -358,8 +334,7 @@ SET(repositories
repositories/books_repository.h
repositories/bugs_repository.h
repositories/bug_reports_repository.h
repositories/buyer_buy_lines_repository.h
repositories/buyer_trade_items_repository.h
repositories/buyer_repository.h
repositories/character_activities_repository.h
repositories/character_alternate_abilities_repository.h
repositories/character_alt_currency_repository.h
@@ -372,7 +347,6 @@ SET(repositories
repositories/character_currency_repository.h
repositories/character_data_repository.h
repositories/character_disciplines_repository.h
repositories/character_evolving_items_repository.h
repositories/character_expedition_lockouts_repository.h
repositories/character_exp_modifiers_repository.h
repositories/character_inspect_messages_repository.h
@@ -382,8 +356,6 @@ SET(repositories
repositories/character_leadership_abilities_repository.h
repositories/character_material_repository.h
repositories/character_memmed_spells_repository.h
repositories/character_parcels_repository.h
repositories/character_parcels_containers_repository.h
repositories/character_peqzone_flags_repository.h
repositories/character_pet_buffs_repository.h
repositories/character_pet_info_repository.h
@@ -410,9 +382,10 @@ SET(repositories
repositories/discovered_items_repository.h
repositories/doors_repository.h
repositories/dynamic_zones_repository.h
repositories/dynamic_zone_lockouts_repository.h
repositories/dynamic_zone_members_repository.h
repositories/dynamic_zone_templates_repository.h
repositories/expeditions_repository.h
repositories/expedition_lockouts_repository.h
repositories/faction_association_repository.h
repositories/faction_base_data_repository.h
repositories/faction_list_repository.h
@@ -440,7 +413,6 @@ SET(repositories
repositories/inventory_snapshots_repository.h
repositories/ip_exemptions_repository.h
repositories/items_repository.h
repositories/items_evolving_details_repository.h
repositories/ldon_trap_entries_repository.h
repositories/ldon_trap_templates_repository.h
repositories/level_exp_mods_repository.h
@@ -478,20 +450,8 @@ SET(repositories
repositories/pets_equipmentset_repository.h
repositories/pets_equipmentset_entries_repository.h
repositories/player_titlesets_repository.h
repositories/player_event_aa_purchase_repository.h
repositories/player_event_killed_npc_repository.h
repositories/player_event_killed_named_npc_repository.h
repositories/player_event_killed_raid_npc_repository.h
repositories/player_event_log_settings_repository.h
repositories/player_event_logs_repository.h
repositories/player_event_loot_items_repository.h
repositories/player_event_merchant_purchase_repository.h
repositories/player_event_merchant_sell_repository.h
repositories/player_event_npc_handin_repository.h
repositories/player_event_npc_handin_entries_repository.h
repositories/player_event_speech_repository.h
repositories/player_event_trade_repository.h
repositories/player_event_trade_entries_repository.h
repositories/quest_globals_repository.h
repositories/raid_details_repository.h
repositories/raid_members_repository.h
@@ -536,7 +496,6 @@ SET(repositories
SET(common_headers
additive_lagged_fibonacci_engine.h
bazaar.h
base_packet.h
bodytypes.h
classes.h
@@ -560,7 +519,6 @@ SET(common_headers
discord/discord.h
discord/discord_manager.h
dynamic_zone_base.h
dynamic_zone_lockout.h
emu_constants.h
emu_limits.h
emu_opcodes.h
@@ -586,7 +544,7 @@ SET(common_headers
events/player_event_discord_formatter.h
events/player_events.h
event_sub.h
evolving_items.h
expedition_lockout_timer.h
extprofile.h
faction.h
file.h
@@ -603,6 +561,7 @@ SET(common_headers
ipc_mutex.h
ip_util.h
item_data.h
item_fieldlist.h
item_instance.h
json_config.h
light_source.h
@@ -617,7 +576,6 @@ SET(common_headers
mutex.h
mysql_request_result.h
mysql_request_row.h
mysql_stmt.h
op_codes.h
opcode_dispatch.h
opcodemgr.h
@@ -645,11 +603,9 @@ SET(common_headers
server_event_scheduler.h
serverinfo.h
servertalk.h
server_reload_types.h
shared_tasks.h
shareddb.h
skills.h
skill_caps.h
spdat.h
strings.h
struct_strategy.h
@@ -670,20 +626,18 @@ SET(common_headers
net/console_server.h
net/console_server_connection.h
net/crc32.h
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqstream.h
net/packet.h
net/reliable_stream_connection.h
net/reliable_stream_pooling.h
net/reliable_stream_structs.h
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.h
net/servertalk_common.h
net/servertalk_server.h
net/servertalk_server_connection.h
net/tcp_connection.h
net/tcp_connection_pooling.h
net/tcp_server.h
net/websocket_server.h
net/websocket_server_connection.h
@@ -742,6 +696,9 @@ SOURCE_GROUP(Net FILES
net/console_server_connection.h
net/crc32.cpp
net/crc32.h
net/daybreak_connection.cpp
net/daybreak_connection.h
net/daybreak_structs.h
net/dns.h
net/endian.h
net/eqmq.cpp
@@ -750,10 +707,6 @@ SOURCE_GROUP(Net FILES
net/eqstream.h
net/packet.cpp
net/packet.h
net/reliable_stream_connection.cpp
net/reliable_stream_connection.h
net/reliable_stream_pooling.h
net/reliable_stream_structs.h
net/servertalk_client_connection.cpp
net/servertalk_client_connection.h
net/servertalk_legacy_client_connection.cpp
@@ -765,7 +718,6 @@ SOURCE_GROUP(Net FILES
net/servertalk_server_connection.h
net/tcp_connection.cpp
net/tcp_connection.h
net/tcp_connection_pooling.h
net/tcp_server.cpp
net/tcp_server.h
net/websocket_server.cpp
@@ -840,8 +792,8 @@ IF (UNIX)
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
ENDIF (UNIX)
IF (EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/std-pch.h)
IF (WIN32 AND EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
ENDIF()
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
-334
View File
@@ -1,334 +0,0 @@
#include "bazaar.h"
#include "../../common/item_instance.h"
#include "repositories/trader_repository.h"
#include <memory>
std::vector<BazaarSearchResultsFromDB_Struct>
Bazaar::GetSearchResults(
Database &db,
Database &content_db,
BazaarSearchCriteria_Struct search,
uint32 char_zone_id,
int32 char_zone_instance_id
)
{
LogTrading(
"Searching for items with search criteria - item_name [{}] min_cost [{}] max_cost [{}] min_level [{}] "
"max_level [{}] max_results [{}] prestige [{}] augment [{}] trader_entity_id [{}] trader_id [{}] "
"search_scope [{}] char_zone_id [{}], char_zone_instance_id [{}]",
search.item_name,
search.min_cost,
search.max_cost,
search.min_level,
search.max_level,
search.max_results,
search.prestige,
search.augment,
search.trader_entity_id,
search.trader_id,
search.search_scope,
char_zone_id,
char_zone_instance_id
);
static std::map<uint8, uint32> item_slot_searches_new = {
{EQ::invslot::slotCharm, 1},
{EQ::invslot::slotEar1, 2},
{EQ::invslot::slotHead, 4},
{EQ::invslot::slotFace, 8},
{EQ::invslot::slotEar2, 16},
{EQ::invslot::slotNeck, 32},
{EQ::invslot::slotShoulders, 64},
{EQ::invslot::slotArms, 128},
{EQ::invslot::slotBack, 256},
{EQ::invslot::slotWrist1, 512},
{EQ::invslot::slotWrist2, 1024},
{EQ::invslot::slotRange, 2048},
{EQ::invslot::slotHands, 4096},
{EQ::invslot::slotPrimary, 8192},
{EQ::invslot::slotSecondary, 16384},
{EQ::invslot::slotFinger1, 32768},
{EQ::invslot::slotFinger2, 65536},
{EQ::invslot::slotChest, 131072},
{EQ::invslot::slotLegs, 262144},
{EQ::invslot::slotFeet, 524288},
{EQ::invslot::slotWaist, 1048576},
{EQ::invslot::slotPowerSource, 2097152},
{EQ::invslot::slotAmmo, 4194304},
};
struct ItemSearchType {
EQ::item::ItemType type;
std::string condition;
};
std::vector<ItemSearchType> item_search_types_new = {
{EQ::item::ItemType::ItemTypeBook, " AND (items.itemclass = 2 or items.itemclass = 31)"},
{EQ::item::ItemType::ItemTypeContainer, " AND (items.itemclass = 1 or items.itemclass = 67)"},
{EQ::item::ItemType::ItemTypeAllEffects, " AND (items.scrolleffect > 0 && items.scrolleffect < 65000)"},
{EQ::item::ItemType::ItemTypeUnknown9, " AND items.worneffect = 998"},
{EQ::item::ItemType::ItemTypeUnknown10, " AND (items.worneffect >= 1298 && items.worneffect <= 1307)"},
{EQ::item::ItemType::ItemTypeFocusEffect, " AND items.focuseffect > 0"},
{EQ::item::ItemType::ItemTypeArmor, " AND items.itemtype = 10"},
{EQ::item::ItemType::ItemType1HBlunt, " AND items.itemtype = 3"},
{EQ::item::ItemType::ItemType1HPiercing, " AND items.itemtype = 2"},
{EQ::item::ItemType::ItemType1HSlash, " AND items.itemtype = 0"},
{EQ::item::ItemType::ItemType2HBlunt, " AND items.itemtype = 4"},
{EQ::item::ItemType::ItemType2HSlash, " AND items.itemtype = 1"},
{EQ::item::ItemType::ItemTypeBow, " AND items.itemtype = 5"},
{EQ::item::ItemType::ItemTypeShield, " AND items.itemtype = 8"},
{EQ::item::ItemType::ItemTypeMisc, " AND items.itemtype = 11"},
{EQ::item::ItemType::ItemTypeFood, " AND items.itemtype = 14"},
{EQ::item::ItemType::ItemTypeDrink, " AND items.itemtype = 15"},
{EQ::item::ItemType::ItemTypeLight, " AND items.itemtype = 16"},
{EQ::item::ItemType::ItemTypeCombinable, " AND items.itemtype = 17"},
{EQ::item::ItemType::ItemTypeBandage, " AND items.itemtype = 18"},
{EQ::item::ItemType::ItemTypeSmallThrowing, " AND (items.itemtype = 19 OR items.itemtype = 7)"},
{EQ::item::ItemType::ItemTypeSpell, " AND items.itemtype = 20"},
{EQ::item::ItemType::ItemTypePotion, " AND items.itemtype = 21"},
{EQ::item::ItemType::ItemTypeBrassInstrument, " AND items.itemtype = 25"},
{EQ::item::ItemType::ItemTypeWindInstrument, " AND items.itemtype = 23"},
{EQ::item::ItemType::ItemTypeStringedInstrument, " AND items.itemtype = 24"},
{EQ::item::ItemType::ItemTypePercussionInstrument, " AND items.itemtype = 26"},
{EQ::item::ItemType::ItemTypeArrow, " AND items.itemtype = 27"},
{EQ::item::ItemType::ItemTypeJewelry, " AND items.itemtype = 29"},
{EQ::item::ItemType::ItemTypeNote, " AND items.itemtype = 32"},
{EQ::item::ItemType::ItemTypeKey, " AND items.itemtype = 33"},
{EQ::item::ItemType::ItemType2HPiercing, " AND items.itemtype = 35"},
{EQ::item::ItemType::ItemTypeAlcohol, " AND items.itemtype = 38"},
{EQ::item::ItemType::ItemTypeMartial, " AND items.itemtype = 45"},
{EQ::item::ItemType::ItemTypeAugmentation, " AND items.itemtype = 54"},
{EQ::item::ItemType::ItemTypeAlternateAbility, " AND items.itemtype = 57"},
{EQ::item::ItemType::ItemTypeCount, " AND items.itemtype = 65"},
{EQ::item::ItemType::ItemTypeCollectible, " AND items.itemtype = 66"}
};
// item stat searches
struct ItemStatSearch {
std::string query_string;
EQ::skills::SkillType skill_type;
};
std::map<uint32, ItemStatSearch> item_stat_searches_new = {
{STAT_AC, {" items.ac" , static_cast<EQ::skills::SkillType>(0)} },
{STAT_AGI, {" items.aagi", static_cast<EQ::skills::SkillType>(0)} },
{STAT_CHA, {" items.acha", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DEX, {" items.adex", static_cast<EQ::skills::SkillType>(0)} },
{STAT_INT, {" items.aint", static_cast<EQ::skills::SkillType>(0)} },
{STAT_STA, {" items.asta", static_cast<EQ::skills::SkillType>(0)} },
{STAT_STR, {" items.astr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_WIS, {" items.awis", static_cast<EQ::skills::SkillType>(0)} },
{STAT_COLD, {" items.cr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DISEASE, {" items.dr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_FIRE, {" items.fr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MAGIC, {" items.mr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_POISON, {" items.pr", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HP, {" items.hp", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MANA, {" items.mana", static_cast<EQ::skills::SkillType>(0)} },
{STAT_ENDURANCE, {" items.endur", static_cast<EQ::skills::SkillType>(0)} },
{STAT_ATTACK, {" items.attack", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HP_REGEN, {" items.regen", static_cast<EQ::skills::SkillType>(0)} },
{STAT_MANA_REGEN, {" items.manaregen", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HASTE, {" items.haste", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DAMAGE_SHIELD, {" items.damageshield", static_cast<EQ::skills::SkillType>(0)} },
{STAT_DS_MITIGATION, {" items.dsmitigation", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEAL_AMOUNT, {" items.healamt", static_cast<EQ::skills::SkillType>(0)} },
{STAT_SPELL_DAMAGE, {" items.spelldmg", static_cast<EQ::skills::SkillType>(0)} },
{STAT_CLAIRVOYANCE, {" items.clairvoyance", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_AGILITY, {" items.heroic_agi", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_CHARISMA, {" items.heroic_cha", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_DEXTERITY, {" items.heroic_dex", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_INTELLIGENCE, {" items.heroic_int", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_STAMINA, {" items.heroic_sta", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_STRENGTH, {" items.heroic_str", static_cast<EQ::skills::SkillType>(0)} },
{STAT_HEROIC_WISDOM, {" items.heroic_wis", static_cast<EQ::skills::SkillType>(0)} },
{STAT_BASH, {" items.skillmodvalue", EQ::skills::SkillBash} },
{STAT_BACKSTAB, {" items.backstabdmg", EQ::skills::SkillBackstab} },
{STAT_DRAGON_PUNCH, {" items.skillmodvalue", EQ::skills::SkillDragonPunch} },
{STAT_EAGLE_STRIKE, {" items.skillmodvalue", EQ::skills::SkillEagleStrike} },
{STAT_FLYING_KICK, {" items.skillmodvalue", EQ::skills::SkillFlyingKick} },
{STAT_KICK, {" items.skillmodvalue", EQ::skills::SkillKick} },
{STAT_ROUND_KICK, {" items.skillmodvalue", EQ::skills::SkillRoundKick} },
{STAT_TIGER_CLAW, {" items.skillmodvalue", EQ::skills::SkillTigerClaw} },
{STAT_FRENZY, {" items.skillmodvalue", EQ::skills::SkillFrenzy} },
};
bool convert = false;
std::string search_criteria_trader("TRUE");
std::string field_criteria_items("FALSE");
std::string where_criteria_items(" TRUE ");
if (search.search_scope == NonRoFBazaarSearchScope) {
search_criteria_trader.append(
fmt::format(
" AND trader.char_entity_id = {} AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}",
search.trader_entity_id,
Zones::BAZAAR,
char_zone_instance_id
)
);
}
else if (search.search_scope == Local_Scope) {
search_criteria_trader.append(fmt::format(
" AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}",
char_zone_id,
char_zone_instance_id)
);
}
else if (search.trader_id > 0) {
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
if (search.trader_id >= TraderRepository::TRADER_CONVERT_ID) {
convert = true;
search_criteria_trader.append(fmt::format(
" AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}",
Zones::BAZAAR,
search.trader_id - TraderRepository::TRADER_CONVERT_ID)
);
}
else {
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
}
}
else {
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
}
}
if (search.min_cost != 0) {
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000));
}
if (search.max_cost != 0) {
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
}
if (search.slot != std::numeric_limits<uint32>::max()) {
if (item_slot_searches_new.contains(search.slot)) {
where_criteria_items.append(
fmt::format(" AND items.slots & {0} = {0}", item_slot_searches_new[search.slot]));
}
}
if (search.type != std::numeric_limits<uint32>::max()) {
for (auto const &[type, condition]: item_search_types_new) {
if (type == search.type) {
where_criteria_items.append(condition);
break;
}
}
}
if (search.race != std::numeric_limits<uint32>::max()) {
where_criteria_items.append(
fmt::format(" AND items.races & {0} = {0}", GetPlayerRaceBit(GetRaceIDFromPlayerRaceValue(search.race))));
}
if (search._class != std::numeric_limits<uint32>::max()) {
where_criteria_items.append(fmt::format(" AND items.classes & {0} = {0}", GetPlayerClassBit(search._class)));
}
if (search.item_stat != std::numeric_limits<uint32>::max()) {
if (item_stat_searches_new.contains(search.item_stat)) {
field_criteria_items = fmt::format("{}", item_stat_searches_new[search.item_stat].query_string);
if (item_stat_searches_new[search.item_stat].skill_type) {
where_criteria_items.append(
fmt::format(" AND items.skillmodtype = {} ", item_stat_searches_new[search.item_stat].skill_type));
}
else {
where_criteria_items.append(
fmt::format(" AND {} > 0 ", item_stat_searches_new[search.item_stat].query_string));
}
}
}
if (search.augment) {
where_criteria_items.append(fmt::format(
" AND (items.augslot1type = {0} OR "
"items.augslot2type = {0} OR "
"items.augslot3type = {0} OR "
"items.augslot4type = {0} OR "
"items.augslot5type = {0} OR "
"items.augslot6type = {0})",
search.augment)
);
}
if (search.min_level != 1) {
where_criteria_items.append(fmt::format(" AND items.reclevel >= {}", search.min_level));
}
if (search.max_level != 100) {
where_criteria_items.append(fmt::format(" AND items.reclevel <= {}", search.max_level));
}
std::vector<BazaarSearchResultsFromDB_Struct> all_entries;
std::vector<std::string> trader_items_ids{};
auto const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader);
if (trader_results.empty()) {
LogTradingDetail("Bazaar - No traders found in bazaar search.");
return all_entries;
}
for (auto const &i: trader_results) {
trader_items_ids.push_back(std::to_string(i.trader.item_id));
}
auto const item_results = ItemsRepository::GetItemsForBazaarSearch(
content_db,
trader_items_ids,
std::string(search.item_name),
field_criteria_items,
where_criteria_items,
search.max_results
);
if (item_results.empty()) {
LogTradingDetail("Bazaar - No items found in bazaar search.");
return all_entries;
}
all_entries.reserve(trader_results.size());
for (auto const& t:trader_results) {
if (!item_results.contains(t.trader.item_id)) {
continue;
}
BazaarSearchResultsFromDB_Struct r{};
r.count = 1;
r.trader_id = t.trader.char_id;
r.serial_number = t.trader.item_sn;
r.cost = t.trader.item_cost;
r.slot_id = t.trader.slot_id;
r.charges = t.trader.item_charges;
r.stackable = item_results.at(t.trader.item_id).stackable;
r.icon_id = item_results.at(t.trader.item_id).icon;
r.trader_zone_id = t.trader.char_zone_id;
r.trader_zone_instance_id = t.trader.char_zone_instance_id;
r.trader_entity_id = t.trader.char_entity_id;
r.serial_number_RoF = fmt::format("{:016}\0", t.trader.item_sn);
r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name);
r.trader_name = fmt::format("{:.63}\0", t.trader_name);
r.item_stat = item_results.at(t.trader.item_id).stats;
if (RuleB(Bazaar, UseAlternateBazaarSearch)) {
if (convert ||
char_zone_id != Zones::BAZAAR ||
(char_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)
) {
r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id;
}
}
all_entries.push_back(r);
}
if (all_entries.size() > search.max_results) {
all_entries.resize(search.max_results);
}
LogTrading("Returning [{}] items from search results", all_entries.size());
return all_entries;
}
-16
View File
@@ -1,16 +0,0 @@
#ifndef EQEMU_BAZAAR_H
#define EQEMU_BAZAAR_H
#include <vector>
#include "shareddb.h"
#include "../../common/item_instance.h"
class Bazaar {
public:
static std::vector<BazaarSearchResultsFromDB_Struct>
GetSearchResults(Database &content_db, Database &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id, int char_zone_instance_id);
};
#endif //EQEMU_BAZAAR_H
-12
View File
@@ -1,12 +0,0 @@
#include "../common/global_define.h"
#include "../common/bodytypes.h"
std::string BodyType::GetName(uint8 body_type_id)
{
return IsValid(body_type_id) ? body_type_names[body_type_id] : "UNKNOWN BODY TYPE";
}
bool BodyType::IsValid(uint8 body_type_id)
{
return body_type_names.find(body_type_id) != body_type_names.end();
}
+46 -90
View File
@@ -18,96 +18,52 @@
#ifndef BODYTYPES_H
#define BODYTYPES_H
#include "types.h"
#include <map>
#include <string>
typedef enum {
BT_Humanoid = 1,
BT_Lycanthrope = 2,
BT_Undead = 3,
BT_Giant = 4,
BT_Construct = 5,
BT_Extraplanar = 6,
BT_Magical = 7, //this name might be a bit off,
BT_SummonedUndead = 8,
BT_RaidGiant = 9, //Velious era Raid Giant
BT_RaidColdain = 10, //Velious era Raid Coldain
BT_NoTarget = 11, //no name, can't target this bodytype
BT_Vampire = 12,
BT_Atenha_Ra = 13,
BT_Greater_Akheva = 14,
BT_Khati_Sha = 15,
BT_Seru = 16,
BT_Grieg_Veneficus = 17,
BT_Draz_Nurakk = 18,
BT_Zek = 19, //"creatures from the Plane of War."
BT_Luggald = 20,
BT_Animal = 21,
BT_Insect = 22,
BT_Monster = 23,
BT_Summoned = 24, //Elemental?
BT_Plant = 25,
BT_Dragon = 26,
BT_Summoned2 = 27,
BT_Summoned3 = 28,
BT_Dragon2 = 29, //database data indicates this is a dragon type (kunark and DoN?)
BT_VeliousDragon = 30, //might not be a tight set
BT_Familiar = 31,
BT_Dragon3 = 32,
BT_Boxes = 33,
BT_Muramite = 34, //tribal dudes
// ...
BT_NoTarget2 = 60,
// ...
BT_SwarmPet = 63, //Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
BT_MonsterSummon = 64,
// 65, trap or effect related?
BT_InvisMan = 66, //no name, seen on 'InvisMan', can be /targeted
BT_Special = 67
} bodyType;
/* bodytypes above 64 make the mob not show up */
// body types above 64 make the mob invisible
namespace BodyType {
constexpr uint8 Humanoid = 1;
constexpr uint8 Lycanthrope = 2;
constexpr uint8 Undead = 3;
constexpr uint8 Giant = 4;
constexpr uint8 Construct = 5;
constexpr uint8 Extraplanar = 6;
constexpr uint8 Magical = 7; // this name might be a bit off,
constexpr uint8 SummonedUndead = 8;
constexpr uint8 RaidGiant = 9; // Velious era Raid Giant
constexpr uint8 RaidColdain = 10; // Velious era Raid Coldain
constexpr uint8 NoTarget = 11; // no name, can't target this bodytype
constexpr uint8 Vampire = 12;
constexpr uint8 AtenHaRa = 13;
constexpr uint8 GreaterAkheva = 14;
constexpr uint8 KhatiSha = 15;
constexpr uint8 Seru = 16;
constexpr uint8 GriegVeneficus = 17;
constexpr uint8 DrazNurakk = 18;
constexpr uint8 Zek = 19; //"creatures from the Plane of War."
constexpr uint8 Luggald = 20;
constexpr uint8 Animal = 21;
constexpr uint8 Insect = 22;
constexpr uint8 Monster = 23;
constexpr uint8 Summoned = 24; // Elemental?
constexpr uint8 Plant = 25;
constexpr uint8 Dragon = 26;
constexpr uint8 Summoned2 = 27;
constexpr uint8 Summoned3 = 28;
constexpr uint8 Dragon2 = 29; // database data indicates this is a dragon type (Kunark and DoN?)
constexpr uint8 VeliousDragon = 30; // might not be a tight set
constexpr uint8 Familiar = 31;
constexpr uint8 Dragon3 = 32;
constexpr uint8 Boxes = 33;
constexpr uint8 Muramite = 34; // tribal dudes
constexpr uint8 NoTarget2 = 60;
constexpr uint8 SwarmPet = 63; // Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
constexpr uint8 MonsterSummon = 64;
constexpr uint8 InvisibleMan = 66; // no name, seen on 'InvisMan', can be /targeted
constexpr uint8 Special = 67;
std::string GetName(uint8 body_type_id);
bool IsValid(uint8 body_type_id);
}
static std::map<uint8, std::string> body_type_names = {
{ BodyType::Humanoid, "Humanoid" },
{ BodyType::Lycanthrope, "Lycanthrope" },
{ BodyType::Undead, "Undead" },
{ BodyType::Giant, "Giant" },
{ BodyType::Construct, "Construct" },
{ BodyType::Extraplanar, "Extraplanar" },
{ BodyType::Magical, "Magical" },
{ BodyType::SummonedUndead, "Summoned Undead" },
{ BodyType::RaidGiant, "Raid Giant" },
{ BodyType::RaidColdain, "Raid Coldain" },
{ BodyType::NoTarget, "Untargetable" },
{ BodyType::Vampire, "Vampire" },
{ BodyType::AtenHaRa, "Aten Ha Ra" },
{ BodyType::GreaterAkheva, "Greater Akheva" },
{ BodyType::KhatiSha, "Khati Sha" },
{ BodyType::Seru, "Seru" },
{ BodyType::GriegVeneficus, "Grieg Veneficus" },
{ BodyType::DrazNurakk, "Draz Nurakk" },
{ BodyType::Zek, "Zek" },
{ BodyType::Luggald, "Luggald" },
{ BodyType::Animal, "Animal" },
{ BodyType::Insect, "Insect" },
{ BodyType::Monster, "Monster" },
{ BodyType::Summoned, "Summoned" },
{ BodyType::Plant, "Plant" },
{ BodyType::Dragon, "Dragon" },
{ BodyType::Summoned2, "Summoned 2" },
{ BodyType::Summoned3, "Summoned 3" },
{ BodyType::Dragon2, "Dragon 2" },
{ BodyType::VeliousDragon, "Velious Dragon" },
{ BodyType::Familiar, "Familiar" },
{ BodyType::Dragon3, "Dragon 3" },
{ BodyType::Boxes, "Boxes" },
{ BodyType::Muramite, "Muramite" },
{ BodyType::NoTarget2, "Untargetable 2" },
{ BodyType::SwarmPet, "Swarm Pet" },
{ BodyType::MonsterSummon, "Monster Summon" },
{ BodyType::InvisibleMan, "Invisible Man" },
{ BodyType::Special, "Special" },
};
constexpr int format_as(bodyType type) { return static_cast<int>(type); }
#endif
-2
View File
@@ -131,8 +131,6 @@ static std::map<uint8, std::string> class_names = {
#define ARMOR_TYPE_LAST ARMOR_TYPE_PLATE
#define ARMOR_TYPE_COUNT 5
#define BOT_CLASS_BASE_ID_PREFIX 3000
const char* GetClassIDName(uint8 class_id, uint8 level = 0);
+66 -97
View File
@@ -6,7 +6,6 @@
#include "../rulesys.h"
#include "../eqemu_logsys.h"
#include "../repositories/instance_list_repository.h"
#include "../zone_store.h"
WorldContentService::WorldContentService()
@@ -27,7 +26,7 @@ WorldContentService *WorldContentService::SetExpansionContext()
// pull expansion from rules
int expansion = RuleI(Expansion, CurrentExpansion);
if (expansion >= Expansion::Classic && expansion <= Expansion::MaxId) {
WorldContentService::Instance()->SetCurrentExpansion(expansion);
content_service.SetCurrentExpansion(expansion);
}
LogInfo(
@@ -41,12 +40,12 @@ WorldContentService *WorldContentService::SetExpansionContext()
std::string WorldContentService::GetCurrentExpansionName()
{
if (WorldContentService::Instance()->GetCurrentExpansion() == Expansion::EXPANSION_ALL) {
if (content_service.GetCurrentExpansion() == Expansion::EXPANSION_ALL) {
return "All Expansions";
}
if (current_expansion >= Expansion::Classic && current_expansion <= Expansion::MaxId) {
return Expansion::ExpansionName[WorldContentService::Instance()->GetCurrentExpansion()];
return Expansion::ExpansionName[content_service.GetCurrentExpansion()];
}
return "Unknown Expansion";
@@ -179,13 +178,12 @@ void WorldContentService::ReloadContentFlags()
LogInfo(
"Loaded content flag [{}] [{}]",
f.flag_name,
(f.enabled ? "enabled" : "disabled")
(f.enabled ? "Enabled" : "Disabled")
);
}
SetContentFlags(set_content_flags);
LoadStaticGlobalZoneInstances();
ZoneStore::Instance()->LoadZones(*m_content_database);
SetContentZones(ZoneRepository::All(*m_content_database));
}
Database *WorldContentService::GetDatabase() const
@@ -237,107 +235,41 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
ReloadContentFlags();
}
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
// SetZones sets the zones for the world content service
// this is used for zone routing middleware
// we pull the zone list from the zone repository and feed from the zone store for now
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
WorldContentService *WorldContentService::SetContentZones(const std::vector<BaseZoneRepository::Zone>& zones)
{
auto r = FindZone(zc->zoneID, zc->instanceID);
if (r.zone_id == 0) {
return;
}
m_zones = zones;
zc->instanceID = r.instance.id;
}
// LoadStaticGlobalZoneInstances loads all static global zone instances
// these are zones that are never set to expire and are global
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
WorldContentService *WorldContentService::LoadStaticGlobalZoneInstances()
{
m_zone_static_instances = InstanceListRepository::GetWhere(
*GetDatabase(),
fmt::format("never_expires = 1 AND is_global = 1")
);
LogInfo("Loaded [{}] zone_instances", m_zone_static_instances.size());
LogInfo("Loaded [{}] zones", m_zones.size());
return this;
}
// FindZone handles content and context aware zone routing (middleware)
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
//
// this is a middleware function that is meant to be used in the zone change process
// this hooks all core zone changes within the server and routes the player to the correct zone
// returning a zone_id of non-zero means the middleware will route the player
// returning a zone_id of 0 means the middleware will not route the player
// this is useful for handling multiple versions of the same zone
//
// implementation >
// the zoning and process spawning logic already is handled by two keys "zone_id" and "instance_id"
// we leverage static, never expires instances to handle this and client still sees it as a normal zone
//
// content awareness >
// simply use the zone_id, server content settings and the middleware will handle the rest
// you don't have to think about instances in any data tables (use instance_id 0)
// you don't have to keep track of instance ids in scripts (use instance_id 0)
// example # 1
// lavastorm (pre-don) version 0 (classic)
// lavastorm (don) version 1
// we want to route players to the correct version of lavastorm based on the current server side expansion
// in order to do that the simplest and cleanest way we intercept the zoning process and route players to an "instance" of the zone
// the reason why we're doing this is because all of the zoning logic already is handled by two keys "zone_id" and "instance_id"
// we can leverage static, never expires instances to handle this but to the client they don't see it any other way than a public normal zone
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
// we decide to route the client to the correct version of the zone based on the current server side expansion
//
// example >
// we want to route players to the correct version of lavastorm based on the current server side expansion (DoesZonePassContentFiltering)
// lavastorm (pre-don) version 0 (classic)
// zone table entry for version = 0, min_expansion = 0, max_expansion = 8
// instance_list table entry for lavastorm has version = 0, is_global = 1, never_expires = 1
// lavastorm (don) version 1
// zone table entry for version = 1, min_expansion = 9, max_expansion = 99
// instance_list table entry for lavastorm has version = 1, is_global = 1, never_expires = 1
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
// example # 2
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
{
for (const auto &z: ZoneStore::Instance()->GetZones()) {
for (auto &i: m_zone_static_instances) {
if (
z.zoneidnumber == zone_id &&
DoesZonePassContentFiltering(z) &&
i.zone == zone_id &&
i.version == z.version) {
if (instance_id > 0 && i.id != instance_id) {
continue;
// if we're already in an instance, we don't want to route the player to another instance
if (zc->instanceID > 0) {
return;
}
LogInfo(
"Routed player to public static instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
i.id,
z.short_name,
z.zoneidnumber,
z.version,
z.long_name,
i.notes
);
return WorldContentService::FindZoneResult{
.zone_id = static_cast<uint32>(z.zoneidnumber),
.instance = i,
.zone = z
};
}
}
}
return WorldContentService::FindZoneResult{.zone_id = 0};
}
bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
{
for (auto &i: m_zone_static_instances) {
if (i.id == instance_id) {
return true;
}
}
return false;
}
bool WorldContentService::DoesZonePassContentFiltering(const ZoneRepository::Zone &z)
{
for (auto &z: m_zones) {
if (z.zoneidnumber == zc->zoneID) {
auto f = ContentFlags{
.min_expansion = z.min_expansion,
.max_expansion = z.max_expansion,
@@ -345,5 +277,42 @@ bool WorldContentService::DoesZonePassContentFiltering(const ZoneRepository::Zon
.content_flags_disabled = z.content_flags_disabled
};
return DoesPassContentFiltering(f);
if (DoesPassContentFiltering(f)) {
LogInfo(
"Attempting to route player to zone [{}] ({}) version [{}] long_name [{}]",
z.short_name,
z.zoneidnumber,
z.version,
z.long_name
);
auto instances = InstanceListRepository::GetWhere(
*GetDatabase(),
fmt::format(
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
z.zoneidnumber,
z.version
)
);
if (!instances.empty()) {
auto instance = instances.front();
zc->instanceID = instance.id;
LogInfo(
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
instance.id,
z.short_name,
z.zoneidnumber,
z.version,
z.long_name,
instance.notes
);
break;
}
}
}
}
}
+4 -20
View File
@@ -5,7 +5,6 @@
#include <vector>
#include "../repositories/content_flags_repository.h"
#include "../repositories/zone_repository.h"
#include "../repositories/instance_list_repository.h"
class Database;
@@ -160,7 +159,6 @@ public:
WorldContentService * SetExpansionContext();
bool DoesPassContentFiltering(const ContentFlags& f);
bool DoesZonePassContentFiltering(const ZoneRepository::Zone& z);
WorldContentService * SetDatabase(Database *database);
Database *GetDatabase() const;
@@ -171,22 +169,7 @@ public:
void SetContentFlag(const std::string &content_flag_name, bool enabled);
void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc);
struct FindZoneResult {
uint32 zone_id = 0;
InstanceListRepository::InstanceList instance;
ZoneRepository::Zone zone;
};
FindZoneResult FindZone(uint32 zone_id, uint32 instance_id);
bool IsInPublicStaticInstance(uint32 instance_id);
static WorldContentService* Instance()
{
static WorldContentService instance;
return &instance;
}
WorldContentService * SetContentZones(const std::vector<ZoneRepository::Zone>& zones);
private:
int current_expansion{};
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
@@ -196,8 +179,9 @@ private:
Database *m_content_database;
// holds a record of the zone table from the database
WorldContentService *LoadStaticGlobalZoneInstances();
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;
std::vector<ZoneRepository::Zone> m_zones = {};
};
extern WorldContentService content_service;
#endif //EQEMU_WORLD_CONTENT_SERVICE_H
+5 -9
View File
@@ -15,7 +15,7 @@
#include <cstdio>
#include <vector>
#ifdef _WINDOWS
#if WINDOWS
#define popen _popen
#endif
@@ -27,8 +27,6 @@ void SendCrashReport(const std::string &crash_report)
// "http://localhost:3010/api/v1/analytics/server-crash-report", // development
};
EQEmuLogSys* log = EQEmuLogSys::Instance();
auto config = EQEmuConfig::get();
for (auto &e: endpoints) {
uri u(e);
@@ -70,12 +68,12 @@ void SendCrashReport(const std::string &crash_report)
p["cpus"] = cpus.size();
p["origination_info"] = "";
if (!log->origination_info.zone_short_name.empty()) {
if (!LogSys.origination_info.zone_short_name.empty()) {
p["origination_info"] = fmt::format(
"{} ({}) instance_id [{}]",
log->origination_info.zone_short_name,
log->origination_info.zone_long_name,
log->origination_info.instance_id
LogSys.origination_info.zone_short_name,
LogSys.origination_info.zone_long_name,
LogSys.origination_info.instance_id
);
}
@@ -296,8 +294,6 @@ void print_trace()
SendCrashReport(crash_report);
}
EQEmuLogSys::Instance()->CloseFileLogs();
exit(1);
}
-846
View File
@@ -1,846 +0,0 @@
#include "../common/data_bucket.h"
#include "database.h"
#include <ctime>
#include <cctype>
#include "../common/json/json.hpp"
using json = nlohmann::json;
const std::string NESTED_KEY_DELIMITER = ".";
std::vector<DataBucketsRepository::DataBuckets> g_data_bucket_cache = {};
#if defined(ZONE)
#include "../zone/zonedb.h"
extern ZoneDatabase database;
#elif defined(WORLD)
#include "../world/worlddb.h"
extern WorldDatabase database;
#else
#error "You must define either ZONE or WORLD"
#endif
void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time)
{
auto k = DataBucketKey{
.key = bucket_key,
.value = bucket_value,
.expires = expires_time,
};
DataBucket::SetData(k);
}
void DataBucket::SetData(const DataBucketKey &k_)
{
DataBucketKey k = k_; // copy the key so we can modify it
bool is_nested = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
if (is_nested) {
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
}
auto b = DataBucketsRepository::NewEntity();
auto r = GetData(k, true);
// if we have an entry, use it
if (r.id > 0) {
b = r;
}
// add scoping to bucket
if (k.character_id > 0) {
b.character_id = k.character_id;
}
else if (k.account_id > 0) {
b.account_id = k.account_id;
}
else if (k.npc_id > 0) {
b.npc_id = k.npc_id;
}
else if (k.bot_id > 0) {
b.bot_id = k.bot_id;
} else if (k.zone_id > 0) {
b.zone_id = k.zone_id;
b.instance_id = k.instance_id;
}
const uint64 bucket_id = b.id;
int64 expires_time_unix = 0;
if (!k.expires.empty()) {
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::ToInt(k.expires);
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
}
if (is_nested) {
LogDataBuckets("Nested keys can't expire; set expiration on the parent key");
expires_time_unix = 0;
}
}
b.expires = expires_time_unix;
b.value = k.value;
b.key_ = k.key;
// Check for nested keys (keys with dots)
if (k_.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
// Retrieve existing JSON or create a new one
std::string existing_value = r.id > 0 ? r.value : "{}";
json json_value = json::object();
// Check if the JSON is valid
if (Strings::IsValidJson(existing_value)) {
try {
json_value = json::parse(existing_value);
} catch (json::parse_error &e) {
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", k_.key, e.what());
json_value = json::object(); // Reset to an empty object on error
}
}
// Recursively merge new key-value pair into the JSON object
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
auto top_key = nested_keys.front();
// remove the top-level key
nested_keys.erase(nested_keys.begin());
json *current = &json_value;
for (size_t i = 0; i < nested_keys.size(); ++i) {
const std::string &key_part = nested_keys[i];
if (i == nested_keys.size() - 1) {
LogDataBucketsDetail("Setting key [{}] key_part [{}]", k.key, key_part);
// If the key already exists and is an object or array, prevent overwriting to avoid data loss
if (current->contains(key_part) &&
((*current)[key_part].is_object() || (*current)[key_part].is_array())) {
LogDataBuckets("Attempted to overwrite an existing object or array at key [{}] - skipping", k_.key);
return;
}
// Set the value at the final key
(*current)[key_part] = k_.value;
} else {
// Traverse or create nested objects
if (!current->contains(key_part)) {
(*current)[key_part] = json::object();
LogDataBucketsDetail("Creating nested root key [{}] key_part [{}]", k.key, key_part);
} else if (!(*current)[key_part].is_object()) {
// If key exists but is not an object, reset to object to avoid conflicts
(*current)[key_part] = json::object();
}
current = &(*current)[key_part];
}
}
// Serialize JSON back to string
b.value = json_value.dump();
b.key_ = top_key; // Use the top-level key
}
if (bucket_id) {
// update the cache if it exists
if (CanCache(k)) {
for (auto &e: g_data_bucket_cache) {
if (CheckBucketMatch(e, k)) {
e = b;
break;
}
}
}
DataBucketsRepository::UpdateOne(database, b);
}
else {
b = DataBucketsRepository::InsertOne(database, b);
// add to cache if it doesn't exist
if (CanCache(k) && !ExistsInCache(b)) {
DeleteFromMissesCache(b);
g_data_bucket_cache.emplace_back(b);
}
}
}
std::string DataBucket::GetData(const std::string &bucket_key)
{
return GetData(DataBucketKey{.key = bucket_key}).value;
}
DataBucketsRepository::DataBuckets DataBucket::ExtractNestedValue(
const DataBucketsRepository::DataBuckets &bucket,
const std::string &full_key)
{
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
auto top_key = nested_keys.front();
nested_keys.erase(nested_keys.begin());
json json_value;
// Check if the JSON is valid
if (!Strings::IsValidJson(bucket.value)) {
LogDataBuckets("Invalid JSON for key [{}]", bucket.key_);
return DataBucketsRepository::NewEntity();
}
try {
json_value = json::parse(bucket.value); // Parse the JSON
} catch (json::parse_error &ex) {
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", bucket.key_, ex.what());
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
}
// Start from the top-level key (e.g., "progression")
json *current = &json_value;
// Traverse the JSON structure
for (const auto &key_part: nested_keys) {
LogDataBuckets("Looking for key part [{}] in JSON", key_part);
if (!current->contains(key_part)) {
LogDataBuckets("Key part [{}] not found in JSON for [{}]", key_part, full_key);
return DataBucketsRepository::NewEntity();
}
current = &(*current)[key_part];
}
// Create a new entity with the extracted value
DataBucketsRepository::DataBuckets result = bucket; // Copy the original bucket
result.value = current->is_string() ? current->get<std::string>() : current->dump();
return result;
}
// GetData fetches bucket data from the database or cache if it exists
// if the bucket doesn't exist, it will be added to the cache as a miss
// if ignore_misses_cache is true, the bucket will not be added to the cache as a miss
// the only place we should be ignoring the misses cache is on the initial read during SetData
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k_, bool ignore_misses_cache)
{
DataBucketKey k = k_; // Copy the key so we can modify it
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
// Extract the top-level key for nested keys
if (is_nested_key) {
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
}
LogDataBuckets(
"Getting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] zone_id [{}] instance_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id,
k.zone_id,
k.instance_id
);
bool can_cache = CanCache(k);
// Attempt to retrieve the value from the cache
if (can_cache) {
for (const auto &e : g_data_bucket_cache) {
if (CheckBucketMatch(e, k)) {
if (e.expires > 0 && e.expires < std::time(nullptr)) {
LogDataBuckets("Attempted to read expired key [{}] removing from cache", e.key_);
DeleteData(k);
return DataBucketsRepository::NewEntity();
}
LogDataBuckets("Returning key [{}] value [{}] from cache", e.key_, e.value);
if (is_nested_key && !k_.key.empty()) {
return ExtractNestedValue(e, k_.key);
}
return e;
}
}
}
// Fetch the value from the database
auto r = DataBucketsRepository::GetWhere(
database,
fmt::format(
" {} `key` = '{}' LIMIT 1",
DataBucket::GetScopedDbFilters(k),
k.key
)
);
if (r.empty()) {
// Handle cache misses
if (!ignore_misses_cache && can_cache) {
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.emplace_back(
DataBucketsRepository::DataBuckets{
.id = 0,
.key_ = k.key,
.value = "",
.expires = 0,
.account_id = k.account_id,
.character_id = k.character_id,
.npc_id = k.npc_id,
.bot_id = k.bot_id,
.zone_id = k.zone_id,
.instance_id = k.instance_id
}
);
LogDataBuckets(
"Key [{}] not found in database, adding to cache as a miss account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
k.key,
k.account_id,
k.character_id,
k.npc_id,
k.bot_id,
k.zone_id,
k.instance_id,
size_before,
g_data_bucket_cache.size()
);
}
return DataBucketsRepository::NewEntity();
}
auto bucket = r.front();
// If the entry has expired, delete it
if (bucket.expires > 0 && bucket.expires < static_cast<long long>(std::time(nullptr))) {
DeleteData(k);
return DataBucketsRepository::NewEntity();
}
// Add the value to the cache if it doesn't exist
if (can_cache) {
bool has_cache = false;
for (const auto &e : g_data_bucket_cache) {
if (e.id == bucket.id) {
has_cache = true;
break;
}
}
if (!has_cache) {
g_data_bucket_cache.emplace_back(bucket);
}
}
// Handle nested key extraction
if (is_nested_key && !k_.key.empty()) {
return ExtractNestedValue(bucket, k_.key);
}
return bucket;
}
std::string DataBucket::GetDataExpires(const std::string &bucket_key)
{
return GetDataExpires(DataBucketKey{.key = bucket_key});
}
std::string DataBucket::GetDataRemaining(const std::string &bucket_key)
{
return GetDataRemaining(DataBucketKey{.key = bucket_key});
}
bool DataBucket::DeleteData(const std::string &bucket_key)
{
return DeleteData(DataBucketKey{.key = bucket_key});
}
bool DataBucket::DeleteData(const DataBucketKey &k)
{
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
if (!is_nested_key) {
// Update cache
if (CanCache(k)) {
// delete from cache where contents match
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
return CheckBucketMatch(e, k);
}
),
g_data_bucket_cache.end()
);
}
// Regular key deletion, no nesting involved
return DataBucketsRepository::DeleteWhere(
database,
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), k.key)
);
}
// If it's a nested key, retrieve the top-level JSON object
auto top_level_key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
DataBucketKey top_level_k = k;
top_level_k.key = top_level_key;
auto r = GetData(top_level_k);
if (r.id == 0 || r.value.empty() || !Strings::IsValidJson(r.value)) {
LogDataBuckets("Attempted to delete nested key [{}] but parent key [{}] does not exist or is invalid JSON", k.key, top_level_key);
return false;
}
json json_value;
try {
json_value = json::parse(r.value);
} catch (json::parse_error &ex) {
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", top_level_key, ex.what());
return false;
}
// Recursively remove the nested key
auto nested_keys = Strings::Split(k.key, NESTED_KEY_DELIMITER);
auto top_key = nested_keys.front();
nested_keys.erase(nested_keys.begin());
json *current = &json_value;
for (size_t i = 0; i < nested_keys.size(); ++i) {
const std::string &key_part = nested_keys[i];
if (i == nested_keys.size() - 1) {
// Last key in the hierarchy - delete it
if (current->contains(key_part)) {
current->erase(key_part);
LogDataBuckets("Deleted nested key [{}] from [{}]", key_part, k.key);
} else {
LogDataBuckets("Key [{}] not found in JSON - nothing to delete", k.key);
return false;
}
} else {
if (!current->contains(key_part) || !(*current)[key_part].is_object()) {
LogDataBuckets("Parent key [{}] does not exist or is not an object", key_part);
return false;
}
current = &(*current)[key_part];
}
}
// If the JSON object is now empty, delete the top-level key
if (json_value.empty()) {
LogDataBuckets("Top-level key [{}] is now empty, deleting entire entry", top_level_key);
// delete cache
if (CanCache(k)) {
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
return CheckBucketMatch(e, top_level_k);
}
),
g_data_bucket_cache.end()
);
}
return DataBucketsRepository::DeleteWhere(
database,
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), top_level_key)
);
}
// Otherwise, update the existing JSON without the deleted key
r.value = json_value.dump();
DataBucketsRepository::UpdateOne(database, r);
// Update cache
if (CanCache(k)) {
for (auto &e : g_data_bucket_cache) {
if (CheckBucketMatch(e, top_level_k)) {
e.value = r.value;
break;
}
}
}
return true;
}
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket expiration key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id
);
auto r = GetData(k);
if (r.id == 0) {
return {};
}
return std::to_string(r.expires);
}
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket remaining key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}]",
k.key,
k.bot_id,
k.account_id,
k.character_id,
k.npc_id,
k.bot_id,
k.zone_id,
k.instance_id
);
auto r = GetData(k);
if (r.id == 0) {
return "0";
}
return fmt::format("{}", r.expires - (long long) std::time(nullptr));
}
std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
{
std::vector<std::string> q = {};
if (k.character_id > 0) {
q.emplace_back(fmt::format("character_id = {}", k.character_id));
}
else {
q.emplace_back("character_id = 0");
}
if (k.account_id > 0) {
q.emplace_back(fmt::format("account_id = {}", k.account_id));
}
else {
q.emplace_back("account_id = 0");
}
if (k.npc_id > 0) {
q.emplace_back(fmt::format("npc_id = {}", k.npc_id));
}
else {
q.emplace_back("npc_id = 0");
}
if (k.bot_id > 0) {
q.emplace_back(fmt::format("bot_id = {}", k.bot_id));
}
else {
q.emplace_back("bot_id = 0");
}
if (k.zone_id > 0) {
q.emplace_back(fmt::format("zone_id = {} AND instance_id = {}", k.zone_id, k.instance_id));
}
else {
q.emplace_back("zone_id = 0 AND instance_id = 0");
}
return fmt::format(
"{} {}",
Strings::Join(q, " AND "),
!q.empty() ? "AND" : ""
);
}
bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k)
{
return (
dbe.key_ == k.key &&
dbe.bot_id == k.bot_id &&
dbe.account_id == k.account_id &&
dbe.character_id == k.character_id &&
dbe.npc_id == k.npc_id &&
dbe.zone_id == k.zone_id &&
dbe.instance_id == k.instance_id
);
}
void DataBucket::LoadZoneCache(uint16 zone_id, uint16 instance_id)
{
const auto &l = DataBucketsRepository::GetWhere(
database,
fmt::format(
"zone_id = {} AND instance_id = {} AND (`expires` > {} OR `expires` = 0)",
zone_id,
instance_id,
(long long) std::time(nullptr)
)
);
if (l.empty()) {
return;
}
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
uint32 added_count = 0;
for (const auto &e: l) {
if (!ExistsInCache(e)) {
added_count++;
}
}
for (const auto &e: l) {
if (!ExistsInCache(e)) {
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
g_data_bucket_cache.emplace_back(e);
}
}
LogDataBucketsDetail("cache size after [{}]", g_data_bucket_cache.size());
LogDataBuckets(
"Loaded [{}] zone keys new cache size is [{}]",
l.size(),
g_data_bucket_cache.size()
);
}
void DataBucket::BulkLoadEntitiesToCache(DataBucketLoadType::Type t, std::vector<uint32> ids)
{
if (ids.empty()) {
return;
}
if (ids.size() == 1) {
bool has_cache = false;
for (const auto &e: g_data_bucket_cache) {
if (t == DataBucketLoadType::Bot) {
has_cache = e.bot_id == ids[0];
}
else if (t == DataBucketLoadType::Account) {
has_cache = e.account_id == ids[0];
}
else if (t == DataBucketLoadType::Client) {
has_cache = e.character_id == ids[0];
}
}
if (has_cache) {
LogDataBucketsDetail("LoadType [{}] ID [{}] has cache", DataBucketLoadType::Name[t], ids[0]);
return;
}
}
std::string column;
switch (t) {
case DataBucketLoadType::Bot:
column = "bot_id";
break;
case DataBucketLoadType::Client:
column = "character_id";
break;
case DataBucketLoadType::Account:
column = "account_id";
break;
default:
LogError("Incorrect LoadType [{}]", static_cast<int>(t));
break;
}
const auto &l = DataBucketsRepository::GetWhere(
database,
fmt::format(
"{} IN ({}) AND (`expires` > {} OR `expires` = 0)",
column,
Strings::Join(ids, ", "),
(long long) std::time(nullptr)
)
);
if (l.empty()) {
return;
}
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
uint32 added_count = 0;
for (const auto &e: l) {
if (!ExistsInCache(e)) {
added_count++;
}
}
for (const auto &e: l) {
if (!ExistsInCache(e)) {
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
g_data_bucket_cache.emplace_back(e);
}
}
LogDataBucketsDetail("cache size after [{}]", g_data_bucket_cache.size());
LogDataBuckets(
"Bulk Loaded ids [{}] column [{}] new cache size is [{}]",
ids.size(),
column,
g_data_bucket_cache.size()
);
}
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type type, uint32 id, uint32 secondary_id)
{
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
return (
(type == DataBucketLoadType::Bot && e.bot_id == id) ||
(type == DataBucketLoadType::Account && e.account_id == id) ||
(type == DataBucketLoadType::Client && e.character_id == id) ||
(type == DataBucketLoadType::Zone && e.zone_id == id && e.instance_id == secondary_id)
);
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"LoadType [{}] id [{}] cache size before [{}] after [{}]",
DataBucketLoadType::Name[type],
id,
size_before,
g_data_bucket_cache.size()
);
}
bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &entry)
{
for (const auto &e: g_data_bucket_cache) {
if (e.id == entry.id) {
return true;
}
}
return false;
}
void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
{
// delete from cache where there might have been a written bucket miss to the cache
// this is to prevent the cache from growing too large
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &ce) {
return ce.id == 0 && ce.key_ == e.key_ &&
ce.account_id == e.account_id &&
ce.character_id == e.character_id &&
ce.npc_id == e.npc_id &&
ce.bot_id == e.bot_id &&
ce.zone_id == e.zone_id &&
ce.instance_id == e.instance_id;
}
),
g_data_bucket_cache.end()
);
LogDataBucketsDetail(
"Deleted bucket misses from cache where key [{}] size before [{}] after [{}]",
e.key_,
size_before,
g_data_bucket_cache.size()
);
}
void DataBucket::ClearCache()
{
g_data_bucket_cache.clear();
LogInfo("Cleared data buckets cache");
}
void DataBucket::DeleteFromCache(uint64 id, DataBucketLoadType::Type type)
{
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
switch (type) {
case DataBucketLoadType::Bot:
return e.bot_id == id;
case DataBucketLoadType::Client:
return e.character_id == id;
case DataBucketLoadType::Account:
return e.account_id == id;
default:
return false;
}
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"Deleted [{}] id [{}] from cache size before [{}] after [{}]",
DataBucketLoadType::Name[type],
id,
size_before,
g_data_bucket_cache.size()
);
}
void DataBucket::DeleteZoneFromCache(uint16 zone_id, uint16 instance_id, DataBucketLoadType::Type type)
{
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketsRepository::DataBuckets &e) {
switch (type) {
case DataBucketLoadType::Zone:
return e.zone_id == zone_id && e.instance_id == instance_id;
default:
return false;
}
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"Deleted zone [{}] instance [{}] from cache size before [{}] after [{}]",
zone_id,
instance_id,
size_before,
g_data_bucket_cache.size()
);
}
// CanCache returns whether a bucket can be cached or not
// characters are only in one zone at a time so we can cache locally to the zone
// bots (not implemented) are only in one zone at a time so we can cache locally to the zone
// npcs (ids) can be in multiple zones so we can't cache locally to the zone
bool DataBucket::CanCache(const DataBucketKey &key)
{
if (key.character_id > 0 || key.account_id > 0 || key.bot_id > 0 || key.zone_id > 0) {
return true;
}
return false;
}
+1237 -1223
View File
File diff suppressed because it is too large Load Diff
+118 -135
View File
@@ -38,7 +38,8 @@
class MySQLRequestResult;
class Client;
namespace EQ {
namespace EQ
{
class InventoryProfile;
}
@@ -54,8 +55,7 @@ struct VarCache_Struct {
uint32 last_update;
VarCache_Struct() : last_update(0) { }
void Add(const std::string &key, const std::string &value) { m_cache[key] = value; }
const std::string* Get(const std::string& key)
{
const std::string *Get(const std::string &key) {
auto it = m_cache.find(key);
return (it != m_cache.end() ? &it->second : nullptr);
}
@@ -76,34 +76,37 @@ class PTimerList;
#define SQL(...) #__VA_ARGS__
class LogSettings;
class Database : public DBcore {
public:
Database();
Database(
const std::string& host,
const std::string& user,
const std::string& password,
const std::string& database,
uint32 port
);
bool Connect(
const std::string& host,
const std::string& user,
const std::string& password,
const std::string& database,
uint32 port,
std::string connection_label = "default"
);
Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port);
bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port, std::string connection_label = "default");
~Database();
/* Character Creation */
bool DeleteCharacter(const std::string& name);
bool MoveCharacterToZone(const std::string& name, uint32 zone_id);
bool CreateCharacter(
uint32 account_id,
char *name,
uint16 gender,
uint16 race,
uint16 class_,
uint8 str,
uint8 sta,
uint8 cha,
uint8 dex,
uint8 int_,
uint8 agi,
uint8 wis,
uint8 face
);
bool DeleteCharacter(char *character_name);
bool MoveCharacterToZone(const char *charname, uint32 zone_id);
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
bool ReserveName(uint32 account_id, const std::string& name);
bool ReserveName(uint32 account_id, char *name);
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp);
bool UpdateName(const std::string& old_name, const std::string& new_name);
bool UpdateNameByID(const int character_id, const std::string& new_name);
bool UpdateName(const char *oldname, const char *newname);
bool CopyCharacter(
const std::string& source_character_name,
const std::string& destination_character_name,
@@ -111,175 +114,155 @@ public:
);
/* General Information Queries */
bool AddBannedIP(const std::string& banned_ip, const std::string& notes); //Add IP address to the banned_ips table.
bool AddToNameFilter(const std::string& name);
bool CheckBannedIPs(const std::string& login_ip); //Check incoming connection against banned IP table.
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
bool CheckNameFilter(const std::string& name, bool surname = false);
bool IsNameUsed(const std::string& name);
bool IsPetNameUsed(const std::string& name);
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
uint32 GetAccountIDByChar(uint32 character_id);
uint32 GetAccountIDByName(const std::string& account_name, const std::string& loginserver, int16* status = 0, uint32* lsid = 0);
bool AddBannedIP(std::string banned_ip, std::string notes); //Add IP address to the banned_ips table.
bool AddToNameFilter(std::string name);
bool CheckBannedIPs(std::string login_ip); //Check incoming connection against banned IP table.
bool CheckGMIPs(std::string login_ip, uint32 account_id);
bool CheckNameFilter(std::string name, bool surname = false);
bool CheckUsedName(std::string name);
uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0);
uint32 GetAccountIDByChar(uint32 char_id);
uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetCharacterID(const std::string& name);
uint32 GetGuildIDByCharID(uint32 character_id);
uint32 GetGroupIDByCharID(uint32 character_id);
uint32 GetRaidIDByCharID(uint32 character_id);
uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id);
uint32 GetGuildIDByCharID(uint32 char_id);
uint32 GetGroupIDByCharID(uint32 char_id);
uint32 GetRaidIDByCharID(uint32 char_id);
const std::string GetAccountName(uint32 account_id, uint32* lsaccount_id = 0);
const std::string GetCharName(uint32 character_id);
const std::string GetCharNameByID(uint32 character_id);
const std::string GetNPCNameByID(uint32 npc_id);
const std::string GetCleanNPCNameByID(uint32 npc_id);
void LoginIP(uint32 account_id, const std::string& login_ip);
void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0);
void GetCharName(uint32 char_id, char* name);
std::string GetCharNameByID(uint32 char_id);
std::string GetNPCNameByID(uint32 npc_id);
std::string GetCleanNPCNameByID(uint32 npc_id);
void LoginIP(uint32 account_id, std::string login_ip);
/* Instancing */
bool AddClientToInstance(uint16 instance_id, uint32 character_id);
bool CheckInstanceByCharID(uint16 instance_id, uint32 character_id);
bool CheckInstanceExists(uint16 instance_id);
bool CheckInstanceExpired(uint16 instance_id);
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
bool GetUnusedInstanceID(uint16 &instance_id);
bool TryGetUnusedInstanceID(uint16& instance_id);
bool IsGlobalInstance(uint16 instance_id);
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
bool RemoveClientsFromInstance(uint16 instance_id);
bool VerifyInstanceAlive(uint16 instance_id, uint32 character_id);
bool VerifyZoneInstance(uint32 zone_id, uint16 instance_id);
uint16 GetInstanceID(uint32 zone, uint32 character_id, int16 version);
uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version);
std::vector<uint16> GetInstanceIDs(uint32 zone_id, uint32 character_id);
uint8_t GetInstanceVersion(uint16 instance_id);
uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma);
uint32 GetInstanceZoneID(uint16 instance_id);
void AssignGroupToInstance(uint32 group_id, uint32 instance_id);
void AssignRaidToInstance(uint32 raid_id, uint32 instance_id);
void AssignGroupToInstance(uint32 gid, uint32 instance_id);
void AssignRaidToInstance(uint32 rid, uint32 instance_id);
void DeleteInstance(uint16 instance_id);
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id);
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 raid_id);
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 charid, uint32 group_id);
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 charid, uint32 raid_id);
void GetCharactersInInstance(uint16 instance_id, std::list<uint32> &character_ids);
void PurgeExpiredInstances();
void SetInstanceDuration(uint16 instance_id, uint32 new_duration);
void CleanupInstanceCorpses();
/* Adventure related. */
void UpdateAdventureStatsEntry(uint32 character_id, uint8 theme_id, bool is_win = false, bool is_remove = false);
bool GetAdventureStats(uint32 character_id, AdventureStats_Struct* as);
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false);
bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as);
/* Account Related */
const std::string GetLiveChar(uint32 account_id);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 account_id, const std::string& password);
bool UpdateLiveChar(const std::string& name, uint32 account_id);
int16 GetAccountStatus(uint32 account_id);
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
uint32 CreateAccount(
const std::string& name,
const std::string& password,
int16 status,
const std::string& loginserver,
uint32 lsaccount_id
);
uint32 GetAccountIDFromLSID(
const std::string& in_loginserver_id,
uint32 in_loginserver_account_id,
char* in_account_name = 0,
int16* in_status = 0
);
bool DeleteAccount(const char *name, const char* loginserver);
bool GetLiveChar(uint32 account_id, char* cname);
bool SetAccountStatus(const char* name, int16 status);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 accid, const char* password);
bool UpdateLiveChar(char* charname, uint32 account_id);
int16 CheckStatus(uint32 account_id);
void SetAccountCRCField(uint32 account_id, std::string field_name, uint64 checksum);
uint32 CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus = 0);
uint32 CreateAccount(const char* name, const char* password, int16 status, const char* loginserver, uint32 lsaccount_id);
uint32 GetAccountIDFromLSID(const std::string& in_loginserver_id, uint32 in_loginserver_account_id, char* in_account_name = 0, int16* in_status = 0);
uint8 GetAgreementFlag(uint32 account_id);
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
void SetAgreementFlag(uint32 account_id);
int GetIPExemption(const std::string& account_ip);
void SetIPExemption(const std::string& account_ip, int exemption_amount);
int GetIPExemption(std::string account_ip);
void SetIPExemption(std::string account_ip, int exemption_amount);
int GetInstanceID(uint32 character_id, uint32 zone_id);
int GetInstanceID(uint32 char_id, uint32 zone_id);
/* Groups */
std::string GetGroupLeaderForLogin(const std::string& character_name);
char* GetGroupLeadershipInfo(
uint32 group_id,
char* leaderbuf,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
char* mentoree = nullptr,
int* mentor_percent = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr
);
std::string GetGroupLeaderName(uint32 group_id);
uint32 GetGroupID(const std::string& name);
void ClearGroup(uint32 group_id = 0);
void ClearGroupLeader(uint32 group_id = 0);
void SetGroupLeaderName(uint32 group_id, const std::string& name);
std::string GetGroupLeaderForLogin(std::string character_name);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
uint32 GetGroupID(const char* name);
void ClearGroup(uint32 gid = 0);
void ClearGroupLeader(uint32 gid = 0);
void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false);
void SetGroupLeaderName(uint32 gid, const char* name);
/* Raids */
const std::string GetRaidLeaderName(uint32 raid_id);
uint32 GetRaidID(const std::string& name);
void ClearRaid(uint32 raid_id = 0);
void ClearRaidDetails(uint32 raid_id = 0);
void ClearRaidLeader(uint32 group_id = std::numeric_limits<uint32>::max(), uint32 raid_id = 0);
void GetGroupLeadershipInfo(
uint32 group_id,
uint32 raid_id,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
char* mentoree = nullptr,
int* mentor_percent = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr
);
void GetRaidLeadershipInfo(
uint32 raid_id,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
RaidLeadershipAA_Struct* RLAA = nullptr
);
void SetRaidGroupLeaderInfo(uint32 group_id, uint32 raid_id);
const char *GetRaidLeaderName(uint32 rid);
uint32 GetRaidID(const char* name);
void ClearRaid(uint32 rid = 0);
void ClearRaidDetails(uint32 rid = 0);
void ClearRaidLeader(uint32 gid = 0xFFFFFFFF, uint32 rid = 0);
void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, RaidLeadershipAA_Struct* RLAA = nullptr);
void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid);
void PurgeAllDeletedDataBuckets();
void ClearGuildOnlineStatus();
void ClearTraderDetails();
void ClearBuyerDetails();
/* Database Variables */
bool GetVariable(const std::string& name, std::string& value);
bool SetVariable(const std::string& name, const std::string& value);
bool GetVariable(std::string varname, std::string &varvalue);
bool SetVariable(const std::string& varname, const std::string &varvalue);
bool LoadVariables();
/* General Queries */
bool GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid = 0, float* graveyard_x = 0, float* graveyard_y = 0, float* graveyard_z = 0, float* graveyard_heading = 0);
bool LoadPTimers(uint32 charid, PTimerList &into);
uint8 GetPEQZone(uint32 zone_id, uint32 version);
uint32 GetServerType();
void AddReport(const std::string& who, const std::string& against, const std::string& lines);
uint8 GetMinStatus(uint32 zone_id, uint32 instance_version);
uint8 GetRaceSkill(uint8 skillid, uint8 in_race);
uint8 GetServerType();
uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level);
void AddReport(std::string who, std::string against, std::string lines);
struct TimeOfDay_Struct LoadTime(time_t &realtime);
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
void ClearMerchantTemp();
void ClearPTimers(uint32 character_id);
void SetIngame(uint32 character_id, uint8 ingame);
void SetLFG(uint32 character_id, bool is_lfg);
void SetLFP(uint32 character_id, bool is_lfp);
void SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon);
void ClearPTimers(uint32 charid);
void SetFirstLogon(uint32 CharID, uint8 firstlogon);
void SetLFG(uint32 CharID, bool LFG);
void SetLFP(uint32 CharID, bool LFP);
void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon);
int64 CountInvSnapshots();
int CountInvSnapshots();
void ClearInvSnapshots(bool from_now = false);
void SourceDatabaseTableFromUrl(const std::string& table_name, const std::string& url);
void SourceSqlFromUrl(const std::string& url);
void PurgeCharacterParcels();
void Encode(std::string &in);
void Decode(std::string &in);
uint64_t GetNextTableId(const std::string& table_name);
void SourceDatabaseTableFromUrl(std::string table_name, std::string url);
void SourceSqlFromUrl(std::string url);
private:
Mutex Mvarcache;
VarCache_Struct varcache;
+24 -13
View File
@@ -50,7 +50,7 @@ bool DatabaseDumpService::IsMySQLInstalled()
{
std::string version_output = GetMySQLVersion();
return version_output.find("mysql") != std::string::npos && (version_output.find("Ver") != std::string::npos || version_output.find("from") != std::string::npos);
return version_output.find("mysql") != std::string::npos && version_output.find("Ver") != std::string::npos;
}
/**
@@ -136,6 +136,11 @@ std::string DatabaseDumpService::GetLoginTableList()
return Strings::Join(DatabaseSchema::GetLoginTables(), " ");
}
std::string DatabaseDumpService::GetQueryServTables()
{
return Strings::Join(DatabaseSchema::GetQueryServerTables(), " ");
}
std::string DatabaseDumpService::GetSystemTablesList()
{
auto system_tables = DatabaseSchema::GetServerTables();
@@ -204,7 +209,7 @@ void DatabaseDumpService::DatabaseDump()
}
if (IsDumpOutputToConsole()) {
EQEmuLogSys::Instance()->SilenceConsoleLogging();
LogSys.SilenceConsoleLogging();
}
LogInfo("MySQL installed [{}]", GetMySQLVersion());
@@ -267,6 +272,11 @@ void DatabaseDumpService::DatabaseDump()
tables_to_dump += GetLoginTableList() + " ";
dump_descriptor += "-login";
}
if (IsDumpQueryServerTables()) {
tables_to_dump += GetQueryServTables();
dump_descriptor += "-queryserv";
}
}
if (IsDumpStaticInstanceData()) {
@@ -324,7 +334,7 @@ void DatabaseDumpService::DatabaseDump()
}
if (!IsDumpOutputToConsole()) {
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
LogSys.LoadLogSettingsDefaults();
}
if (!pipe_file.empty()) {
@@ -391,6 +401,7 @@ void DatabaseDumpService::DatabaseDump()
// LogDebug("[{}] dump-to-console", IsDumpOutputToConsole());
// LogDebug("[{}] dump-path", GetSetDumpPath());
// LogDebug("[{}] compression", (IsDumpWithCompression() ? "true" : "false"));
// LogDebug("[{}] query-serv", (IsDumpQueryServerTables() ? "true" : "false"));
// LogDebug("[{}] has-compression-binary", (HasCompressionBinary() ? "true" : "false"));
// LogDebug("[{}] content", (IsDumpContentTables() ? "true" : "false"));
// LogDebug("[{}] no-data", (IsDumpWithNoData() ? "true" : "false"));
@@ -500,6 +511,16 @@ const std::string &DatabaseDumpService::GetDumpFileName() const
return dump_file_name;
}
bool DatabaseDumpService::IsDumpQueryServerTables() const
{
return dump_query_server_tables;
}
void DatabaseDumpService::SetDumpQueryServerTables(bool dump_query_server_tables)
{
DatabaseDumpService::dump_query_server_tables = dump_query_server_tables;
}
bool DatabaseDumpService::IsDumpOutputToConsole() const
{
return dump_output_to_console;
@@ -554,13 +575,8 @@ void DatabaseDumpService::RemoveSqlBackup()
{
std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath());
if (File::Exists(file)) {
try {
std::filesystem::remove(file);
}
catch (std::exception &e) {
LogError("std::filesystem::remove err [{}]", e.what());
}
}
RemoveCredentialsFile();
}
@@ -596,13 +612,8 @@ void DatabaseDumpService::BuildCredentialsFile()
void DatabaseDumpService::RemoveCredentialsFile()
{
if (File::Exists(CREDENTIALS_FILE)) {
try {
std::filesystem::remove(CREDENTIALS_FILE);
}
catch (std::exception &e) {
LogError("std::filesystem::remove err [{}]", e.what());
}
}
}
bool DatabaseDumpService::IsDumpStaticInstanceData()
+4
View File
@@ -45,6 +45,8 @@ public:
void SetDumpPath(const std::string &dump_path);
const std::string &GetDumpFileName() const;
void SetDumpFileName(const std::string &dump_file_name);
bool IsDumpQueryServerTables() const;
void SetDumpQueryServerTables(bool dump_query_server_tables);
bool IsDumpOutputToConsole() const;
void SetDumpOutputToConsole(bool dump_output_to_console);
bool IsDumpDropTableSyntaxOnly() const;
@@ -65,6 +67,7 @@ private:
bool dump_system_tables = false;
bool dump_content_tables = false;
bool dump_player_tables = false;
bool dump_query_server_tables = false;
bool dump_login_server_tables = false;
bool dump_with_no_data = false;
bool dump_table_lock = false;
@@ -93,6 +96,7 @@ private:
bool HasCompressionBinary();
std::string GetDumpFileNameWithPath();
std::string GetSetDumpPath();
std::string GetQueryServTables();
void RemoveSqlBackup();
void BuildCredentialsFile();
void RemoveCredentialsFile();
+5 -91
View File
@@ -7,7 +7,6 @@
#include "../http/httplib.h"
#include "database_update_manifest.cpp"
#include "database_update_manifest_custom.cpp"
#include "database_update_manifest_bots.cpp"
#include "database_dump_service.h"
@@ -15,7 +14,7 @@ constexpr int BREAK_LENGTH = 70;
DatabaseVersion DatabaseUpdate::GetDatabaseVersions()
{
auto results = m_database->QueryDatabase("SELECT `version`, `bots_version`, `custom_version` FROM `db_version` LIMIT 1");
auto results = m_database->QueryDatabase("SELECT `version`, `bots_version` FROM `db_version` LIMIT 1");
if (!results.Success() || !results.RowCount()) {
LogError("Failed to read from [db_version] table!");
return DatabaseVersion{};
@@ -26,7 +25,6 @@ DatabaseVersion DatabaseUpdate::GetDatabaseVersions()
return DatabaseVersion{
.server_database_version = Strings::ToInt(r[0]),
.bots_database_version = Strings::ToInt(r[1]),
.custom_database_version = Strings::ToInt(r[2]),
};
}
@@ -35,7 +33,6 @@ DatabaseVersion DatabaseUpdate::GetBinaryDatabaseVersions()
return DatabaseVersion{
.server_database_version = CURRENT_BINARY_DATABASE_VERSION,
.bots_database_version = (RuleB(Bots, Enabled) ? CURRENT_BINARY_BOTS_DATABASE_VERSION : 0),
.custom_database_version = CUSTOM_BINARY_DATABASE_VERSION,
};
}
@@ -46,7 +43,6 @@ constexpr int LOOK_BACK_AMOUNT = 10;
// this check will take action
void DatabaseUpdate::CheckDbUpdates()
{
InjectCustomVersionColumn();
InjectBotsVersionColumn();
auto v = GetDatabaseVersions();
auto b = GetBinaryDatabaseVersions();
@@ -63,15 +59,6 @@ void DatabaseUpdate::CheckDbUpdates()
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `version` = {}", b.server_database_version));
}
if (UpdateManifest(manifest_entries_custom, v.custom_database_version, b.custom_database_version)) {
LogInfo(
"Updates ran successfully, setting database version to [{}] from [{}]",
b.custom_database_version,
v.custom_database_version
);
m_database->QueryDatabase(fmt::format("UPDATE `db_version` SET `custom_version` = {}", b.custom_database_version));
}
if (b.bots_database_version > 0) {
if (UpdateManifest(bot_manifest_entries, v.bots_database_version, b.bots_database_version)) {
LogInfo(
@@ -154,8 +141,7 @@ bool DatabaseUpdate::UpdateManifest(
std::vector<int> missing_migrations = {};
if (version_low != version_high) {
EQEmuLogSys::Instance()->DisableMySQLErrorLogs();
bool force_interactive = false;
LogSys.DisableMySQLErrorLogs();
for (int version = version_low + 1; version <= version_high; ++version) {
for (auto &e: entries) {
if (e.version == version) {
@@ -177,20 +163,13 @@ bool DatabaseUpdate::UpdateManifest(
prefix,
e.description
);
if (!has_migration && e.force_interactive) {
force_interactive = true;
}
}
}
}
EQEmuLogSys::Instance()->EnableMySQLErrorLogs();
LogSys.EnableMySQLErrorLogs();
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
if (!missing_migrations.empty() && m_skip_backup) {
LogInfo("Skipping database backup");
}
else if (!missing_migrations.empty()) {
if (!missing_migrations.empty()) {
LogInfo("Automatically backing up database before applying updates");
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
auto s = DatabaseDumpService();
@@ -205,42 +184,6 @@ bool DatabaseUpdate::UpdateManifest(
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
}
if (force_interactive && !std::getenv("FORCE_INTERACTIVE")) {
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
LogInfo("Some migrations require user input. Running interactively");
LogInfo("This is usually due to a major change that could cause data loss");
LogInfo("Your server is automatically backed up before these updates are applied");
LogInfo("but you should also make sure you take a backup prior to running this update");
LogInfo("Would you like to run this update? [y/n] (Timeout 60s)");
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
// user input
std::string input;
bool gave_input = false;
time_t start_time = time(nullptr);
time_t wait_time_seconds = 60;
// spawn a concurrent thread that waits for input from std::cin
std::thread t1(
[&]() {
std::cin >> input;
gave_input = true;
}
);
t1.detach();
// check the inputReceived flag once every 50ms for 10 seconds
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// prompt for user skip
if (Strings::Trim(input) != "y") {
LogInfo("Exiting due to user input");
std::exit(1);
}
}
for (auto &m: missing_migrations) {
for (auto &e: entries) {
if (e.version == m) {
@@ -328,13 +271,6 @@ DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
return this;
}
DatabaseUpdate *DatabaseUpdate::SetSkipBackup(bool skip)
{
m_skip_backup = skip;
return this;
}
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
{
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
@@ -357,16 +293,6 @@ bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
);
}
if (b.custom_database_version > 0) {
LogInfo(
"{:>8} | database [{}] binary [{}] {}",
"Custom",
v.custom_database_version,
b.custom_database_version,
(v.custom_database_version == b.custom_database_version) ? "up to date" : "checking updates"
);
}
LogInfo("{:>8} | [server.auto_database_updates] [<green>true]", "Config");
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
@@ -376,10 +302,7 @@ bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
// bots database version is optional, if not enabled then it is always up-to-date
bool bots_up_to_date = RuleB(Bots, Enabled) ? v.bots_database_version >= b.bots_database_version : true;
// custom database version is optional, if not enabled then it is always up-to-date
bool custom_up_to_date = v.custom_database_version >= b.custom_database_version;
return server_up_to_date && bots_up_to_date && custom_up_to_date;
return server_up_to_date && bots_up_to_date;
}
// checks to see if there are pending updates
@@ -399,12 +322,3 @@ void DatabaseUpdate::InjectBotsVersionColumn()
m_database->QueryDatabase("ALTER TABLE db_version ADD bots_version int(11) DEFAULT '0' AFTER version");
}
}
void DatabaseUpdate::InjectCustomVersionColumn()
{
auto results = m_database->QueryDatabase("SHOW COLUMNS FROM `db_version` LIKE 'custom_version'");
if (!results.Success() || results.RowCount() == 0) {
LogInfo("Adding custom_version column to db_version table");
m_database->QueryDatabase("ALTER TABLE `db_version` ADD COLUMN `custom_version` INT(11) UNSIGNED NOT NULL DEFAULT 0");
}
}
-13
View File
@@ -11,13 +11,11 @@ struct ManifestEntry {
std::string match{}; // match field that is not always used, but works in conjunction with "condition" values [missing|match|contains]
std::string sql{}; // the SQL DDL that gets ran when the condition is true
bool content_schema_update{}; // if true, this migration is a content schema update and should be ran against the content database
bool force_interactive; // if true, this migration will always be run interactively
};
struct DatabaseVersion {
int server_database_version;
int bots_database_version;
int custom_database_version;
};
class DatabaseUpdate {
@@ -31,23 +29,12 @@ public:
DatabaseUpdate *SetDatabase(Database *db);
DatabaseUpdate *SetContentDatabase(Database *db);
DatabaseUpdate *SetSkipBackup(bool skip);
bool HasPendingUpdates();
static DatabaseUpdate* Instance()
{
static DatabaseUpdate instance;
return &instance;
}
private:
bool m_skip_backup = false;
Database *m_database;
Database *m_content_database;
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
void InjectBotsVersionColumn();
void InjectCustomVersionColumn();
};
#endif //EQEMU_DATABASE_UPDATE_H
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,55 +0,0 @@
#include "database_update.h"
std::vector<ManifestEntry> manifest_entries_custom = {
ManifestEntry{
.version = 1,
.description = "2025_05_16_new_database_check_test",
.check = "SHOW TABLES LIKE 'new_table'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `new_table` (
`id` int NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
)",
.content_schema_update = false,
},
// Used for testing
// ManifestEntry{
// .version = 9229,
// .description = "new_database_check_test",
// .check = "SHOW TABLES LIKE 'new_table'",
// .condition = "empty",
// .match = "",
// .sql = R"(
//CREATE TABLE `new_table` (
// `id` int NOT NULL AUTO_INCREMENT,
// PRIMARY KEY (`id`)
//);
//CREATE TABLE `new_table1` (
// `id` int NOT NULL AUTO_INCREMENT,
// PRIMARY KEY (`id`)
//);
//CREATE TABLE `new_table2` (
// `id` int NOT NULL AUTO_INCREMENT,
// PRIMARY KEY (`id`)
//);
//CREATE TABLE `new_table3` (
// `id` int NOT NULL AUTO_INCREMENT,
// PRIMARY KEY (`id`)
//);
//)",
// }
};
// see struct definitions for what each field does
// struct ManifestEntry {
// int version{}; // database version of the migration
// std::string description{}; // description of the migration ex: "add_new_table" or "add_index_to_table"
// std::string check{}; // query that checks against the condition
// std::string condition{}; // condition or "match_type" - Possible values [contains|match|missing|empty|not_empty]
// std::string match{}; // match field that is not always used, but works in conjunction with "condition" values [missing|match|contains]
// std::string sql{}; // the SQL DDL that gets ran when the condition is true
// };
+28 -68
View File
@@ -30,8 +30,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/respawn_times_repository.h"
#include "../common/repositories/spawn_condition_values_repository.h"
#include "repositories/spawn2_disabled_repository.h"
#include "repositories/data_buckets_repository.h"
#include "repositories/zone_state_spawns_repository.h"
#include "database.h"
#include <iomanip>
@@ -114,9 +114,7 @@ bool Database::CheckInstanceExpired(uint16 instance_id)
timeval tv{};
gettimeofday(&tv, nullptr);
// Use uint64_t for the addition to prevent overflow
uint64_t expiration_time = static_cast<uint64_t>(i.start_time) + static_cast<uint64_t>(i.duration);
return expiration_time <= tv.tv_sec;
return (i.start_time + i.duration) <= tv.tv_sec;
}
bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration)
@@ -128,35 +126,11 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
e.version = version;
e.start_time = std::time(nullptr);
e.duration = duration;
e.expire_at = e.start_time + duration;
RespawnTimesRepository::ClearInstanceTimers(*this, e.id);
InstanceListRepository::ReplaceOne(*this, e);
return instance_id > 0 && e.id;
return InstanceListRepository::InsertOne(*this, e).id;
}
bool Database::GetUnusedInstanceID(uint16 &instance_id)
{
// attempt to get an unused instance id
for (int a = 0; a < 10; a++) {
uint16 attempted_id = 0;
if (TryGetUnusedInstanceID(attempted_id)) {
auto i = InstanceListRepository::NewEntity();
i.id = attempted_id;
i.notes = "Prefetching";
auto n = InstanceListRepository::InsertOne(*this, i);
if (n.id > 0) {
instance_id = n.id;
return true;
}
}
}
instance_id = 0;
return false;
}
bool Database::TryGetUnusedInstanceID(uint16 &instance_id)
{
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
uint32 max_instance_id = 32000;
@@ -447,25 +421,20 @@ void Database::AssignGroupToInstance(uint32 group_id, uint32 instance_id)
auto zone_id = GetInstanceZoneID(instance_id);
auto version = GetInstanceVersion(instance_id);
const auto& l = GroupIdRepository::GetWhere(
auto l = GroupIdRepository::GetWhere(
*this,
fmt::format(
"`group_id` = {}",
"groupid = {}",
group_id
)
);
if (l.empty()) {
return;
}
for (const auto& e : l) {
if (!e.character_id) {
continue;
}
if (!GetInstanceID(zone_id, e.character_id, version)) {
AddClientToInstance(instance_id, e.character_id);
if (!GetInstanceID(zone_id, e.charid, version)) {
AddClientToInstance(instance_id, e.charid);
}
}
}
@@ -495,18 +464,16 @@ void Database::AssignRaidToInstance(uint32 raid_id, uint32 instance_id)
void Database::DeleteInstance(uint16 instance_id)
{
// I'm not sure why this isn't in here but we should add it in a later change and make sure it's tested
// InstanceListRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id));
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id = {}", instance_id));
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
DynamicZoneMembersRepository::DeleteByInstance(*this, instance_id);
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
if (RuleB(Zone, StateSavingOnShutdown)) {
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` = {}", instance_id));
}
}
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
@@ -537,7 +504,7 @@ void Database::FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 ch
return;
}
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id).c_str());
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id));
auto raid_leader_instance_id = GetInstanceID(zone_id, raid_leader_id, version);
if (!raid_leader_instance_id) {
@@ -561,12 +528,14 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
void Database::PurgeExpiredInstances()
{
/**
* Delay purging by a day so that we can continue using adjacent free instance id's
* from the table without risking the chance we immediately re-allocate a zone that freshly expired but
* has not been fully de-allocated
*/
auto l = InstanceListRepository::GetWhere(
*this,
fmt::format(
"expire_at <= (UNIX_TIMESTAMP() - {}) and expire_at != 0 AND never_expires = 0",
RuleI(Instances, ExpireOffsetTimeSeconds)
)
"(start_time + duration) <= (UNIX_TIMESTAMP() - 86400) AND never_expires = 0"
);
if (l.empty()) {
return;
@@ -577,24 +546,16 @@ void Database::PurgeExpiredInstances()
instance_ids.emplace_back(std::to_string(e.id));
}
const auto ids = Strings::Implode(",", instance_ids);
const auto imploded_instance_ids = Strings::Implode(",", instance_ids);
TransactionBegin();
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
CharacterCorpsesRepository::BuryInstances(*this, ids);
DynamicZoneMembersRepository::DeleteByManyInstances(*this, ids);
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", ids));
if (RuleB(Zone, StateSavingOnShutdown)) {
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", ids));
}
TransactionCommit();
LogInfo("Purged [{}] expired instances", l.size());
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids);
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
}
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
@@ -606,7 +567,6 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
i.start_time = std::time(nullptr);
i.duration = new_duration;
i.expire_at = i.start_time + i.duration;
InstanceListRepository::UpdateOne(*this, i);
}
+30 -31
View File
@@ -36,6 +36,7 @@ namespace DatabaseSchema {
{
return {
{"adventure_stats", "player_id"},
{"buyer", "charid"},
{"char_recipe_list", "char_id"},
{"character_activities", "charid"},
{"character_alt_currency", "char_id"},
@@ -51,7 +52,6 @@ namespace DatabaseSchema {
{"character_enabledtasks", "charid"},
{"character_expedition_lockouts", "character_id"},
{"character_exp_modifiers", "character_id"},
{"character_evolving_items", "character_id"},
{"character_inspect_messages", "id"},
{"character_instance_safereturns", "character_id"},
{"character_item_recast", "id"},
@@ -59,12 +59,9 @@ namespace DatabaseSchema {
{"character_leadership_abilities", "id"},
{"character_material", "id"},
{"character_memmed_spells", "id"},
{"character_parcels", "char_id"},
{"character_parcels_containers", "id"},
{"character_pet_buffs", "char_id"},
{"character_pet_info", "char_id"},
{"character_pet_inventory", "char_id"},
{"character_pet_name", "character_id"},
{"character_peqzone_flags", "id"},
{"character_potionbelt", "id"},
{"character_skills", "id"},
@@ -80,7 +77,7 @@ namespace DatabaseSchema {
{"guild_members", "char_id"},
{"guilds", "id"},
{"instance_list_player", "id"},
{"inventory", "character_id"},
{"inventory", "charid"},
{"inventory_snapshots", "charid"},
{"keyring", "char_id"},
{"mail", "charid"},
@@ -108,8 +105,6 @@ namespace DatabaseSchema {
"adventure_details",
"adventure_stats",
"buyer",
"buyer_buy_lines",
"buyer_trade_items",
"char_recipe_list",
"character_activities",
"character_alt_currency",
@@ -126,7 +121,6 @@ namespace DatabaseSchema {
"character_enabledtasks",
"character_expedition_lockouts",
"character_exp_modifiers",
"character_evolving_items",
"character_inspect_messages",
"character_instance_safereturns",
"character_item_recast",
@@ -134,12 +128,9 @@ namespace DatabaseSchema {
"character_leadership_abilities",
"character_material",
"character_memmed_spells",
"character_parcels",
"character_parcels_containers",
"character_pet_buffs",
"character_pet_info",
"character_pet_inventory",
"character_pet_name",
"character_peqzone_flags",
"character_potionbelt",
"character_skills",
@@ -216,7 +207,6 @@ namespace DatabaseSchema {
"ground_spawns",
"horses",
"items",
"items_evolving_details",
"ldon_trap_entries",
"ldon_trap_templates",
"lootdrop",
@@ -292,6 +282,32 @@ namespace DatabaseSchema {
};
}
/**
* Gets QueryServer tables
*
* @return
*/
static std::vector<std::string> GetQueryServerTables()
{
return {
"qs_merchant_transaction_record",
"qs_merchant_transaction_record_entries",
"qs_player_aa_rate_hourly",
"qs_player_delete_record",
"qs_player_delete_record_entries",
"qs_player_events",
"qs_player_handin_record",
"qs_player_handin_record_entries",
"qs_player_move_record",
"qs_player_move_record_entries",
"qs_player_npc_kill_record",
"qs_player_npc_kill_record_entries",
"qs_player_speech",
"qs_player_trade_record",
"qs_player_trade_record_entries",
};
}
/**
* Gets state tables
* Tables that keep track of server state
@@ -305,16 +321,14 @@ namespace DatabaseSchema {
"banned_ips",
"bug_reports",
"bugs",
"buyer",
"buyer_buy_lines",
"buyer_trade_items",
"completed_shared_task_activity_state",
"completed_shared_task_members",
"completed_shared_tasks",
"discord_webhooks",
"dynamic_zone_lockouts",
"dynamic_zone_members",
"dynamic_zones",
"expedition_lockouts",
"expeditions",
"gm_ips",
"group_id",
"group_leaders",
@@ -333,25 +347,12 @@ namespace DatabaseSchema {
"saylink",
"server_scheduled_events",
"spawn2_disabled",
"player_event_aa_purchase",
"player_event_killed_npc",
"player_event_killed_named_npc",
"player_event_killed_raid_npc",
"player_event_log_settings",
"player_event_logs",
"player_event_loot_items",
"player_event_merchant_purchase",
"player_event_merchant_sell",
"player_event_npc_handin",
"player_event_npc_handin_entries",
"player_event_speech",
"player_event_trade",
"player_event_trade_entries",
"shared_task_activity_state",
"shared_task_dynamic_zones",
"shared_task_members",
"shared_tasks",
"zone_state_spawns",
};
}
@@ -393,7 +394,6 @@ namespace DatabaseSchema {
static std::vector<std::string> GetBotTables()
{
return {
"bot_blocked_buffs",
"bot_buffs",
"bot_command_settings",
"bot_create_combinations",
@@ -407,7 +407,6 @@ namespace DatabaseSchema {
"bot_pet_buffs",
"bot_pet_inventories",
"bot_pets",
"bot_settings",
"bot_spell_casting_chances",
"bot_spell_settings",
"bot_spells_entries",
+3 -11
View File
@@ -7,7 +7,6 @@
#include "timer.h"
#include "dbcore.h"
#include "mysql_stmt.h"
#include <fstream>
#include <iostream>
@@ -160,7 +159,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
(uint32) mysql_insert_id(mysql)
);
if (EQEmuLogSys::Instance()->log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
if ((strncasecmp(query, "select", 6) == 0)) {
LogMySQLQuery(
"{0} -- ({1} row{2} returned) ({3}s)",
@@ -189,9 +188,9 @@ void DBcore::TransactionBegin()
QueryDatabase("START TRANSACTION");
}
MySQLRequestResult DBcore::TransactionCommit()
void DBcore::TransactionCommit()
{
return QueryDatabase("COMMIT");
QueryDatabase("COMMIT");
}
void DBcore::TransactionRollback()
@@ -302,9 +301,7 @@ std::string DBcore::Escape(const std::string& s)
void DBcore::SetMutex(Mutex *mutex)
{
if (m_mutex && m_mutex != mutex) {
safe_delete(m_mutex);
}
DBcore::m_mutex = mutex;
}
@@ -439,8 +436,3 @@ MySQLRequestResult DBcore::QueryDatabaseMulti(const std::string &query)
return r;
}
mysql::PreparedStmt DBcore::Prepare(std::string query)
{
return mysql::PreparedStmt(*mysql, std::move(query), m_mutex);
}
+1 -8
View File
@@ -17,8 +17,6 @@
#define CR_SERVER_GONE_ERROR 2006
#define CR_SERVER_LOST 2013
namespace mysql { class PreparedStmt; }
class DBcore {
public:
enum eStatus {
@@ -32,7 +30,7 @@ public:
MySQLRequestResult QueryDatabase(const std::string& query, bool retryOnFailureOnce = true);
MySQLRequestResult QueryDatabaseMulti(const std::string &query);
void TransactionBegin();
MySQLRequestResult TransactionCommit();
void TransactionCommit();
void TransactionRollback();
std::string Escape(const std::string& s);
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
@@ -50,11 +48,6 @@ public:
}
void SetMutex(Mutex *mutex);
// only safe on connections shared with other threads if results buffered
// unsafe to use off main thread due to internal server logging
// throws std::runtime_error on failure
mysql::PreparedStmt Prepare(std::string query);
protected:
bool Open(
const char *iHost,
+70 -6
View File
@@ -19,17 +19,81 @@
#include "deity.h"
uint32 Deity::GetBitmask(uint32 deity_id)
EQ::deity::DeityTypeBit EQ::deity::GetDeityBitmask(DeityType deity_type)
{
return IsValid(deity_id) ? deity_bitmasks[deity_id] : Deity::Bitmask::All;
switch (deity_type) {
case DeityBertoxxulous:
return bit_DeityBertoxxulous;
case DeityBrellSirilis:
return bit_DeityBrellSirilis;
case DeityCazicThule:
return bit_DeityCazicThule;
case DeityErollisiMarr:
return bit_DeityErollisiMarr;
case DeityBristlebane:
return bit_DeityBristlebane;
case DeityInnoruuk:
return bit_DeityInnoruuk;
case DeityKarana:
return bit_DeityKarana;
case DeityMithanielMarr:
return bit_DeityMithanielMarr;
case DeityPrexus:
return bit_DeityPrexus;
case DeityQuellious:
return bit_DeityQuellious;
case DeityRallosZek:
return bit_DeityRallosZek;
case DeityRodcetNife:
return bit_DeityRodcetNife;
case DeitySolusekRo:
return bit_DeitySolusekRo;
case DeityTheTribunal:
return bit_DeityTheTribunal;
case DeityTunare:
return bit_DeityTunare;
case DeityVeeshan:
return bit_DeityVeeshan;
case DeityAgnostic_LB:
case DeityAgnostic:
return bit_DeityAgnostic;
default:
return bit_DeityAll;
}
}
std::string Deity::GetName(uint32 deity_id)
const std::map<EQ::deity::DeityType, std::string>& EQ::deity::GetDeityMap()
{
return IsValid(deity_id) ? deity_names[deity_id] : "UNKNOWN DEITY";
static const std::map<EQ::deity::DeityType, std::string> deity_map = {
{ DeityAgnostic, "Agnostic" },
{ DeityAgnostic_LB, "Agnostic" },
{ DeityBertoxxulous, "Bertoxxulous" },
{ DeityBrellSirilis, "Brell Serilis" },
{ DeityBristlebane, "Bristlebane" },
{ DeityCazicThule, "Cazic-Thule" },
{ DeityErollisiMarr, "Erollisi Marr" },
{ DeityInnoruuk, "Innoruuk" },
{ DeityKarana, "Karana" },
{ DeityMithanielMarr, "Mithaniel Marr" },
{ DeityPrexus, "Prexus" },
{ DeityQuellious, "Quellious" },
{ DeityRallosZek, "Rallos Zek" },
{ DeityRodcetNife, "Rodcet Nife" },
{ DeitySolusekRo, "Solusek Ro" },
{ DeityTheTribunal, "The Tribunal" },
{ DeityTunare, "Tunare" },
{ DeityVeeshan, "Veeshan" }
};
return deity_map;
}
bool Deity::IsValid(uint32 deity_id)
std::string EQ::deity::GetDeityName(DeityType deity_type)
{
return deity_names.find(deity_id) != deity_names.end();
if (EQ::deity::GetDeityMap().find(deity_type) != EQ::deity::GetDeityMap().end()) {
return EQ::deity::GetDeityMap().find(deity_type)->second;
}
return std::string();
}
+52 -85
View File
@@ -23,95 +23,62 @@
#include "types.h"
#include <map>
#include <string>
#include <limits>
namespace Deity {
constexpr uint32 Unknown = 0;
constexpr uint32 Agnostic1 = 140;
constexpr uint32 Bertoxxulous = 201;
constexpr uint32 BrellSirilis = 202;
constexpr uint32 CazicThule = 203;
constexpr uint32 ErollisiMarr = 204;
constexpr uint32 Bristlebane = 205;
constexpr uint32 Innoruuk = 206;
constexpr uint32 Karana = 207;
constexpr uint32 MithanielMarr = 208;
constexpr uint32 Prexus = 209;
constexpr uint32 Quellious = 210;
constexpr uint32 RallosZek = 211;
constexpr uint32 RodcetNife = 212;
constexpr uint32 SolusekRo = 213;
constexpr uint32 TheTribunal = 214;
constexpr uint32 Tunare = 215;
constexpr uint32 Veeshan = 216;
constexpr uint32 Agnostic2 = 396;
namespace Bitmask {
constexpr uint32 Agnostic = 1;
constexpr uint32 Bertoxxulous = 2;
constexpr uint32 BrellSirilis = 4;
constexpr uint32 CazicThule = 8;
constexpr uint32 ErollisiMarr = 16;
constexpr uint32 Bristlebane = 32;
constexpr uint32 Innoruuk = 64;
constexpr uint32 Karana = 128;
constexpr uint32 MithanielMarr = 256;
constexpr uint32 Prexus = 512;
constexpr uint32 Quellious = 1024;
constexpr uint32 RallosZek = 2048;
constexpr uint32 RodcetNife = 4096;
constexpr uint32 SolusekRo = 8192;
constexpr uint32 TheTribunal = 16384;
constexpr uint32 Tunare = 32768;
constexpr uint32 Veeshan = 65536;
constexpr uint32 All = std::numeric_limits<uint32>::max();
}
uint32 GetBitmask(uint32 deity_id);
std::string GetName(uint32 deity_id);
bool IsValid(uint32 deity_id);
}
static std::map<uint32, std::string> deity_names = {
{ Deity::Agnostic1, "Agnostic" },
{ Deity::Agnostic2, "Agnostic" },
{ Deity::Bertoxxulous, "Bertoxxulous" },
{ Deity::BrellSirilis, "Brell Serilis" },
{ Deity::Bristlebane, "Bristlebane" },
{ Deity::CazicThule, "Cazic-Thule" },
{ Deity::ErollisiMarr, "Erollisi Marr" },
{ Deity::Innoruuk, "Innoruuk" },
{ Deity::Karana, "Karana" },
{ Deity::MithanielMarr, "Mithaniel Marr" },
{ Deity::Prexus, "Prexus" },
{ Deity::Quellious, "Quellious" },
{ Deity::RallosZek, "Rallos Zek" },
{ Deity::RodcetNife, "Rodcet Nife" },
{ Deity::SolusekRo, "Solusek Ro" },
{ Deity::TheTribunal, "The Tribunal" },
{ Deity::Tunare, "Tunare" },
{ Deity::Veeshan, "Veeshan" }
namespace EQ
{
namespace deity {
enum DeityType {
DeityUnknown = 0,
DeityAgnostic_LB = 140,
DeityBertoxxulous = 201,
DeityBrellSirilis,
DeityCazicThule,
DeityErollisiMarr,
DeityBristlebane,
DeityInnoruuk,
DeityKarana,
DeityMithanielMarr,
DeityPrexus,
DeityQuellious,
DeityRallosZek,
DeityRodcetNife,
DeitySolusekRo,
DeityTheTribunal,
DeityTunare,
DeityVeeshan,
DeityAgnostic = 396
};
static std::map<uint32, uint32> deity_bitmasks = {
{ Deity::Agnostic1, Deity::Bitmask::Agnostic },
{ Deity::Agnostic2, Deity::Bitmask::Agnostic },
{ Deity::Bertoxxulous, Deity::Bitmask::Bertoxxulous },
{ Deity::BrellSirilis, Deity::Bitmask::BrellSirilis },
{ Deity::CazicThule, Deity::Bitmask::CazicThule },
{ Deity::ErollisiMarr, Deity::Bitmask::ErollisiMarr },
{ Deity::Bristlebane, Deity::Bitmask::Bristlebane },
{ Deity::Innoruuk, Deity::Bitmask::Innoruuk },
{ Deity::Karana, Deity::Bitmask::Karana },
{ Deity::MithanielMarr, Deity::Bitmask::MithanielMarr },
{ Deity::Prexus, Deity::Bitmask::Prexus },
{ Deity::Quellious, Deity::Bitmask::Quellious },
{ Deity::RallosZek, Deity::Bitmask::RallosZek },
{ Deity::RodcetNife, Deity::Bitmask::RodcetNife },
{ Deity::SolusekRo, Deity::Bitmask::SolusekRo },
{ Deity::TheTribunal, Deity::Bitmask::TheTribunal },
{ Deity::Tunare, Deity::Bitmask::Tunare },
{ Deity::Veeshan, Deity::Bitmask::Veeshan }
enum DeityTypeBit : uint32 {
bit_DeityAgnostic = 0x00000001,
bit_DeityBertoxxulous = 0x00000002,
bit_DeityBrellSirilis = 0x00000004,
bit_DeityCazicThule = 0x00000008,
bit_DeityErollisiMarr = 0x00000010,
bit_DeityBristlebane = 0x00000020,
bit_DeityInnoruuk = 0x00000040,
bit_DeityKarana = 0x00000080,
bit_DeityMithanielMarr = 0x00000100,
bit_DeityPrexus = 0x00000200,
bit_DeityQuellious = 0x00000400,
bit_DeityRallosZek = 0x00000800,
bit_DeityRodcetNife = 0x00001000,
bit_DeitySolusekRo = 0x00002000,
bit_DeityTheTribunal = 0x00004000,
bit_DeityTunare = 0x00008000,
bit_DeityVeeshan = 0x00010000,
bit_DeityAll = UINT32_MAX
};
constexpr int format_as(DeityType type) { return static_cast<int>(type); }
extern DeityTypeBit GetDeityBitmask(DeityType deity_type);
extern std::string GetDeityName(DeityType deity_type);
extern const std::map<DeityType, std::string>& GetDeityMap();
} /*deity*/
} /*EQEmu*/
#endif /* COMMON_DEITY_H */
+1
View File
@@ -4,6 +4,7 @@
#include <string>
#include "../types.h"
#include "../http/httplib.h"
#include "../repositories/player_event_logs_repository.h"
#include "../events/player_events.h"
+2 -2
View File
@@ -26,7 +26,7 @@ void DiscordManager::ProcessMessageQueue()
continue;
}
auto webhook = EQEmuLogSys::Instance()->GetDiscordWebhooks()[q.first];
auto webhook = LogSys.GetDiscordWebhooks()[q.first];
std::string message;
for (auto &m: q.second) {
@@ -68,7 +68,7 @@ void DiscordManager::ProcessMessageQueue()
void DiscordManager::QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e)
{
auto w = PlayerEventLogs::Instance()->GetDiscordWebhookUrlFromEventType(e.player_event_log.event_type_id);
auto w = player_event_logs.GetDiscordWebhookUrlFromEventType(e.player_event_log.event_type_id);
if (!w.empty()) {
Discord::SendPlayerEventMessage(e, w);
}
-6
View File
@@ -13,12 +13,6 @@ public:
void QueueWebhookMessage(uint32 webhook_id, const std::string& message);
void ProcessMessageQueue();
void QueuePlayerEventMessage(const PlayerEvent::PlayerEventContainer& e);
static DiscordManager* Instance()
{
static DiscordManager instance;
return &instance;
}
private:
std::mutex webhook_queue_lock{};
std::map<uint32, std::vector<std::string>> webhook_message_queue{};
+67 -297
View File
@@ -1,13 +1,11 @@
#include "dynamic_zone_base.h"
#include "database.h"
#include "eqemu_logsys.h"
#include "repositories/instance_list_repository.h"
#include "repositories/instance_list_player_repository.h"
#include "rulesys.h"
#include "servertalk.h"
#include "util/uuid.h"
#include "repositories/character_expedition_lockouts_repository.h"
#include "repositories/dynamic_zone_lockouts_repository.h"
#include "repositories/instance_list_repository.h"
#include "repositories/instance_list_player_repository.h"
DynamicZoneBase::DynamicZoneBase(DynamicZonesRepository::DynamicZoneInstance&& entry)
{
@@ -58,16 +56,15 @@ uint32_t DynamicZoneBase::CreateInstance()
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
insert_instance.duration = static_cast<int>(m_duration.count());
insert_instance.never_expires = m_never_expires;
insert_instance.expire_at = insert_instance.start_time + insert_instance.duration;
auto instance = InstanceListRepository::ReplaceOne(GetDatabase(), insert_instance);
if (!instance)
auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance);
if (instance.id == 0)
{
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
return 0;
}
m_instance_id = unused_instance_id;
m_instance_id = instance.id;
return m_instance_id;
}
@@ -96,15 +93,13 @@ void DynamicZoneBase::LoadRepositoryResult(DynamicZonesRepository::DynamicZoneIn
m_zonein.y = dz_entry.zone_in_y;
m_zonein.z = dz_entry.zone_in_z;
m_zonein.heading = dz_entry.zone_in_heading;
m_has_zonein = dz_entry.has_zone_in != 0;
m_is_locked = dz_entry.is_locked;
m_add_replay = dz_entry.add_replay;
m_has_zonein = (dz_entry.has_zone_in != 0);
// instance_list portion
m_zone_id = dz_entry.zone;
m_zone_version = dz_entry.version;
m_start_time = std::chrono::system_clock::from_time_t(dz_entry.start_time);
m_duration = std::chrono::seconds(dz_entry.duration);
m_never_expires = dz_entry.never_expires != 0;
m_never_expires = (dz_entry.never_expires != 0);
m_expire_time = m_start_time + m_duration;
}
@@ -124,40 +119,37 @@ void DynamicZoneBase::AddMemberFromRepositoryResult(
uint32_t DynamicZoneBase::SaveToDatabase()
{
LogDynamicZonesDetail("Saving dz instance [{}] to database", m_instance_id);
if (m_instance_id == 0)
if (m_instance_id != 0)
{
return 0;
auto insert_dz = DynamicZonesRepository::NewEntity();
insert_dz.uuid = m_uuid;
insert_dz.name = m_name;
insert_dz.leader_id = m_leader.id;
insert_dz.min_players = m_min_players;
insert_dz.max_players = m_max_players;
insert_dz.instance_id = m_instance_id,
insert_dz.type = static_cast<int>(m_type);
insert_dz.dz_switch_id = m_dz_switch_id;
insert_dz.compass_zone_id = m_compass.zone_id;
insert_dz.compass_x = m_compass.x;
insert_dz.compass_y = m_compass.y;
insert_dz.compass_z = m_compass.z;
insert_dz.safe_return_zone_id = m_safereturn.zone_id;
insert_dz.safe_return_x = m_safereturn.x;
insert_dz.safe_return_y = m_safereturn.y;
insert_dz.safe_return_z = m_safereturn.z;
insert_dz.safe_return_heading = m_safereturn.heading;
insert_dz.zone_in_x = m_zonein.x;
insert_dz.zone_in_y = m_zonein.y;
insert_dz.zone_in_z = m_zonein.z;
insert_dz.zone_in_heading = m_zonein.heading;
insert_dz.has_zone_in = m_has_zonein;
auto inserted_dz = DynamicZonesRepository::InsertOne(GetDatabase(), insert_dz);
return inserted_dz.id;
}
auto dz = DynamicZonesRepository::NewEntity();
dz.uuid = m_uuid;
dz.name = m_name;
dz.leader_id = m_leader.id;
dz.min_players = m_min_players;
dz.max_players = m_max_players;
dz.instance_id = static_cast<int32_t>(m_instance_id),
dz.type = static_cast<uint8_t>(m_type);
dz.dz_switch_id = m_dz_switch_id;
dz.compass_zone_id = m_compass.zone_id;
dz.compass_x = m_compass.x;
dz.compass_y = m_compass.y;
dz.compass_z = m_compass.z;
dz.safe_return_zone_id = m_safereturn.zone_id;
dz.safe_return_x = m_safereturn.x;
dz.safe_return_y = m_safereturn.y;
dz.safe_return_z = m_safereturn.z;
dz.safe_return_heading = m_safereturn.heading;
dz.zone_in_x = m_zonein.x;
dz.zone_in_y = m_zonein.y;
dz.zone_in_z = m_zonein.z;
dz.zone_in_heading = m_zonein.heading;
dz.has_zone_in = static_cast<uint8_t>(m_has_zonein);
dz.is_locked = static_cast<int8_t>(m_is_locked);
dz.add_replay = static_cast<int8_t>(m_add_replay);
dz = DynamicZonesRepository::InsertOne(GetDatabase(), std::move(dz));
return dz.id;
return 0;
}
bool DynamicZoneBase::AddMember(const DynamicZoneMember& add_member)
@@ -204,9 +196,10 @@ bool DynamicZoneBase::RemoveMember(const DynamicZoneMember& remove_member)
return true;
}
bool DynamicZoneBase::SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name)
bool DynamicZoneBase::SwapMember(
const DynamicZoneMember& add_member, const std::string& remove_char_name)
{
auto remove_member = GetMemberData(remove_name);
auto remove_member = GetMemberData(remove_char_name);
if (!add_member.IsValid() || !remove_member.IsValid())
{
return false;
@@ -237,18 +230,9 @@ void DynamicZoneBase::RemoveAllMembers()
void DynamicZoneBase::SaveMembers(const std::vector<DynamicZoneMember>& members)
{
if (members.empty())
{
return;
}
LogDynamicZonesDetail("Saving [{}] member(s) for dz [{}]", members.size(), m_id);
m_members = members;
if (m_members.size() > m_max_players)
{
m_members.resize(m_max_players);
}
// the lower level instance_list_players needs to be kept updated as well
std::vector<DynamicZoneMembersRepository::DynamicZoneMembers> insert_members;
@@ -258,12 +242,12 @@ void DynamicZoneBase::SaveMembers(const std::vector<DynamicZoneMember>& members)
DynamicZoneMembersRepository::DynamicZoneMembers member_entry{};
member_entry.dynamic_zone_id = m_id;
member_entry.character_id = member.id;
insert_members.push_back(member_entry);
insert_members.emplace_back(member_entry);
InstanceListPlayerRepository::InstanceListPlayer player_entry{};
player_entry.id = m_instance_id;
player_entry.charid = member.id;
insert_players.push_back(player_entry);
InstanceListPlayerRepository::InstanceListPlayer player_entry;
player_entry.id = static_cast<int>(m_instance_id);
player_entry.charid = static_cast<int>(member.id);
insert_players.emplace_back(player_entry);
}
DynamicZoneMembersRepository::InsertOrUpdateMany(GetDatabase(), insert_members);
@@ -355,44 +339,6 @@ void DynamicZoneBase::SetLeader(const DynamicZoneMember& new_leader, bool update
}
}
void DynamicZoneBase::SetLocked(bool lock, bool update_db, DzLockMsg lock_msg, uint32_t color)
{
m_is_locked = lock;
if (update_db)
{
DynamicZonesRepository::UpdateLocked(GetDatabase(), m_id, lock);
ServerPacket pack(ServerOP_DzLock, sizeof(ServerDzLock_Struct));
auto buf = reinterpret_cast<ServerDzLock_Struct*>(pack.pBuffer);
buf->dz_id = GetID();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->lock = m_is_locked;
buf->lock_msg = static_cast<uint8_t>(lock_msg);
buf->color = color;
SendServerPacket(&pack);
}
}
void DynamicZoneBase::SetReplayOnJoin(bool enabled, bool update_db)
{
m_add_replay = enabled;
if (update_db)
{
DynamicZonesRepository::UpdateReplayOnJoin(GetDatabase(), m_id, enabled);
ServerPacket pack(ServerOP_DzReplayOnJoin, sizeof(ServerDzBool_Struct));
auto buf = reinterpret_cast<ServerDzBool_Struct*>(pack.pBuffer);
buf->dz_id = GetID();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->enabled = enabled;
SendServerPacket(&pack);
}
}
uint32_t DynamicZoneBase::GetSecondsRemaining() const
{
auto remaining = std::chrono::duration_cast<std::chrono::seconds>(GetDurationRemaining()).count();
@@ -532,13 +478,13 @@ void DynamicZoneBase::RemoveInternalMember(uint32_t character_id)
), m_members.end());
}
bool DynamicZoneBase::HasMember(uint32_t character_id) const
bool DynamicZoneBase::HasMember(uint32_t character_id)
{
return std::any_of(m_members.begin(), m_members.end(),
[&](const DynamicZoneMember& member) { return member.id == character_id; });
}
bool DynamicZoneBase::HasMember(const std::string& character_name) const
bool DynamicZoneBase::HasMember(const std::string& character_name)
{
return std::any_of(m_members.begin(), m_members.end(),
[&](const DynamicZoneMember& member) {
@@ -644,34 +590,35 @@ std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type)
}
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerPacket(uint16_t zone_id, uint16_t instance_id)
EQ::Net::DynamicPacket DynamicZoneBase::GetSerializedDzPacket()
{
std::ostringstream ss = GetSerialized();
std::string_view sv = ss.view();
EQ::Net::DynamicPacket dyn_pack;
dyn_pack.PutSerialize(0, *this);
auto pack_size = sizeof(ServerDzCreate_Struct) + sv.size();
LogDynamicZonesDetail("Serialized server dz size [{}]", dyn_pack.Length());
return dyn_pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateServerDzCreatePacket(
uint16_t origin_zone_id, uint16_t origin_instance_id)
{
EQ::Net::DynamicPacket dyn_pack = GetSerializedDzPacket();
auto pack_size = sizeof(ServerDzCreateSerialized_Struct) + dyn_pack.Length();
auto pack = std::make_unique<ServerPacket>(ServerOP_DzCreated, static_cast<uint32_t>(pack_size));
auto buf = reinterpret_cast<ServerDzCreate_Struct*>(pack->pBuffer);
buf->origin_zone_id = zone_id;
buf->origin_instance_id = instance_id;
buf->dz_id = GetID();
buf->cereal_size = static_cast<uint32_t>(sv.size());
memcpy(buf->cereal_data, sv.data(), sv.size());
auto buf = reinterpret_cast<ServerDzCreateSerialized_Struct*>(pack->pBuffer);
buf->origin_zone_id = origin_zone_id;
buf->origin_instance_id = origin_instance_id;
buf->cereal_size = static_cast<uint32_t>(dyn_pack.Length());
memcpy(buf->cereal_data, dyn_pack.Data(), dyn_pack.Length());
return pack;
}
std::ostringstream DynamicZoneBase::GetSerialized()
void DynamicZoneBase::LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size)
{
std::ostringstream ss;
cereal::BinaryOutputArchive archive(ss);
archive(*this);
return ss;
}
void DynamicZoneBase::Unserialize(std::span<char> buf)
{
EQ::Util::MemoryStreamReader ss(buf.data(), buf.size());
LogDynamicZonesDetail("Deserializing server dz size [{}]", cereal_size);
EQ::Util::MemoryStreamReader ss(cereal_data, cereal_size);
cereal::BinaryInputArchive archive(ss);
archive(*this);
}
@@ -700,180 +647,3 @@ void DynamicZoneBase::LoadTemplate(const DynamicZoneTemplatesRepository::Dynamic
m_zonein.z = dz_template.zone_in_z;
m_zonein.heading = dz_template.zone_in_h;
}
std::vector<uint32_t> DynamicZoneBase::GetMemberIds()
{
std::vector<uint32_t> ids;
ids.reserve(m_members.size());
for (const auto& member : m_members)
{
ids.push_back(member.id);
}
return ids;
}
bool DynamicZoneBase::HasLockout(const std::string& event)
{
return std::ranges::any_of(m_lockouts, [&](const auto& l) { return l.IsEvent(event); });
}
bool DynamicZoneBase::HasReplayLockout()
{
return HasLockout(DzLockout::ReplayTimer);
}
void DynamicZoneBase::AddLockout(const std::string& event, uint32_t seconds)
{
auto lockout = DzLockout::Create(m_name, event, seconds, m_uuid);
AddLockout(lockout);
}
void DynamicZoneBase::AddLockout(const DzLockout& lockout, bool members_only)
{
if (!members_only)
{
DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { lockout });
}
CharacterExpeditionLockoutsRepository::InsertLockout(GetDatabase(), GetMemberIds(), lockout);
HandleLockoutUpdate(lockout, false, members_only);
SendServerPacket(CreateLockoutPacket(lockout, false, members_only).get());
}
void DynamicZoneBase::AddLockoutDuration(const std::string& event, int seconds, bool members_only)
{
auto lockout = DzLockout::Create(m_name, event, std::max(0, seconds), m_uuid);
// lockout has unsigned duration, pass original seconds to support reducing existing timers
int secs = static_cast<int>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
CharacterExpeditionLockoutsRepository::AddLockoutDuration(GetDatabase(), GetMemberIds(), lockout, secs);
HandleLockoutDuration(lockout, seconds, members_only, true);
SendServerPacket(CreateLockoutDurationPacket(lockout, seconds, members_only).get());
}
void DynamicZoneBase::UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only)
{
// some live expeditions update existing lockout timers during progression
auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(event); });
if (it != m_lockouts.end())
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
DzLockout lockout(m_uuid, m_name, event, it->GetStartTime() + seconds, seconds);
AddLockout(lockout, members_only);
}
}
void DynamicZoneBase::RemoveLockout(const std::string& event)
{
DynamicZoneLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format(
"dynamic_zone_id = {} AND event_name = '{}'", GetID(), Strings::Escape(event)));
CharacterExpeditionLockoutsRepository::DeleteWhere(GetDatabase(), fmt::format(
"character_id IN ({}) AND expedition_name = '{}' AND event_name = '{}'",
fmt::join(GetMemberIds(), ","), Strings::Escape(m_name), Strings::Escape(event)));
DzLockout lockout{m_uuid, m_name, event, 0, 0};
HandleLockoutUpdate(lockout, true, false);
SendServerPacket(CreateLockoutPacket(lockout, true).get());
}
void DynamicZoneBase::HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only)
{
if (!members_only)
{
std::erase_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); });
if (!remove)
{
m_lockouts.push_back(lockout);
}
}
}
void DynamicZoneBase::HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db)
{
if (!members_only)
{
auto it = std::ranges::find_if(m_lockouts, [&](const auto& l) { return l.IsEvent(lockout.Event()); });
if (it != m_lockouts.end())
{
it->AddLockoutTime(seconds);
}
else
{
it = m_lockouts.insert(m_lockouts.end(), lockout);
}
if (insert_db)
{
DynamicZoneLockoutsRepository::InsertLockouts(GetDatabase(), GetID(), { *it });
}
}
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only) const
{
uint32_t pack_size = sizeof(ServerDzLockout_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzLockout, pack_size);
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
buf->dz_id = GetID();
buf->expire_time = lockout.GetExpireTime();
buf->duration = lockout.GetDuration();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->remove = remove;
buf->members_only = members_only;
strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name));
return pack;
}
std::unique_ptr<ServerPacket> DynamicZoneBase::CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only) const
{
uint32_t pack_size = sizeof(ServerDzLockout_Struct);
auto pack = std::make_unique<ServerPacket>(ServerOP_DzLockoutDuration, pack_size);
auto buf = reinterpret_cast<ServerDzLockout_Struct*>(pack->pBuffer);
buf->dz_id = GetID();
buf->expire_time = lockout.GetExpireTime();
buf->duration = lockout.GetDuration();
buf->sender_zone_id = GetCurrentZoneID();
buf->sender_instance_id = GetCurrentInstanceID();
buf->members_only = members_only;
buf->seconds = seconds;
strn0cpy(buf->event_name, lockout.Event().c_str(), sizeof(buf->event_name));
return pack;
}
void DynamicZoneBase::SyncCharacterLockouts(uint32_t char_id, std::vector<DzLockout>& lockouts)
{
// adds missing event lockouts to client for this expedition and updates
// client timers that are both shorter and from another expedition
bool modified = false;
for (const auto& lockout : m_lockouts)
{
if (lockout.IsReplay() || lockout.IsExpired() || lockout.UUID() != m_uuid)
{
continue;
}
auto it = std::find_if(lockouts.begin(), lockouts.end(), [&](const DzLockout& l) { return l.IsSame(lockout); });
if (it == lockouts.end())
{
modified = true;
lockouts.push_back(lockout); // insert missing
}
else if (it->GetSecondsRemaining() < lockout.GetSecondsRemaining() && it->UUID() != m_uuid)
{
// only update lockout timer not uuid so loot event apis still work
modified = true;
it->SetDuration(lockout.GetDuration());
it->SetExpireTime(lockout.GetExpireTime());
}
}
if (modified)
{
CharacterExpeditionLockoutsRepository::InsertLockouts(GetDatabase(), char_id, lockouts);
}
}
+14 -68
View File
@@ -1,8 +1,8 @@
#ifndef COMMON_DYNAMIC_ZONE_BASE_H
#define COMMON_DYNAMIC_ZONE_BASE_H
#include "dynamic_zone_lockout.h"
#include "eq_constants.h"
#include "net/packet.h"
#include "repositories/dynamic_zones_repository.h"
#include "repositories/dynamic_zone_members_repository.h"
#include "repositories/dynamic_zone_templates_repository.h"
@@ -10,40 +10,12 @@
#include <chrono>
#include <cstdint>
#include <memory>
#include <span>
#include <string>
#include <vector>
class Database;
class ServerPacket;
// message string 8312 added in September 08 2020 Test patch (used by both dz and shared tasks)
inline constexpr char DzNotAllAdded[] = "Not all players in your {0} were added to the {1}. The {1} can take a maximum of {2} players, and your {0} has {3}.";
enum class DzLockMsg : uint8_t
{
None = 0, Close, Begin
};
enum class DynamicZoneType
{
None = 0,
Expedition,
Tutorial,
Task,
Mission, // Shared Task
Quest
};
enum class DynamicZoneMemberStatus
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
struct DynamicZoneMember
{
uint32_t id = 0;
@@ -121,7 +93,6 @@ public:
const std::string& GetName() const { return m_name; }
const std::string& GetUUID() const { return m_uuid; }
const DynamicZoneMember& GetLeader() const { return m_leader; }
const std::vector<DzLockout>& GetLockouts() const { return m_lockouts; }
const std::vector<DynamicZoneMember>& GetMembers() const { return m_members; }
const DynamicZoneLocation& GetCompassLocation() const { return m_compass; }
const DynamicZoneLocation& GetSafeReturnLocation() const { return m_safereturn; }
@@ -133,34 +104,31 @@ public:
uint32_t GetDatabaseMemberCount();
DynamicZoneMember GetMemberData(uint32_t character_id);
DynamicZoneMember GetMemberData(const std::string& character_name);
std::vector<uint32_t> GetMemberIds();
std::ostringstream GetSerialized();
EQ::Net::DynamicPacket GetSerializedDzPacket();
bool HasDatabaseMember(uint32_t character_id);
bool HasMember(uint32_t character_id) const;
bool HasMember(const std::string& character_name) const;
bool HasMember(uint32_t character_id);
bool HasMember(const std::string& character_name);
bool HasMembers() const { return !m_members.empty(); }
bool HasZoneInLocation() const { return m_has_zonein; }
bool IsExpedition() const { return m_type == DynamicZoneType::Expedition; }
bool IsExpired() const { return m_expire_time < std::chrono::system_clock::now(); }
bool IsInstanceID(uint32_t instance_id) const { return (m_instance_id != 0 && m_instance_id == instance_id); }
bool IsLocked() const { return m_is_locked; }
bool IsValid() const { return m_instance_id != 0; }
bool IsSameDz(uint32_t zone_id, uint32_t instance_id) const { return zone_id == m_zone_id && instance_id == m_instance_id; }
void LoadSerializedDzPacket(char* cereal_data, uint32_t cereal_size);
void LoadTemplate(const DynamicZoneTemplatesRepository::DynamicZoneTemplates& dz_template);
void RemoveAllMembers();
bool RemoveMember(uint32_t character_id);
bool RemoveMember(const std::string& character_name);
bool RemoveMember(const DynamicZoneMember& remove_member);
void SaveMembers(const std::vector<DynamicZoneMember>& members);
void SetCompass(const DynamicZoneLocation& location, bool update_db = false);
void SetCompass(uint32_t zone_id, float x, float y, float z, bool update_db = false);
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetLeader(const DynamicZoneMember& leader, bool update_db = false);
void SetLocked(bool lock, bool update_db = false, DzLockMsg lock_msg = DzLockMsg::None, uint32_t color = Chat::Yellow);
void SetMaxPlayers(uint32_t max_players) { m_max_players = max_players; }
void SetMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);
void SetMinPlayers(uint32_t min_players) { m_min_players = min_players; }
void SetName(const std::string& name) { m_name = name; }
void SetReplayOnJoin(bool enabled, bool update_db = false);
void SetSafeReturn(const DynamicZoneLocation& location, bool update_db = false);
void SetSafeReturn(uint32_t zone_id, float x, float y, float z, float heading, bool update_db = false);
void SetSwitchID(int dz_switch_id, bool update_db = false);
@@ -168,48 +136,34 @@ public:
void SetUUID(std::string uuid) { m_uuid = std::move(uuid); }
void SetZoneInLocation(const DynamicZoneLocation& location, bool update_db = false);
void SetZoneInLocation(float x, float y, float z, float heading, bool update_db = false);
bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_name);
void AddLockout(const std::string& event, uint32_t seconds);
void AddLockoutDuration(const std::string& event, int seconds, bool members_only = true);
bool HasLockout(const std::string& event);
bool HasReplayLockout();
void RemoveLockout(const std::string& event);
void SyncCharacterLockouts(uint32_t char_id, std::vector<DzLockout>& lockouts);
void UpdateLockoutDuration(const std::string& event, uint32_t seconds, bool members_only = true);
bool SwapMember(const DynamicZoneMember& add_member, const std::string& remove_char_name);
protected:
virtual uint16_t GetCurrentInstanceID() const { return 0; }
virtual uint16_t GetCurrentZoneID() const { return 0; }
virtual uint16_t GetCurrentInstanceID() { return 0; }
virtual uint16_t GetCurrentZoneID() { return 0; }
virtual Database& GetDatabase() = 0;
virtual void HandleLockoutDuration(const DzLockout& lockout, int seconds, bool members_only, bool insert_db);
virtual void HandleLockoutUpdate(const DzLockout& lockout, bool remove, bool members_only);
virtual void ProcessCompassChange(const DynamicZoneLocation& location) { m_compass = location; }
virtual void ProcessMemberAddRemove(const DynamicZoneMember& member, bool removed);
virtual bool ProcessMemberStatusChange(uint32_t character_id, DynamicZoneMemberStatus status);
virtual void ProcessRemoveAllMembers() { m_members.clear(); }
virtual bool ProcessMemberStatusChange(uint32_t member_id, DynamicZoneMemberStatus status);
virtual void ProcessRemoveAllMembers(bool silent = false) { m_members.clear(); }
virtual void ProcessSetSwitchID(int dz_switch_id) { m_dz_switch_id = dz_switch_id; }
virtual bool SendServerPacket(ServerPacket* packet) = 0;
void AddLockout(const DzLockout& lockout, bool members_only = false);
void AddInternalMember(const DynamicZoneMember& member);
uint32_t Create();
uint32_t CreateInstance();
void LoadRepositoryResult(DynamicZonesRepository::DynamicZoneInstance&& dz_entry);
void RemoveInternalMember(uint32_t character_id);
void SaveMembers(const std::vector<DynamicZoneMember>& members);
uint32_t SaveToDatabase();
bool SetInternalMemberStatus(uint32_t character_id, DynamicZoneMemberStatus status);
std::unique_ptr<ServerPacket> CreateServerPacket(uint16_t zone_id, uint16_t instance_id);
std::unique_ptr<ServerPacket> CreateServerDzCreatePacket(uint16_t origin_zone_id, uint16_t origin_instance_id);
std::unique_ptr<ServerPacket> CreateServerDzLocationPacket(uint16_t server_opcode, const DynamicZoneLocation& location);
std::unique_ptr<ServerPacket> CreateServerDzSwitchIDPacket();
std::unique_ptr<ServerPacket> CreateServerMemberAddRemovePacket(const DynamicZoneMember& member, bool removed);
std::unique_ptr<ServerPacket> CreateServerMemberStatusPacket(uint32_t character_id, DynamicZoneMemberStatus status);
std::unique_ptr<ServerPacket> CreateServerMemberSwapPacket(const DynamicZoneMember& remove_member, const DynamicZoneMember& add_member);
std::unique_ptr<ServerPacket> CreateServerRemoveAllMembersPacket();
std::unique_ptr<ServerPacket> CreateLockoutPacket(const DzLockout& lockout, bool remove, bool members_only = false) const;
std::unique_ptr<ServerPacket> CreateLockoutDurationPacket(const DzLockout& lockout, int seconds, bool members_only = false) const;
uint32_t m_id = 0;
uint32_t m_zone_id = 0;
@@ -221,8 +175,6 @@ protected:
bool m_never_expires = false;
bool m_has_zonein = false;
bool m_has_member_statuses = false;
bool m_is_locked = false;
bool m_add_replay = true;
std::string m_name;
std::string m_uuid;
DynamicZoneMember m_leader;
@@ -230,15 +182,12 @@ protected:
DynamicZoneLocation m_compass;
DynamicZoneLocation m_safereturn;
DynamicZoneLocation m_zonein;
std::chrono::seconds m_duration = {};
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_start_time;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
std::vector<DynamicZoneMember> m_members;
std::vector<DzLockout> m_lockouts;
public:
void Unserialize(std::span<char> buf);
template<class Archive>
void serialize(Archive& archive)
{
@@ -253,8 +202,6 @@ public:
m_never_expires,
m_has_zonein,
m_has_member_statuses,
m_is_locked,
m_add_replay,
m_name,
m_uuid,
m_leader,
@@ -265,8 +212,7 @@ public:
m_duration,
m_start_time,
m_expire_time,
m_members,
m_lockouts
m_members
);
}
};
-92
View File
@@ -1,92 +0,0 @@
#include "dynamic_zone_lockout.h"
#include "strings.h"
#include "rulesys.h"
#include "util/uuid.h"
#include <fmt/format.h>
#include <cereal/types/chrono.hpp>
DzLockout::DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration)
: m_uuid(std::move(uuid))
, m_name(std::move(expedition))
, m_event(std::move(event))
, m_expire_time(std::chrono::system_clock::from_time_t(expire_time))
, m_duration(duration)
{
m_is_replay = m_event == ReplayTimer;
}
DzLockout::DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout)
: m_uuid(std::move(lockout.from_expedition_uuid))
, m_name(name)
, m_event(std::move(lockout.event_name))
, m_expire_time(std::chrono::system_clock::from_time_t(lockout.expire_time))
, m_duration(lockout.duration)
{
m_is_replay = m_event == ReplayTimer;
}
DzLockout DzLockout::Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
if (uuid.empty())
{
uuid = EQ::Util::UUID::Generate().ToString();
}
DzLockout lockout{uuid, expedition, event, 0, seconds};
lockout.Reset(); // sets expire time
return lockout;
}
uint32_t DzLockout::GetSecondsRemaining() const
{
auto now = std::chrono::system_clock::now();
if (m_expire_time > now)
{
auto remaining = m_expire_time - now;
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(remaining).count());
}
return 0;
}
DzLockout::TimeStrings DzLockout::GetTimeRemainingStrs() const
{
auto seconds = GetSecondsRemaining();
return DzLockout::TimeStrings{
fmt::format_int(seconds / 86400).str(), // days
fmt::format_int(seconds / 3600 % 24).str(), // hours
fmt::format_int(seconds / 60 % 60).str(), // minutes
fmt::format_int(seconds % 60).str() // seconds
};
}
bool DzLockout::IsSame(const DzLockout& other) const
{
return other.IsSame(m_name, m_event);
}
bool DzLockout::IsSame(const std::string& expedition, const std::string& event) const
{
return m_name == expedition && m_event == event;
}
void DzLockout::AddLockoutTime(int seconds)
{
seconds = static_cast<int>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
auto new_duration = std::max(0, static_cast<int>(m_duration.count()) + seconds);
auto start_time = m_expire_time - m_duration;
m_duration = std::chrono::seconds(new_duration);
m_expire_time = start_time + m_duration;
}
template <typename T>
void DzLockout::serialize(T& archive)
{
archive(m_is_replay, m_uuid, m_name, m_event, m_duration, m_expire_time);
}
template void DzLockout::serialize(cereal::BinaryOutputArchive&);
template void DzLockout::serialize(cereal::BinaryInputArchive&);
-56
View File
@@ -1,56 +0,0 @@
#pragma once
#include <chrono>
#include <string>
#include "repositories/base/base_dynamic_zone_lockouts_repository.h"
class DzLockout
{
public:
DzLockout() = default;
DzLockout(std::string uuid, std::string expedition, std::string event, uint64_t expire_time, uint32_t duration);
DzLockout(std::string_view name, BaseDynamicZoneLockoutsRepository::DynamicZoneLockouts&& lockout);
static constexpr char ReplayTimer[] = "Replay Timer";
static DzLockout Create(const std::string& expedition, const std::string& event, uint32_t seconds, std::string uuid = {});
struct TimeStrings
{
std::string days;
std::string hours;
std::string mins;
std::string secs;
};
void AddLockoutTime(int seconds);
uint32_t GetDuration() const { return static_cast<uint32_t>(m_duration.count()); }
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); }
uint32_t GetSecondsRemaining() const;
TimeStrings GetTimeRemainingStrs() const;
const std::string& DzName() const { return m_name; }
const std::string& Event() const { return m_event; }
const std::string& UUID() const { return m_uuid; }
bool IsEvent(std::string_view event) const { return m_event == event; }
bool IsExpired() const { return GetSecondsRemaining() == 0; }
bool IsReplay() const { return m_is_replay; }
bool IsSame(const DzLockout& other) const;
bool IsSame(const std::string& expedition, const std::string& event) const;
bool IsUUID(const std::string& uuid) const { return uuid == m_uuid; }
void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; }
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); }
void SetUUID(const std::string& uuid) { m_uuid = uuid; }
template <typename T>
void serialize(T& archive);
private:
bool m_is_replay = false;
std::string m_uuid; // dz received in
std::string m_name;
std::string m_event;
std::chrono::seconds m_duration = {};
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
+348 -107
View File
@@ -59,40 +59,103 @@ int16 EQ::invtype::GetInvTypeSize(int16 inv_type) {
return local_array[inv_type];
}
uint32 Bug::GetID(const std::string& category_name)
{
for (const auto& e : bug_category_names) {
if (e.second == category_name) {
return e.first;
const char* EQ::bug::CategoryIDToCategoryName(CategoryID category_id) {
switch (category_id) {
case catVideo:
return "Video";
case catAudio:
return "Audio";
case catPathing:
return "Pathing";
case catQuest:
return "Quest";
case catTradeskills:
return "Tradeskills";
case catSpellStacking:
return "Spell stacking";
case catDoorsPortals:
return "Doors/Portals";
case catItems:
return "Items";
case catNPC:
return "NPC";
case catDialogs:
return "Dialogs";
case catLoNTCG:
return "LoN - TCG";
case catMercenaries:
return "Mercenaries";
case catOther:
default:
return "Other";
}
}
return Bug::Category::Other;
EQ::bug::CategoryID EQ::bug::CategoryNameToCategoryID(const char* category_name) {
if (!category_name)
return catOther;
if (!strcmp(category_name, "Video"))
return catVideo;
if (!strcmp(category_name, "Audio"))
return catAudio;
if (!strcmp(category_name, "Pathing"))
return catPathing;
if (!strcmp(category_name, "Quest"))
return catQuest;
if (!strcmp(category_name, "Tradeskills"))
return catTradeskills;
if (!strcmp(category_name, "Spell stacking"))
return catSpellStacking;
if (!strcmp(category_name, "Doors/Portals"))
return catDoorsPortals;
if (!strcmp(category_name, "Items"))
return catItems;
if (!strcmp(category_name, "NPC"))
return catNPC;
if (!strcmp(category_name, "Dialogs"))
return catDialogs;
if (!strcmp(category_name, "LoN - TCG"))
return catLoNTCG;
if (!strcmp(category_name, "Mercenaries"))
return catMercenaries;
return catOther;
}
std::string Bug::GetName(uint32 category_id)
{
return IsValid(category_id) ? bug_category_names[category_id] : "UNKNOWN BUG CATEGORY";
const char *EQ::constants::GetStanceName(StanceType stance_type) {
switch (stance_type) {
case stanceUnknown:
return "Unknown";
case stancePassive:
return "Passive";
case stanceBalanced:
return "Balanced";
case stanceEfficient:
return "Efficient";
case stanceReactive:
return "Reactive";
case stanceAggressive:
return "Aggressive";
case stanceAssist:
return "Assist";
case stanceBurn:
return "Burn";
case stanceEfficient2:
return "Efficient2";
case stanceBurnAE:
return "BurnAE";
default:
return "Invalid";
}
}
bool Bug::IsValid(uint32 category_id)
{
return bug_category_names.find(category_id) != bug_category_names.end();
int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
if (EQ::ValueWithin(stance_type, EQ::constants::stancePassive, EQ::constants::stanceBurnAE)) {
return (stance_type - EQ::constants::stancePassive);
}
std::string Stance::GetName(uint8 stance_id)
{
return IsValid(stance_id) ? stance_names[stance_id] : "UNKNOWN STANCE";
}
bool Stance::IsValid(uint8 stance_id)
{
return stance_names.find(stance_id) != stance_names.end();
}
uint8 Stance::GetIndex(uint8 stance_id)
{
return IsValid(stance_id) ? (stance_id - Stance::Passive) : 0;
return 0;
}
const std::map<uint8, std::string>& EQ::constants::GetLanguageMap()
@@ -140,6 +203,29 @@ std::string EQ::constants::GetLanguageName(uint8 language_id)
return EQ::constants::GetLanguageMap().find(language_id)->second;
}
const std::map<uint32, std::string>& EQ::constants::GetLDoNThemeMap()
{
static const std::map<uint32, std::string> ldon_theme_map = {
{ LDoNThemes::Unused, "Unused" },
{ LDoNThemes::GUK, "Deepest Guk" },
{ LDoNThemes::MIR, "Miragul's Menagerie" },
{ LDoNThemes::MMC, "Mistmoore Catacombs" },
{ LDoNThemes::RUJ, "Rujarkian Hills" },
{ LDoNThemes::TAK, "Takish-Hiz" },
};
return ldon_theme_map;
}
std::string EQ::constants::GetLDoNThemeName(uint32 theme_id)
{
if (!EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) {
return std::string();
}
return EQ::constants::GetLDoNThemeMap().find(theme_id)->second;
}
const std::map<int8, std::string>& EQ::constants::GetFlyModeMap()
{
static const std::map<int8, std::string> flymode_map = {
@@ -163,6 +249,102 @@ std::string EQ::constants::GetFlyModeName(int8 flymode_id)
return EQ::constants::GetFlyModeMap().find(flymode_id)->second;
}
const std::map<bodyType, std::string>& EQ::constants::GetBodyTypeMap()
{
static const std::map<bodyType, std::string> bodytype_map = {
{ BT_Humanoid, "Humanoid" },
{ BT_Lycanthrope, "Lycanthrope" },
{ BT_Undead, "Undead" },
{ BT_Giant, "Giant" },
{ BT_Construct, "Construct" },
{ BT_Extraplanar, "Extraplanar" },
{ BT_Magical, "Magical" },
{ BT_SummonedUndead, "Summoned Undead" },
{ BT_RaidGiant, "Raid Giant" },
{ BT_RaidColdain, "Raid Coldain" },
{ BT_NoTarget, "Untargetable" },
{ BT_Vampire, "Vampire" },
{ BT_Atenha_Ra, "Aten Ha Ra" },
{ BT_Greater_Akheva, "Greater Akheva" },
{ BT_Khati_Sha, "Khati Sha" },
{ BT_Seru, "Seru" },
{ BT_Grieg_Veneficus, "Grieg Veneficus" },
{ BT_Draz_Nurakk, "Draz Nurakk" },
{ BT_Zek, "Zek" },
{ BT_Luggald, "Luggald" },
{ BT_Animal, "Animal" },
{ BT_Insect, "Insect" },
{ BT_Monster, "Monster" },
{ BT_Summoned, "Summoned" },
{ BT_Plant, "Plant" },
{ BT_Dragon, "Dragon" },
{ BT_Summoned2, "Summoned 2" },
{ BT_Summoned3, "Summoned 3" },
{ BT_Dragon2, "Dragon 2" },
{ BT_VeliousDragon, "Velious Dragon" },
{ BT_Familiar, "Familiar" },
{ BT_Dragon3, "Dragon 3" },
{ BT_Boxes, "Boxes" },
{ BT_Muramite, "Muramite" },
{ BT_NoTarget2, "Untargetable 2" },
{ BT_SwarmPet, "Swarm Pet" },
{ BT_MonsterSummon, "Monster Summon" },
{ BT_InvisMan, "Invisible Man" },
{ BT_Special, "Special" },
};
return bodytype_map;
}
std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id)
{
if (EQ::constants::GetBodyTypeMap().find(bodytype_id) != EQ::constants::GetBodyTypeMap().end()) {
return EQ::constants::GetBodyTypeMap().find(bodytype_id)->second;
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetAccountStatusMap()
{
static const std::map<uint8, std::string> account_status_map = {
{ AccountStatus::Player, "Player" },
{ AccountStatus::Steward, "Steward" },
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
{ AccountStatus::Guide, "Guide" },
{ AccountStatus::QuestTroupe, "Quest Troupe" },
{ AccountStatus::SeniorGuide, "Senior Guide" },
{ AccountStatus::GMTester, "GM Tester" },
{ AccountStatus::EQSupport, "EQ Support" },
{ AccountStatus::GMStaff, "GM Staff" },
{ AccountStatus::GMAdmin, "GM Admin" },
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
{ AccountStatus::QuestMaster, "Quest Master" },
{ AccountStatus::GMAreas, "GM Areas" },
{ AccountStatus::GMCoder, "GM Coder" },
{ AccountStatus::GMMgmt, "GM Mgmt" },
{ AccountStatus::GMImpossible, "GM Impossible" },
{ AccountStatus::Max, "GM Max" }
};
return account_status_map;
}
std::string EQ::constants::GetAccountStatusName(uint8 account_status)
{
for (
auto status_level = EQ::constants::GetAccountStatusMap().rbegin();
status_level != EQ::constants::GetAccountStatusMap().rend();
++status_level
) {
if (account_status >= status_level->first) {
return status_level->second;
}
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
{
static const std::map<uint8, std::string> consider_level_map = {
@@ -253,6 +435,84 @@ std::string EQ::constants::GetSpawnAnimationName(uint8 animation_id)
return EQ::constants::GetSpawnAnimationMap().find(animation_id)->second;
}
const std::map<int, std::string>& EQ::constants::GetObjectTypeMap()
{
static const std::map<int, std::string> object_type_map = {
{ ObjectTypes::SmallBag, "Small Bag" },
{ ObjectTypes::LargeBag, "Large Bag" },
{ ObjectTypes::Quiver, "Quiver" },
{ ObjectTypes::BeltPouch, "Belt Pouch" },
{ ObjectTypes::WristPouch, "Wrist Pouch" },
{ ObjectTypes::Backpack, "Backpack" },
{ ObjectTypes::SmallChest, "Small Chest" },
{ ObjectTypes::LargeChest, "Large Chest" },
{ ObjectTypes::Bandolier, "Bandolier" },
{ ObjectTypes::Medicine, "Medicine" },
{ ObjectTypes::Tinkering, "Tinkering" },
{ ObjectTypes::Lexicon, "Lexicon" },
{ ObjectTypes::PoisonMaking, "Mortar and Pestle" },
{ ObjectTypes::Quest, "Quest" },
{ ObjectTypes::MixingBowl, "Mixing Bowl" },
{ ObjectTypes::Baking, "Baking" },
{ ObjectTypes::Tailoring, "Tailoring" },
{ ObjectTypes::Blacksmithing, "Blacksmithing" },
{ ObjectTypes::Fletching, "Fletching" },
{ ObjectTypes::Brewing, "Brewing" },
{ ObjectTypes::JewelryMaking, "Jewelry Making" },
{ ObjectTypes::Pottery, "Pottery" },
{ ObjectTypes::Kiln, "Kiln" },
{ ObjectTypes::KeyMaker, "Key Maker" },
{ ObjectTypes::ResearchWIZ, "Lexicon" },
{ ObjectTypes::ResearchMAG, "Lexicon" },
{ ObjectTypes::ResearchNEC, "Lexicon" },
{ ObjectTypes::ResearchENC, "Lexicon" },
{ ObjectTypes::Unknown, "Unknown" },
{ ObjectTypes::ResearchPractice, "Lexicon" },
{ ObjectTypes::Alchemy, "Alchemy" },
{ ObjectTypes::HighElfForge, "High Elf Forge" },
{ ObjectTypes::DarkElfForge, "Dark Elf Forge" },
{ ObjectTypes::OgreForge, "Ogre Forge" },
{ ObjectTypes::DwarfForge, "Dwarf Forge" },
{ ObjectTypes::GnomeForge, "Gnome Forge" },
{ ObjectTypes::BarbarianForge, "Barbarian Forge" },
{ ObjectTypes::IksarForge, "Iksar Forge" },
{ ObjectTypes::HumanForgeOne, "Human Forge" },
{ ObjectTypes::HumanForgeTwo, "Human Forge" },
{ ObjectTypes::HalflingTailoringOne, "Halfling Tailoring" },
{ ObjectTypes::HalflingTailoringTwo, "Halfling Tailoring" },
{ ObjectTypes::EruditeTailoring, "Erudite Tailoring" },
{ ObjectTypes::WoodElfTailoring, "Wood Elf Tailoring" },
{ ObjectTypes::WoodElfFletching, "Wood Elf Fletching" },
{ ObjectTypes::IksarPottery, "Iksar Pottery" },
{ ObjectTypes::Fishing, "Fishing" },
{ ObjectTypes::TrollForge, "Troll Forge" },
{ ObjectTypes::WoodElfForge, "Wood Elf Forge" },
{ ObjectTypes::HalflingForge, "Halfling Forge" },
{ ObjectTypes::EruditeForge, "Erudite Forge" },
{ ObjectTypes::Merchant, "Merchant" },
{ ObjectTypes::FroglokForge, "Froglok Forge" },
{ ObjectTypes::Augmenter, "Augmenter" },
{ ObjectTypes::Churn, "Churn" },
{ ObjectTypes::TransformationMold, "Transformation Mold" },
{ ObjectTypes::DetransformationMold, "Detransformation Mold" },
{ ObjectTypes::Unattuner, "Unattuner" },
{ ObjectTypes::TradeskillBag, "Tradeskill Bag" },
{ ObjectTypes::CollectibleBag, "Collectible Bag" },
{ ObjectTypes::NoDeposit, "No Deposit" }
};
return object_type_map;
}
std::string EQ::constants::GetObjectTypeName(int object_type)
{
if (!EQ::ValueWithin(object_type, ObjectTypes::SmallBag, ObjectTypes::NoDeposit)) {
return std::string();
}
return EQ::constants::GetObjectTypeMap().find(object_type)->second;
}
const std::map<uint8, std::string> &EQ::constants::GetWeatherTypeMap()
{
static const std::map<uint8, std::string> weather_type_map = {
@@ -380,94 +640,75 @@ std::string EQ::constants::GetAppearanceTypeName(uint32 appearance_type)
return std::string();
}
std::string SpecialAbility::GetName(int ability_id)
const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
{
return IsValid(ability_id) ? special_ability_names[ability_id] : "UNKNOWN SPECIAL ABILITY";
}
bool SpecialAbility::IsValid(int ability_id)
{
return special_ability_names.find(ability_id) != special_ability_names.end();
}
const std::map<uint32, std::string>& EQ::constants::GetConsiderColorMap()
{
static const std::map<uint32, std::string> consider_color_map = {
{ ConsiderColor::Green, "Green" },
{ ConsiderColor::DarkBlue, "Dark Blue" },
{ ConsiderColor::Gray, "Gray" },
{ ConsiderColor::White, "White" },
{ ConsiderColor::Red, "Red" },
{ ConsiderColor::Yellow, "Yellow" },
{ ConsiderColor::LightBlue, "Light Blue" },
{ ConsiderColor::WhiteTitanium, "White" },
static const std::map<uint32, std::string> special_ability_map = {
{ SPECATK_SUMMON, "Summon" },
{ SPECATK_ENRAGE, "Enrage" },
{ SPECATK_RAMPAGE, "Rampage" },
{ SPECATK_AREA_RAMPAGE, "Area Rampage" },
{ SPECATK_FLURRY, "Flurry" },
{ SPECATK_TRIPLE, "Triple Attack" },
{ SPECATK_QUAD, "Quadruple Attack" },
{ SPECATK_INNATE_DW, "Dual Wield" },
{ SPECATK_BANE, "Bane Attack" },
{ SPECATK_MAGICAL, "Magical Attack" },
{ SPECATK_RANGED_ATK, "Ranged Attack" },
{ UNSLOWABLE, "Immune to Slow" },
{ UNMEZABLE, "Immune to Mesmerize" },
{ UNCHARMABLE, "Immune to Charm" },
{ UNSTUNABLE, "Immune to Stun" },
{ UNSNAREABLE, "Immune to Snare" },
{ UNFEARABLE, "Immune to Fear" },
{ UNDISPELLABLE, "Immune to Dispell" },
{ IMMUNE_MELEE, "Immune to Melee" },
{ IMMUNE_MAGIC, "Immune to Magic" },
{ IMMUNE_FLEEING, "Immune to Fleeing" },
{ IMMUNE_MELEE_EXCEPT_BANE, "Immune to Melee except Bane" },
{ IMMUNE_MELEE_NONMAGICAL, "Immune to Non-Magical Melee" },
{ IMMUNE_AGGRO, "Immune to Aggro" },
{ IMMUNE_AGGRO_ON, "Immune to Being Aggro" },
{ IMMUNE_CASTING_FROM_RANGE, "Immune to Ranged Spells" },
{ IMMUNE_FEIGN_DEATH, "Immune to Feign Death" },
{ IMMUNE_TAUNT, "Immune to Taunt" },
{ NPC_TUNNELVISION, "Tunnel Vision" },
{ NPC_NO_BUFFHEAL_FRIENDS, "Does Not Heal of Buff Allies" },
{ IMMUNE_PACIFY, "Immune to Pacify" },
{ LEASH, "Leashed" },
{ TETHER, "Tethered" },
{ DESTRUCTIBLE_OBJECT, "Destructible Object" },
{ NO_HARM_FROM_CLIENT, "Immune to Harm from Client" },
{ ALWAYS_FLEE, "Always Flees" },
{ FLEE_PERCENT, "Flee Percentage" },
{ ALLOW_BENEFICIAL, "Allows Beneficial Spells" },
{ DISABLE_MELEE, "Melee is Disabled" },
{ NPC_CHASE_DISTANCE, "Chase Distance" },
{ ALLOW_TO_TANK, "Allowed to Tank" },
{ IGNORE_ROOT_AGGRO_RULES, "Ignores Root Aggro" },
{ CASTING_RESIST_DIFF, "Casting Resist Difficulty" },
{ COUNTER_AVOID_DAMAGE, "Counter Damage Avoidance" },
{ PROX_AGGRO, "Proximity Aggro" },
{ IMMUNE_RANGED_ATTACKS, "Immune to Ranged Attacks" },
{ IMMUNE_DAMAGE_CLIENT, "Immune to Client Damage" },
{ IMMUNE_DAMAGE_NPC, "Immune to NPC Damage" },
{ IMMUNE_AGGRO_CLIENT, "Immune to Client Aggro" },
{ IMMUNE_AGGRO_NPC, "Immune to NPC Aggro" },
{ MODIFY_AVOID_DAMAGE, "Modify Damage Avoidance" },
{ IMMUNE_FADING_MEMORIES, "Immune to Memory Fades" },
{ IMMUNE_OPEN, "Immune to Open" },
{ IMMUNE_ASSASSINATE, "Immune to Assassinate" },
{ IMMUNE_HEADSHOT, "Immune to Headshot" },
};
return consider_color_map;
return special_ability_map;
}
std::string EQ::constants::GetConsiderColorName(uint32 consider_color)
std::string EQ::constants::GetSpecialAbilityName(uint32 ability_id)
{
const auto& c = EQ::constants::GetConsiderColorMap().find(consider_color);
return c != EQ::constants::GetConsiderColorMap().end() ? c->second : std::string();
const auto& a = EQ::constants::GetSpecialAbilityMap().find(ability_id);
if (a != EQ::constants::GetSpecialAbilityMap().end()) {
return a->second;
}
std::string AccountStatus::GetName(uint8 account_status)
{
for (
auto e = account_status_names.rbegin();
e != account_status_names.rend();
++e
) {
if (account_status >= e->first) {
return e->second;
}
}
return "UNKNOWN ACCOUNT STATUS";
}
std::string ComparisonType::GetName(uint8 type)
{
return IsValid(type) ? comparison_types[type] : "UNKNOWN COMPARISON TYPE";
}
bool ComparisonType::IsValid(uint8 type)
{
return comparison_types.find(type) != comparison_types.end();
}
uint32 LDoNTheme::GetBitmask(uint32 theme_id)
{
return IsValid(theme_id) ? ldon_theme_names[theme_id].second : LDoNTheme::UnusedBit;
}
std::string LDoNTheme::GetName(uint32 theme_id)
{
return IsValid(theme_id) ? ldon_theme_names[theme_id].first : "UNKNOWN LDON THEME";
}
bool LDoNTheme::IsValid(uint32 theme_id)
{
return ldon_theme_names.find(theme_id) != ldon_theme_names.end();
}
std::string PetCommand::GetName(uint8 pet_command)
{
return IsValid(pet_command) ? pet_commands[pet_command] : "UNKNOWN PET COMMAND";
}
bool PetCommand::IsValid(uint8 pet_command)
{
return pet_commands.find(pet_command) != pet_commands.end();
}
std::string PetType::GetName(uint8 pet_type)
{
return IsValid(pet_type) ? pet_types[pet_type] : "UNKNOWN PET TYPE";
}
bool PetType::IsValid(uint8 pet_type)
{
return pet_types.find(pet_type) != pet_types.end();
return std::string();
}
+246 -481
View File
@@ -26,76 +26,6 @@
#include <string.h>
namespace AccountStatus {
constexpr uint8 Player = 0;
constexpr uint8 Steward = 10;
constexpr uint8 ApprenticeGuide = 20;
constexpr uint8 Guide = 50;
constexpr uint8 QuestTroupe = 80;
constexpr uint8 SeniorGuide = 81;
constexpr uint8 GMTester = 85;
constexpr uint8 EQSupport = 90;
constexpr uint8 GMStaff = 95;
constexpr uint8 GMAdmin = 100;
constexpr uint8 GMLeadAdmin = 150;
constexpr uint8 QuestMaster = 160;
constexpr uint8 GMAreas = 170;
constexpr uint8 GMCoder = 180;
constexpr uint8 GMMgmt = 200;
constexpr uint8 GMImpossible = 250;
constexpr uint8 Max = 255;
std::string GetName(uint8 account_status);
}
static std::map<uint8, std::string> account_status_names = {
{ AccountStatus::Player, "Player" },
{ AccountStatus::Steward, "Steward" },
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
{ AccountStatus::Guide, "Guide" },
{ AccountStatus::QuestTroupe, "Quest Troupe" },
{ AccountStatus::SeniorGuide, "Senior Guide" },
{ AccountStatus::GMTester, "GM Tester" },
{ AccountStatus::EQSupport, "EQ Support" },
{ AccountStatus::GMStaff, "GM Staff" },
{ AccountStatus::GMAdmin, "GM Admin" },
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
{ AccountStatus::QuestMaster, "Quest Master" },
{ AccountStatus::GMAreas, "GM Areas" },
{ AccountStatus::GMCoder, "GM Coder" },
{ AccountStatus::GMMgmt, "GM Mgmt" },
{ AccountStatus::GMImpossible, "GM Impossible" },
{ AccountStatus::Max, "GM Max" }
};
namespace ComparisonType {
constexpr uint8 Equal = 0;
constexpr uint8 NotEqual = 1;
constexpr uint8 GreaterOrEqual = 2;
constexpr uint8 LesserOrEqual = 3;
constexpr uint8 Greater = 4;
constexpr uint8 Lesser = 5;
constexpr uint8 Any = 6;
constexpr uint8 NotAny = 7;
constexpr uint8 Between = 8;
constexpr uint8 NotBetween = 9;
std::string GetName(uint8 type);
bool IsValid(uint8 type);
}
static std::map<uint8, std::string> comparison_types = {
{ ComparisonType::Equal, "Equal" },
{ ComparisonType::NotEqual, "Not Equal" },
{ ComparisonType::GreaterOrEqual, "Greater or Equal" },
{ ComparisonType::LesserOrEqual, "Lesser or Equal" },
{ ComparisonType::Greater, "Greater" },
{ ComparisonType::Lesser, "Lesser" },
{ ComparisonType::Any, "Any" },
{ ComparisonType::NotAny, "Not Any" },
{ ComparisonType::Between, "Between" },
{ ComparisonType::NotBetween, "Not Between" },
};
// local definitions are the result of using hybrid-client or server-only values and methods
namespace EQ
@@ -130,11 +60,9 @@ namespace EQ
using RoF2::invtype::MAIL_SIZE;
using RoF2::invtype::GUILD_TROPHY_TRIBUTE_SIZE;
using RoF2::invtype::KRONO_SIZE;
using RoF2::invtype::GUILD_BANK_MAIN_SIZE;
using RoF2::invtype::GUILD_BANK_DEPOSIT_SIZE;
using RoF2::invtype::OTHER_SIZE;
using RoF2::invtype::TRADE_NPC_SIZE;
using Titanium::invtype::TRADE_NPC_SIZE;
using RoF2::invtype::TYPE_INVALID;
using RoF2::invtype::TYPE_BEGIN;
@@ -161,7 +89,7 @@ namespace EQ
using RoF2::invslot::SLOT_INVALID;
using RoF2::invslot::SLOT_BEGIN;
using RoF2::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value)
@@ -181,25 +109,25 @@ namespace EQ
using RoF2::invslot::BONUS_STAT_END;
using RoF2::invslot::BONUS_SKILL_END;
using RoF2::invslot::BANK_BEGIN;
using RoF2::invslot::BANK_END;
using Titanium::invslot::BANK_BEGIN;
using SoF::invslot::BANK_END;
using RoF2::invslot::SHARED_BANK_BEGIN;
using RoF2::invslot::SHARED_BANK_END;
using Titanium::invslot::SHARED_BANK_BEGIN;
using Titanium::invslot::SHARED_BANK_END;
using RoF2::invslot::TRADE_BEGIN;
using RoF2::invslot::TRADE_END;
using Titanium::invslot::TRADE_BEGIN;
using Titanium::invslot::TRADE_END;
using RoF2::invslot::TRADE_NPC_END;
using Titanium::invslot::TRADE_NPC_END;
using RoF2::invslot::WORLD_BEGIN;
using RoF2::invslot::WORLD_END;
using Titanium::invslot::WORLD_BEGIN;
using Titanium::invslot::WORLD_END;
using RoF2::invslot::TRIBUTE_BEGIN;
using RoF2::invslot::TRIBUTE_END;
using Titanium::invslot::TRIBUTE_BEGIN;
using Titanium::invslot::TRIBUTE_END;
using RoF2::invslot::GUILD_TRIBUTE_BEGIN;
using RoF2::invslot::GUILD_TRIBUTE_END;
using Titanium::invslot::GUILD_TRIBUTE_BEGIN;
using Titanium::invslot::GUILD_TRIBUTE_END;
const int16 CORPSE_BEGIN = invslot::slotGeneral1;
const int16 CORPSE_END = CORPSE_BEGIN + invslot::slotCursor;
@@ -216,40 +144,38 @@ namespace EQ
} // namespace invslot
namespace invbag {
using RoF2::invbag::SLOT_INVALID;
using RoF2::invbag::SLOT_BEGIN;
using RoF2::invbag::SLOT_END;
using RoF2::invbag::SLOT_COUNT;
using Titanium::invbag::SLOT_INVALID;
using Titanium::invbag::SLOT_BEGIN;
using Titanium::invbag::SLOT_END;
using Titanium::invbag::SLOT_COUNT;
using RoF2::invslot::WORLD_END;
const int16 GENERAL_BAGS_BEGIN = WORLD_END + 1;
using Titanium::invbag::GENERAL_BAGS_BEGIN;
const int16 GENERAL_BAGS_COUNT = invslot::GENERAL_COUNT * SLOT_COUNT;
const int16 GENERAL_BAGS_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_COUNT) - 1;
const int16 GENERAL_BAGS_8_COUNT = 8 * SLOT_COUNT;
const int16 GENERAL_BAGS_8_END = (GENERAL_BAGS_BEGIN + GENERAL_BAGS_8_COUNT) - 1;
const int16 CURSOR_BAG_BEGIN = GENERAL_BAGS_END + 1;
const int16 CURSOR_BAG_BEGIN = 351;
const int16 CURSOR_BAG_COUNT = SLOT_COUNT;
const int16 CURSOR_BAG_END = (CURSOR_BAG_BEGIN + CURSOR_BAG_COUNT) - 1;
const int16 BANK_BAGS_BEGIN = CURSOR_BAG_END + 1;
using Titanium::invbag::BANK_BAGS_BEGIN;
const int16 BANK_BAGS_COUNT = (invtype::BANK_SIZE * SLOT_COUNT);
const int16 BANK_BAGS_END = (BANK_BAGS_BEGIN + BANK_BAGS_COUNT) - 1;
const int16 BANK_BAGS_16_COUNT = 16 * SLOT_COUNT;
const int16 BANK_BAGS_16_END = (BANK_BAGS_BEGIN + BANK_BAGS_16_COUNT) - 1;
const int16 SHARED_BANK_BAGS_BEGIN = BANK_BAGS_END + 1;
using Titanium::invbag::SHARED_BANK_BAGS_BEGIN;
const int16 SHARED_BANK_BAGS_COUNT = invtype::SHARED_BANK_SIZE * SLOT_COUNT;
const int16 SHARED_BANK_BAGS_END = (SHARED_BANK_BAGS_BEGIN + SHARED_BANK_BAGS_COUNT) - 1;
const int16 TRADE_BAGS_BEGIN = SHARED_BANK_BAGS_END + 1;
using Titanium::invbag::TRADE_BAGS_BEGIN;
const int16 TRADE_BAGS_COUNT = invtype::TRADE_SIZE * SLOT_COUNT;
const int16 TRADE_BAGS_END = (TRADE_BAGS_BEGIN + TRADE_BAGS_COUNT) - 1;
using RoF2::invbag::GetInvBagIndexName;
using Titanium::invbag::GetInvBagIndexName;
} // namespace invbag
@@ -278,6 +204,19 @@ namespace EQ
const size_t SAY_LINK_CLOSER_SIZE = 1;
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
enum StanceType : int {
stanceUnknown = 0,
stancePassive,
stanceBalanced,
stanceEfficient,
stanceReactive,
stanceAggressive,
stanceAssist,
stanceBurn,
stanceEfficient2,
stanceBurnAE
};
enum BotSpellIDs : int {
Warrior = 3001,
Cleric,
@@ -328,6 +267,70 @@ namespace EQ
Looting
};
enum ObjectTypes : int {
SmallBag,
LargeBag,
Quiver,
BeltPouch,
WristPouch,
Backpack,
SmallChest,
LargeChest,
Bandolier,
Medicine,
Tinkering,
Lexicon,
PoisonMaking,
Quest,
MixingBowl,
Baking,
Tailoring,
Blacksmithing,
Fletching,
Brewing,
JewelryMaking,
Pottery,
Kiln,
KeyMaker,
ResearchWIZ,
ResearchMAG,
ResearchNEC,
ResearchENC,
Unknown,
ResearchPractice,
Alchemy,
HighElfForge,
DarkElfForge,
OgreForge,
DwarfForge,
GnomeForge,
BarbarianForge,
IksarForge,
HumanForgeOne,
HumanForgeTwo,
HalflingTailoringOne,
HalflingTailoringTwo,
EruditeTailoring,
WoodElfTailoring,
WoodElfFletching,
IksarPottery,
Fishing,
TrollForge,
WoodElfForge,
HalflingForge,
EruditeForge,
Merchant,
FroglokForge,
Augmenter,
Churn,
TransformationMold,
DetransformationMold,
Unattuner,
TradeskillBag,
CollectibleBag,
NoDeposit
};
enum WeatherTypes : uint8 {
None,
Raining,
@@ -353,12 +356,24 @@ namespace EQ
Proximity
};
const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type);
extern const std::map<uint8, std::string>& GetLanguageMap();
std::string GetLanguageName(uint8 language_id);
extern const std::map<uint32, std::string>& GetLDoNThemeMap();
std::string GetLDoNThemeName(uint32 theme_id);
extern const std::map<int8, std::string>& GetFlyModeMap();
std::string GetFlyModeName(int8 flymode_id);
extern const std::map<bodyType, std::string>& GetBodyTypeMap();
std::string GetBodyTypeName(bodyType bodytype_id);
extern const std::map<uint8, std::string>& GetAccountStatusMap();
std::string GetAccountStatusName(uint8 account_status);
extern const std::map<uint8, std::string>& GetConsiderLevelMap();
std::string GetConsiderLevelName(uint8 consider_level);
@@ -371,6 +386,9 @@ namespace EQ
extern const std::map<uint8, std::string>& GetSpawnAnimationMap();
std::string GetSpawnAnimationName(uint8 animation_id);
extern const std::map<int, std::string>& GetObjectTypeMap();
std::string GetObjectTypeName(int object_type);
extern const std::map<uint8, std::string>& GetWeatherTypeMap();
std::string GetWeatherTypeName(uint8 weather_type);
@@ -383,8 +401,12 @@ namespace EQ
extern const std::map<uint32, std::string>& GetAppearanceTypeMap();
std::string GetAppearanceTypeName(uint32 animation_type);
extern const std::map<uint32, std::string>& GetConsiderColorMap();
std::string GetConsiderColorName(uint32 consider_color);
extern const std::map<uint32, std::string>& GetSpecialAbilityMap();
std::string GetSpecialAbilityName(uint32 ability_id);
const int STANCE_TYPE_FIRST = stancePassive;
const int STANCE_TYPE_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE;
} /*constants*/
@@ -439,6 +461,37 @@ namespace EQ
} // namespace spells
namespace bug {
enum CategoryID : uint32 {
catOther = 0,
catVideo,
catAudio,
catPathing,
catQuest,
catTradeskills,
catSpellStacking,
catDoorsPortals,
catItems,
catNPC,
catDialogs,
catLoNTCG,
catMercenaries
};
enum OptionalInfoFlag : uint32 {
infoNoOptionalInfo = 0x0,
infoCanDuplicate = 0x1,
infoCrashBug = 0x2,
infoTargetInfo = 0x4,
infoCharacterFlags = 0x8,
infoUnknownValue = 0xFFFFFFF0
};
const char* CategoryIDToCategoryName(CategoryID category_id);
CategoryID CategoryNameToCategoryID(const char* category_name);
} // namespace bug
enum WaypointStatus : int {
RoamBoxPauseInProgress = -3,
QuestControlNoGrid = -2,
@@ -452,7 +505,7 @@ namespace EQ
Raid,
Guild
};
};
}; // namespace consent
} /*EQEmu*/
enum ServerLockType : int {
@@ -461,6 +514,26 @@ enum ServerLockType : int {
Unlock
};
enum AccountStatus : uint8 {
Player = 0,
Steward = 10,
ApprenticeGuide = 20,
Guide = 50,
QuestTroupe = 80,
SeniorGuide = 81,
GMTester = 85,
EQSupport = 90,
GMStaff = 95,
GMAdmin = 100,
GMLeadAdmin = 150,
QuestMaster = 160,
GMAreas = 170,
GMCoder = 180,
GMMgmt = 200,
GMImpossible = 250,
Max = 255
};
enum Invisibility : uint8 {
Visible,
Invisible,
@@ -486,17 +559,6 @@ enum ConsiderLevel : uint8 {
Scowls
};
namespace ConsiderColor {
constexpr uint32 Green = 2;
constexpr uint32 DarkBlue = 4;
constexpr uint32 Gray = 6;
constexpr uint32 White = 10;
constexpr uint32 Red = 13;
constexpr uint32 Yellow = 15;
constexpr uint32 LightBlue = 18;
constexpr uint32 WhiteTitanium = 20;
};
enum TargetDescriptionType : uint8 {
LCSelf,
UCSelf,
@@ -512,6 +574,19 @@ enum ReloadWorld : uint8 {
ForceRepop
};
enum BucketComparison : uint8 {
BucketEqualTo = 0,
BucketNotEqualTo,
BucketGreaterThanOrEqualTo,
BucketLesserThanOrEqualTo,
BucketGreaterThan,
BucketLesserThan,
BucketIsAny,
BucketIsNotAny,
BucketIsBetween,
BucketIsNotBetween
};
enum class EntityFilterType {
All,
Bots,
@@ -525,132 +600,66 @@ enum class ApplySpellType {
Raid
};
namespace SpecialAbility {
constexpr int Summon = 1;
constexpr int Enrage = 2;
constexpr int Rampage = 3;
constexpr int AreaRampage = 4;
constexpr int Flurry = 5;
constexpr int TripleAttack = 6;
constexpr int QuadrupleAttack = 7;
constexpr int DualWield = 8;
constexpr int BaneAttack = 9;
constexpr int MagicalAttack = 10;
constexpr int RangedAttack = 11;
constexpr int SlowImmunity = 12;
constexpr int MesmerizeImmunity = 13;
constexpr int CharmImmunity = 14;
constexpr int StunImmunity = 15;
constexpr int SnareImmunity = 16;
constexpr int FearImmunity = 17;
constexpr int DispellImmunity = 18;
constexpr int MeleeImmunity = 19;
constexpr int MagicImmunity = 20;
constexpr int FleeingImmunity = 21;
constexpr int MeleeImmunityExceptBane = 22;
constexpr int MeleeImmunityExceptMagical = 23;
constexpr int AggroImmunity = 24;
constexpr int BeingAggroImmunity = 25;
constexpr int CastingFromRangeImmunity = 26;
constexpr int FeignDeathImmunity = 27;
constexpr int TauntImmunity = 28;
constexpr int TunnelVision = 29;
constexpr int NoBuffHealFriends = 30;
constexpr int PacifyImmunity = 31;
constexpr int Leash = 32;
constexpr int Tether = 33;
constexpr int DestructibleObject = 34;
constexpr int HarmFromClientImmunity = 35;
constexpr int AlwaysFlee = 36;
constexpr int FleePercent = 37;
constexpr int AllowBeneficial = 38;
constexpr int DisableMelee = 39;
constexpr int NPCChaseDistance = 40;
constexpr int AllowedToTank = 41;
constexpr int IgnoreRootAggroRules = 42;
constexpr int CastingResistDifficulty = 43;
constexpr int CounterAvoidDamage = 44;
constexpr int ProximityAggro = 45;
constexpr int RangedAttackImmunity = 46;
constexpr int ClientDamageImmunity = 47;
constexpr int NPCDamageImmunity = 48;
constexpr int ClientAggroImmunity = 49;
constexpr int NPCAggroImmunity = 50;
constexpr int ModifyAvoidDamage = 51;
constexpr int MemoryFadeImmunity = 52;
constexpr int OpenImmunity = 53;
constexpr int AssassinateImmunity = 54;
constexpr int HeadshotImmunity = 55;
constexpr int BotAggroImmunity = 56;
constexpr int BotDamageImmunity = 57;
constexpr int Max = 58;
constexpr int MaxParameters = 9;
std::string GetName(int ability_id);
bool IsValid(int ability_id);
}
static std::map<int, std::string> special_ability_names = {
{ SpecialAbility::Summon, "Summon" },
{ SpecialAbility::Enrage, "Enrage" },
{ SpecialAbility::Rampage, "Rampage" },
{ SpecialAbility::AreaRampage, "Area Rampage" },
{ SpecialAbility::Flurry, "Flurry" },
{ SpecialAbility::TripleAttack, "Triple Attack" },
{ SpecialAbility::QuadrupleAttack, "Quadruple Attack" },
{ SpecialAbility::DualWield, "Dual Wield" },
{ SpecialAbility::BaneAttack, "Bane Attack" },
{ SpecialAbility::MagicalAttack, "Magical Attack" },
{ SpecialAbility::RangedAttack, "Ranged Attack" },
{ SpecialAbility::SlowImmunity, "Immune to Slow" },
{ SpecialAbility::MesmerizeImmunity, "Immune to Mesmerize" },
{ SpecialAbility::CharmImmunity, "Immune to Charm" },
{ SpecialAbility::StunImmunity, "Immune to Stun" },
{ SpecialAbility::SnareImmunity, "Immune to Snare" },
{ SpecialAbility::FearImmunity, "Immune to Fear" },
{ SpecialAbility::DispellImmunity, "Immune to Dispell" },
{ SpecialAbility::MeleeImmunity, "Immune to Melee" },
{ SpecialAbility::MagicImmunity, "Immune to Magic" },
{ SpecialAbility::FleeingImmunity, "Immune to Fleeing" },
{ SpecialAbility::MeleeImmunityExceptBane, "Immune to Melee except Bane" },
{ SpecialAbility::MeleeImmunityExceptMagical, "Immune to Non-Magical Melee" },
{ SpecialAbility::AggroImmunity, "Immune to Aggro" },
{ SpecialAbility::BeingAggroImmunity, "Immune to Being Aggro" },
{ SpecialAbility::CastingFromRangeImmunity, "Immune to Ranged Spells" },
{ SpecialAbility::FeignDeathImmunity, "Immune to Feign Death" },
{ SpecialAbility::TauntImmunity, "Immune to Taunt" },
{ SpecialAbility::TunnelVision, "Tunnel Vision" },
{ SpecialAbility::NoBuffHealFriends, "Does Not Heal or Buff Allies" },
{ SpecialAbility::PacifyImmunity, "Immune to Pacify" },
{ SpecialAbility::Leash, "Leashed" },
{ SpecialAbility::Tether, "Tethered" },
{ SpecialAbility::DestructibleObject, "Destructible Object" },
{ SpecialAbility::HarmFromClientImmunity, "Immune to Harm from Client" },
{ SpecialAbility::AlwaysFlee, "Always Flees" },
{ SpecialAbility::FleePercent, "Flee Percentage" },
{ SpecialAbility::AllowBeneficial, "Allows Beneficial Spells" },
{ SpecialAbility::DisableMelee, "Melee is Disabled" },
{ SpecialAbility::NPCChaseDistance, "Chase Distance" },
{ SpecialAbility::AllowedToTank, "Allowed to Tank" },
{ SpecialAbility::IgnoreRootAggroRules, "Ignores Root Aggro" },
{ SpecialAbility::CastingResistDifficulty, "Casting Resist Difficulty" },
{ SpecialAbility::CounterAvoidDamage, "Counter Damage Avoidance" },
{ SpecialAbility::ProximityAggro, "Proximity Aggro" },
{ SpecialAbility::RangedAttackImmunity, "Immune to Ranged Attacks" },
{ SpecialAbility::ClientDamageImmunity, "Immune to Client Damage" },
{ SpecialAbility::NPCDamageImmunity, "Immune to NPC Damage" },
{ SpecialAbility::ClientAggroImmunity, "Immune to Client Aggro" },
{ SpecialAbility::NPCAggroImmunity, "Immune to NPC Aggro" },
{ SpecialAbility::ModifyAvoidDamage, "Modify Damage Avoidance" },
{ SpecialAbility::MemoryFadeImmunity, "Immune to Memory Fades" },
{ SpecialAbility::OpenImmunity, "Immune to Open" },
{ SpecialAbility::AssassinateImmunity, "Immune to Assassinate" },
{ SpecialAbility::HeadshotImmunity, "Immune to Headshot" },
{ SpecialAbility::BotAggroImmunity, "Immune to Bot Aggro" },
{ SpecialAbility::BotDamageImmunity, "Immune to Bot Damage" },
enum {
SPECATK_SUMMON = 1,
SPECATK_ENRAGE = 2,
SPECATK_RAMPAGE = 3,
SPECATK_AREA_RAMPAGE = 4,
SPECATK_FLURRY = 5,
SPECATK_TRIPLE = 6,
SPECATK_QUAD = 7,
SPECATK_INNATE_DW = 8,
SPECATK_BANE = 9,
SPECATK_MAGICAL = 10,
SPECATK_RANGED_ATK = 11,
UNSLOWABLE = 12,
UNMEZABLE = 13,
UNCHARMABLE = 14,
UNSTUNABLE = 15,
UNSNAREABLE = 16,
UNFEARABLE = 17,
UNDISPELLABLE = 18,
IMMUNE_MELEE = 19,
IMMUNE_MAGIC = 20,
IMMUNE_FLEEING = 21,
IMMUNE_MELEE_EXCEPT_BANE = 22,
IMMUNE_MELEE_NONMAGICAL = 23,
IMMUNE_AGGRO = 24,
IMMUNE_AGGRO_ON = 25,
IMMUNE_CASTING_FROM_RANGE = 26,
IMMUNE_FEIGN_DEATH = 27,
IMMUNE_TAUNT = 28,
NPC_TUNNELVISION = 29,
NPC_NO_BUFFHEAL_FRIENDS = 30,
IMMUNE_PACIFY = 31,
LEASH = 32,
TETHER = 33,
DESTRUCTIBLE_OBJECT = 34,
NO_HARM_FROM_CLIENT = 35,
ALWAYS_FLEE = 36,
FLEE_PERCENT = 37,
ALLOW_BENEFICIAL = 38,
DISABLE_MELEE = 39,
NPC_CHASE_DISTANCE = 40,
ALLOW_TO_TANK = 41,
IGNORE_ROOT_AGGRO_RULES = 42,
CASTING_RESIST_DIFF = 43,
COUNTER_AVOID_DAMAGE = 44, // Modify by percent NPC's opponents chance to riposte, block, parry or dodge individually, or for all skills
PROX_AGGRO = 45,
IMMUNE_RANGED_ATTACKS = 46,
IMMUNE_DAMAGE_CLIENT = 47,
IMMUNE_DAMAGE_NPC = 48,
IMMUNE_AGGRO_CLIENT = 49,
IMMUNE_AGGRO_NPC = 50,
MODIFY_AVOID_DAMAGE = 51, // Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills
IMMUNE_FADING_MEMORIES = 52,
IMMUNE_OPEN = 53,
IMMUNE_ASSASSINATE = 54,
IMMUNE_HEADSHOT = 55,
MAX_SPECIAL_ATTACK = 56
};
namespace HeroicBonusBucket
{
const std::string WisMaxMana = "HWIS-MaxMana";
@@ -675,248 +684,4 @@ namespace HeroicBonusBucket
const std::string DexEnduranceRegen = "HDEX-EnduranceRegen";
}
namespace Bug {
namespace Category {
constexpr uint32 Other = 0;
constexpr uint32 Video = 1;
constexpr uint32 Audio = 2;
constexpr uint32 Pathing = 3;
constexpr uint32 Quest = 4;
constexpr uint32 Tradeskills = 5;
constexpr uint32 SpellStacking = 6;
constexpr uint32 DoorsPortals = 7;
constexpr uint32 Items = 8;
constexpr uint32 NPC = 9;
constexpr uint32 Dialogs = 10;
constexpr uint32 LoNTCG = 11;
constexpr uint32 Mercenaries = 12;
}
namespace InformationFlag {
constexpr uint32 None = 0;
constexpr uint32 Repeatable = 1;
constexpr uint32 Crash = 2;
constexpr uint32 TargetInfo = 4;
constexpr uint32 CharacterFlags = 8;
constexpr uint32 Unknown = 4294967280;
}
uint32 GetID(const std::string& category_name);
std::string GetName(uint32 category_id);
bool IsValid(uint32 category_id);
}
static std::map<uint32, std::string> bug_category_names = {
{ Bug::Category::Other, "Other" },
{ Bug::Category::Video, "Video" },
{ Bug::Category::Audio, "Audio" },
{ Bug::Category::Pathing, "Pathing" },
{ Bug::Category::Quest, "Quest" },
{ Bug::Category::Tradeskills, "Tradeskills" },
{ Bug::Category::SpellStacking, "Spell Stacking" },
{ Bug::Category::DoorsPortals, "Doors and Portals" },
{ Bug::Category::Items, "Items" },
{ Bug::Category::NPC, "NPC" },
{ Bug::Category::Dialogs, "Dialogs" },
{ Bug::Category::LoNTCG, "LoN - TCG" },
{ Bug::Category::Mercenaries, "Mercenaries" }
};
namespace Stance {
constexpr uint32 Unknown = 0;
constexpr uint32 Passive = 1;
constexpr uint32 Balanced = 2;
constexpr uint32 Efficient = 3;
constexpr uint32 Reactive = 4;
constexpr uint32 Aggressive = 5;
constexpr uint32 Assist = 6;
constexpr uint32 Burn = 7;
constexpr uint32 Efficient2 = 8;
constexpr uint32 AEBurn = 9;
std::string GetName(uint8 stance_id);
uint8 GetIndex(uint8 stance_id);
bool IsValid(uint8 stance_id);
}
static std::map<uint32, std::string> stance_names = {
{ Stance::Unknown, "Unknown" },
{ Stance::Passive, "Passive" },
{ Stance::Balanced, "Balanced" },
{ Stance::Efficient, "Efficient" },
{ Stance::Reactive, "Reactive" },
{ Stance::Aggressive, "Aggressive" },
{ Stance::Assist, "Assist" },
{ Stance::Burn, "Burn" },
{ Stance::Efficient2, "Efficient" },
{ Stance::AEBurn, "AE Burn" }
};
namespace LDoNTheme {
constexpr uint32 Unused = 0;
constexpr uint32 GUK = 1;
constexpr uint32 MIR = 2;
constexpr uint32 MMC = 3;
constexpr uint32 RUJ = 4;
constexpr uint32 TAK = 5;
constexpr uint32 UnusedBit = 0;
constexpr uint32 GUKBit = 1;
constexpr uint32 MIRBit = 2;
constexpr uint32 MMCBit = 4;
constexpr uint32 RUJBit = 8;
constexpr uint32 TAKBit = 16;
uint32 GetBitmask(uint32 theme_id);
std::string GetName(uint32 theme_id);
bool IsValid(uint32 theme_id);
}
static std::map<uint32, std::pair<std::string, uint32>> ldon_theme_names = {
{ LDoNTheme::Unused, { "Unused", LDoNTheme::UnusedBit }, },
{ LDoNTheme::GUK, { "Deepest Guk", LDoNTheme::GUKBit }, },
{ LDoNTheme::MIR, { "Miragul's Menagerie", LDoNTheme::MIRBit }, },
{ LDoNTheme::MMC, { "Mistmoore Catacombs", LDoNTheme::MMCBit }, },
{ LDoNTheme::RUJ, { "Rujarkian Hills", LDoNTheme::RUJBit }, },
{ LDoNTheme::TAK, { "Takish-Hiz", LDoNTheme::TAKBit }, },
};
namespace PCNPCOnlyFlagType {
constexpr int PC = 1;
constexpr int NPC = 2;
}
namespace BookType {
constexpr uint8 Scroll = 0;
constexpr uint8 Book = 1;
constexpr uint8 ItemInfo = 2;
}
namespace PetButton {
constexpr uint8 Sit = 0;
constexpr uint8 Stop = 1;
constexpr uint8 Regroup = 2;
constexpr uint8 Follow = 3;
constexpr uint8 Guard = 4;
constexpr uint8 Taunt = 5;
constexpr uint8 Hold = 6;
constexpr uint8 GreaterHold = 7;
constexpr uint8 Focus = 8;
constexpr uint8 SpellHold = 9;
}
namespace PetButtonState {
constexpr uint8 Off = 0;
constexpr uint8 On = 1;
}
namespace PetCommand {
constexpr uint8 HealthReport = 0; // /pet health or Pet Window
constexpr uint8 Leader = 1; // /pet leader or Pet Window
constexpr uint8 Attack = 2; // /pet attack or Pet Window
constexpr uint8 QAttack = 3; // /pet qattack or Pet Window
constexpr uint8 FollowMe = 4; // /pet follow or Pet Window
constexpr uint8 GuardHere = 5; // /pet guard or Pet Window
constexpr uint8 Sit = 6; // /pet sit or Pet Window
constexpr uint8 SitDown = 7; // /pet sit on
constexpr uint8 StandUp = 8; // /pet sit off
constexpr uint8 Stop = 9; // /pet stop or Pet Window - Not implemented
constexpr uint8 StopOn = 10; // /pet stop on - Not implemented
constexpr uint8 StopOff = 11; // /pet stop off - Not implemented
constexpr uint8 Taunt = 12; // /pet taunt or Pet Window
constexpr uint8 TauntOn = 13; // /pet taunt on
constexpr uint8 TauntOff = 14; // /pet taunt off
constexpr uint8 Hold = 15; // /pet hold or Pet Window, won't add to hate list unless attacking
constexpr uint8 HoldOn = 16; // /pet hold on
constexpr uint8 HoldOff = 17; // /pet hold off
constexpr uint8 GreaterHold = 18; // /pet ghold, will never add to hate list unless told to
constexpr uint8 GreaterHoldOn = 19; // /pet ghold on
constexpr uint8 GreaterHoldOff = 20; // /pet ghold off
constexpr uint8 SpellHold = 21; // /pet no cast or /pet spellhold or Pet Window
constexpr uint8 SpellHoldOn = 22; // /pet spellhold on
constexpr uint8 SpellHoldOff = 23; // /pet spellhold off
constexpr uint8 Focus = 24; // /pet focus or Pet Window
constexpr uint8 FocusOn = 25; // /pet focus on
constexpr uint8 FocusOff = 26; // /pet focus off
constexpr uint8 Feign = 27; // /pet feign
constexpr uint8 BackOff = 28; // /pet back off
constexpr uint8 GetLost = 29; // /pet get lost
constexpr uint8 GuardMe = 30; // Same as /pet follow, but different message in older clients
constexpr uint8 Regroup = 31; // /pet regroup, acts like classic hold
constexpr uint8 RegroupOn = 32; // /pet regroup on
constexpr uint8 RegroupOff = 33; // /pet regroup off
constexpr uint8 Max = 34;
static std::map<uint8, std::string> pet_commands = {
{ PetCommand::HealthReport, "Health Report" },
{ PetCommand::Leader, "Leader" },
{ PetCommand::Attack, "Attack" },
{ PetCommand::QAttack, "QAttack" },
{ PetCommand::FollowMe, "Follow Me" },
{ PetCommand::GuardHere, "Guard Here" },
{ PetCommand::Sit, "Sit" },
{ PetCommand::SitDown, "Sit Down" },
{ PetCommand::StandUp, "Stand Up" },
{ PetCommand::Stop, "Stop" },
{ PetCommand::StopOn, "Stop On" },
{ PetCommand::StopOff, "Stop Off" },
{ PetCommand::Taunt, "Taunt" },
{ PetCommand::TauntOn, "Taunt On" },
{ PetCommand::TauntOff, "Taunt Off" },
{ PetCommand::Hold, "Hold" },
{ PetCommand::HoldOn, "Hold On" },
{ PetCommand::HoldOff, "Hold Off" },
{ PetCommand::GreaterHold, "Greater Hold" },
{ PetCommand::GreaterHoldOn, "Greater Hold On" },
{ PetCommand::GreaterHoldOff, "Greater Hold Off" },
{ PetCommand::SpellHold, "Spell Hold" },
{ PetCommand::SpellHoldOn, "Spell Hold On" },
{ PetCommand::SpellHoldOff, "Spell Hold Off" },
{ PetCommand::Focus, "Focus" },
{ PetCommand::FocusOn, "Focus On" },
{ PetCommand::FocusOff, "Focus Off" },
{ PetCommand::Feign, "Feign" },
{ PetCommand::BackOff, "Back Off" },
{ PetCommand::GetLost, "Get Lost" },
{ PetCommand::GuardMe, "Guard Me" },
{ PetCommand::Regroup, "Regroup" },
{ PetCommand::RegroupOn, "Regroup On" },
{ PetCommand::RegroupOff, "Regroup Off" },
{ PetCommand::Max, "Max" }
};
std::string GetName(uint8 pet_command);
bool IsValid(uint8 pet_command);
}
namespace PetOrder {
constexpr uint8 Follow = 0;
constexpr uint8 Sit = 1;
constexpr uint8 Guard = 2;
constexpr uint8 Feign = 3;
}
namespace PetType {
constexpr uint8 Familiar = 0;
constexpr uint8 Animation = 1;
constexpr uint8 Normal = 2;
constexpr uint8 Charmed = 3;
constexpr uint8 Follow = 4;
constexpr uint8 TargetLock = 5;
constexpr uint8 None = 255;
static std::map<uint8, std::string> pet_types = {
{ PetType::Familiar, "Familiar" },
{ PetType::Animation, "Animation" },
{ PetType::Normal, "Normal" },
{ PetType::Charmed, "Charmed" },
{ PetType::Follow, "Follow" },
{ PetType::TargetLock, "Target Lock" },
{ PetType::None, "None" }
};
std::string GetName(uint8 pet_type);
bool IsValid(uint8 pet_type);
}
#endif /*COMMON_EMU_CONSTANTS_H*/
-18
View File
@@ -67,7 +67,6 @@ N(OP_Buff),
N(OP_BuffCreate),
N(OP_BuffRemoveRequest),
N(OP_Bug),
N(OP_BuyerItems),
N(OP_CameraEffect),
N(OP_Camp),
N(OP_CancelSneakHide),
@@ -77,7 +76,6 @@ N(OP_CashReward),
N(OP_CastSpell),
N(OP_ChangeSize),
N(OP_ChannelMessage),
N(OP_ChangePetName),
N(OP_CharacterCreate),
N(OP_CharacterCreateRequest),
N(OP_CharInventory),
@@ -163,7 +161,6 @@ N(OP_EnduranceUpdate),
N(OP_EnterChat),
N(OP_EnterWorld),
N(OP_EnvDamage),
N(OP_EvolveItem),
N(OP_ExpansionInfo),
N(OP_ExpUpdate),
N(OP_FaceChange),
@@ -285,17 +282,12 @@ N(OP_InspectMessageUpdate),
N(OP_InspectRequest),
N(OP_InstillDoubt),
N(OP_InterruptCast),
N(OP_InvokeChangePetName),
N(OP_InvokeChangePetNameImmediate),
N(OP_InvokeNameChangeImmediate),
N(OP_InvokeNameChangeLazy),
N(OP_ItemLinkClick),
N(OP_ItemLinkResponse),
N(OP_ItemLinkText),
N(OP_ItemName),
N(OP_ItemPacket),
N(OP_ItemPreview),
N(OP_ItemPreviewRequest),
N(OP_ItemRecastDelay),
N(OP_ItemVerifyReply),
N(OP_ItemVerifyRequest),
@@ -407,8 +399,6 @@ N(OP_PetitionSearchText),
N(OP_PetitionUnCheckout),
N(OP_PetitionUpdate),
N(OP_PickPocket),
N(OP_PickZone),
N(OP_PickZoneWindow),
N(OP_PlayerProfile),
N(OP_PlayerStateAdd),
N(OP_PlayerStateRemove),
@@ -520,11 +510,6 @@ N(OP_ShopEndConfirm),
N(OP_ShopItem),
N(OP_ShopPlayerBuy),
N(OP_ShopPlayerSell),
N(OP_ShopSendParcel),
N(OP_ShopDeleteParcel),
N(OP_ShopRespondParcel),
N(OP_ShopRetrieveParcel),
N(OP_ShopParcelIcon),
N(OP_ShopRequest),
N(OP_SimpleMessage),
N(OP_SkillUpdate),
@@ -543,7 +528,6 @@ N(OP_Stamina),
N(OP_Stun),
N(OP_Surname),
N(OP_SwapSpell),
N(OP_SystemFingerprint),
N(OP_TargetBuffs),
N(OP_TargetCommand),
N(OP_TargetHoTT),
@@ -568,7 +552,6 @@ N(OP_TradeBusy),
N(OP_TradeCoins),
N(OP_TradeMoneyUpdate),
N(OP_Trader),
N(OP_TraderBulkSend),
N(OP_TraderBuy),
N(OP_TraderDelItem),
N(OP_TradeRequest),
@@ -576,7 +559,6 @@ N(OP_TradeRequestAck),
N(OP_TraderItemUpdate),
N(OP_TraderShop),
N(OP_TradeSkillCombine),
N(OP_TradeSkillRecipeInspect),
N(OP_Translocate),
N(OP_TributeInfo),
N(OP_TributeItem),
+82 -48
View File
@@ -758,10 +758,10 @@ typedef enum {
FilterFocusEffects = 22, //0=show, 1=hide
FilterPetSpells = 23, //0=show, 1=hide
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
FilterItemSpeech = 25, //0=show, 1=hide // RoF2 Confirmed
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
FilterUnknown25 = 25,
FilterUnknown26 = 26,
FilterUnknown27 = 27,
FilterUnknown28 = 28,
_FilterCount
} eqFilterType;
@@ -793,26 +793,46 @@ typedef enum {
#define STAT_MANA_REGEN 18
#define STAT_HASTE 19
#define STAT_DAMAGE_SHIELD 20
#define STAT_DS_MITIGATION 22
#define STAT_HEAL_AMOUNT 23
#define STAT_SPELL_DAMAGE 24
#define STAT_CLAIRVOYANCE 25
#define STAT_HEROIC_AGILITY 26
#define STAT_HEROIC_CHARISMA 27
#define STAT_HEROIC_DEXTERITY 28
#define STAT_HEROIC_INTELLIGENCE 29
#define STAT_HEROIC_STAMINA 30
#define STAT_HEROIC_STRENGTH 31
#define STAT_HEROIC_WISDOM 32
#define STAT_BASH 33
#define STAT_BACKSTAB 34
#define STAT_DRAGON_PUNCH 35
#define STAT_EAGLE_STRIKE 36
#define STAT_FLYING_KICK 37
#define STAT_KICK 38
#define STAT_ROUND_KICK 39
#define STAT_TIGER_CLAW 40
#define STAT_FRENZY 41
/*
** Recast timer types. Used as an off set to charProfileStruct timers.
**
** (Another orphaned enumeration...)
*/
enum RecastTimerTypes
{
RecTimer_0 = 0,
RecTimer_1,
RecTimer_WeaponHealClick, // 2
RecTimer_MuramiteBaneNukeClick, // 3
RecTimer_4,
RecTimer_DispellClick, // 5 (also click heal orbs?)
RecTimer_Epic, // 6
RecTimer_OoWBPClick, // 7
RecTimer_VishQuestClassItem, // 8
RecTimer_HealPotion, // 9
RecTimer_10,
RecTimer_11,
RecTimer_12,
RecTimer_13,
RecTimer_14,
RecTimer_15,
RecTimer_16,
RecTimer_17,
RecTimer_18,
RecTimer_ModRod, // 19
_RecTimerCount
};
enum GroupUpdateAction
{
GUA_Joined = 0,
GUA_Left = 1,
GUA_LastLeft = 6,
GUA_FullGroupInfo = 7,
GUA_MakeLeader = 8,
GUA_Started = 9
};
static const uint8 DamageTypeSomething = 0x1C; //0x1c is something...
static const uint8 DamageTypeFalling = 0xFC;
@@ -974,6 +994,43 @@ namespace ZoneBlockedSpellTypes {
const uint8 Region = 2;
};
enum class DynamicZoneType
{
None = 0,
Expedition,
Tutorial,
Task,
Mission, // Shared Task
Quest
};
enum class DynamicZoneMemberStatus : uint8_t
{
Unknown = 0,
Online,
Offline,
InDynamicZone,
LinkDead
};
enum LDoNThemes {
Unused = 0,
GUK,
MIR,
MMC,
RUJ,
TAK
};
enum LDoNThemeBits {
UnusedBit = 0,
GUKBit = 1,
MIRBit = 2,
MMCBit = 4,
RUJBit = 8,
TAKBit = 16
};
enum StartZoneIndex {
Odus = 0,
Qeynos,
@@ -988,8 +1045,7 @@ enum StartZoneIndex {
Felwithe,
Akanon,
Cabilis,
SharVahl,
RatheMtn
SharVahl
};
enum FVNoDropFlagRule
@@ -1075,26 +1131,4 @@ namespace LeadershipAbilitySlot {
constexpr uint16 HealthOfTargetsTarget = 14;
}
enum ExpSource
{
Quest,
GM,
Kill,
Death,
Resurrection,
LDoNChest,
Task,
Sacrifice
};
#define PARCEL_SEND_ITEMS 0
#define PARCEL_SEND_MONEY 1
#define PARCEL_MONEY_ITEM_ID 99990 // item id of money
#define PARCEL_LIMIT 5
#define PARCEL_BEGIN_SLOT 1
namespace DoorType {
constexpr uint32 BuyerStall = 155;
}
#endif /*COMMON_EQ_CONSTANTS_H*/
+28 -36
View File
@@ -47,7 +47,6 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
ClientUnknown::constants::EXPANSION_BIT,
ClientUnknown::constants::EXPANSIONS_MASK,
ClientUnknown::INULL,
ClientUnknown::INULL,
ClientUnknown::INULL
),
/*[ClientVersion::Client62] =*/
@@ -56,7 +55,6 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
Client62::constants::EXPANSION_BIT,
Client62::constants::EXPANSIONS_MASK,
Client62::INULL,
Client62::INULL,
Client62::INULL
),
/*[ClientVersion::Titanium] =*/
@@ -65,8 +63,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
Titanium::constants::EXPANSION_BIT,
Titanium::constants::EXPANSIONS_MASK,
Titanium::constants::CHARACTER_CREATION_LIMIT,
Titanium::constants::SAY_LINK_BODY_SIZE,
Titanium::INULL
Titanium::constants::SAY_LINK_BODY_SIZE
),
/*[ClientVersion::SoF] =*/
EQ::constants::LookupEntry(
@@ -74,8 +71,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
SoF::constants::EXPANSION_BIT,
SoF::constants::EXPANSIONS_MASK,
SoF::constants::CHARACTER_CREATION_LIMIT,
SoF::constants::SAY_LINK_BODY_SIZE,
SoF::INULL
SoF::constants::SAY_LINK_BODY_SIZE
),
/*[ClientVersion::SoD] =*/
EQ::constants::LookupEntry(
@@ -83,8 +79,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
SoD::constants::EXPANSION_BIT,
SoD::constants::EXPANSIONS_MASK,
SoD::constants::CHARACTER_CREATION_LIMIT,
SoD::constants::SAY_LINK_BODY_SIZE,
SoD::INULL
SoD::constants::SAY_LINK_BODY_SIZE
),
/*[ClientVersion::UF] =*/
EQ::constants::LookupEntry(
@@ -92,8 +87,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
UF::constants::EXPANSION_BIT,
UF::constants::EXPANSIONS_MASK,
UF::constants::CHARACTER_CREATION_LIMIT,
UF::constants::SAY_LINK_BODY_SIZE,
UF::INULL
UF::constants::SAY_LINK_BODY_SIZE
),
/*[ClientVersion::RoF] =*/
EQ::constants::LookupEntry(
@@ -101,8 +95,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
RoF::constants::EXPANSION_BIT,
RoF::constants::EXPANSIONS_MASK,
RoF::constants::CHARACTER_CREATION_LIMIT,
RoF::constants::SAY_LINK_BODY_SIZE,
RoF::INULL
RoF::constants::SAY_LINK_BODY_SIZE
),
/*[ClientVersion::RoF2] =*/
EQ::constants::LookupEntry(
@@ -110,8 +103,7 @@ static const EQ::constants::LookupEntry constants_static_lookup_entries[EQ::vers
RoF2::constants::EXPANSION_BIT,
RoF2::constants::EXPANSIONS_MASK,
RoF2::constants::CHARACTER_CREATION_LIMIT,
RoF2::constants::SAY_LINK_BODY_SIZE,
RoF2::constants::MAX_BAZAAR_TRADERS
RoF2::constants::SAY_LINK_BODY_SIZE
)
};
@@ -173,7 +165,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL,
ClientUnknown::INULL, ClientUnknown::INULL, ClientUnknown::INULL
ClientUnknown::INULL
),
ClientUnknown::INULL,
@@ -200,7 +192,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL, Client62::INULL, Client62::INULL,
Client62::INULL, Client62::INULL, Client62::INULL
Client62::INULL
),
Client62::INULL,
@@ -227,7 +219,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Titanium::invtype::VIEW_MOD_PC_SIZE, Titanium::invtype::VIEW_MOD_BANK_SIZE, Titanium::invtype::VIEW_MOD_SHARED_BANK_SIZE,
Titanium::invtype::VIEW_MOD_LIMBO_SIZE, Titanium::invtype::ALT_STORAGE_SIZE, Titanium::invtype::ARCHIVED_SIZE,
Titanium::INULL, Titanium::INULL, Titanium::INULL,
Titanium::INULL, Titanium::INULL, Titanium::invtype::OTHER_SIZE
Titanium::invtype::OTHER_SIZE
),
Titanium::invslot::EQUIPMENT_BITMASK,
@@ -254,7 +246,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoF::invtype::VIEW_MOD_PC_SIZE, SoF::invtype::VIEW_MOD_BANK_SIZE, SoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoF::invtype::VIEW_MOD_LIMBO_SIZE, SoF::invtype::ALT_STORAGE_SIZE, SoF::invtype::ARCHIVED_SIZE,
SoF::INULL, SoF::INULL, SoF::INULL,
SoF::INULL, SoF::INULL, SoF::invtype::OTHER_SIZE
SoF::invtype::OTHER_SIZE
),
SoF::invslot::EQUIPMENT_BITMASK,
@@ -281,7 +273,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoD::invtype::VIEW_MOD_PC_SIZE, SoD::invtype::VIEW_MOD_BANK_SIZE, SoD::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoD::invtype::VIEW_MOD_LIMBO_SIZE, SoD::invtype::ALT_STORAGE_SIZE, SoD::invtype::ARCHIVED_SIZE,
SoD::INULL, SoD::INULL, SoD::INULL,
SoD::INULL, SoD::INULL, SoD::invtype::OTHER_SIZE
SoD::invtype::OTHER_SIZE
),
SoD::invslot::EQUIPMENT_BITMASK,
@@ -308,7 +300,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
UF::invtype::VIEW_MOD_PC_SIZE, UF::invtype::VIEW_MOD_BANK_SIZE, UF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
UF::invtype::VIEW_MOD_LIMBO_SIZE, UF::invtype::ALT_STORAGE_SIZE, UF::invtype::ARCHIVED_SIZE,
UF::INULL, UF::INULL, UF::INULL,
UF::INULL, UF::INULL, UF::invtype::OTHER_SIZE
UF::invtype::OTHER_SIZE
),
UF::invslot::EQUIPMENT_BITMASK,
@@ -335,7 +327,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF::invtype::VIEW_MOD_PC_SIZE, RoF::invtype::VIEW_MOD_BANK_SIZE, RoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF::invtype::VIEW_MOD_LIMBO_SIZE, RoF::invtype::ALT_STORAGE_SIZE, RoF::invtype::ARCHIVED_SIZE,
RoF::invtype::MAIL_SIZE, RoF::invtype::GUILD_TROPHY_TRIBUTE_SIZE, RoF::INULL,
RoF::INULL,RoF::INULL,RoF::invtype::OTHER_SIZE
RoF::invtype::OTHER_SIZE
),
RoF::invslot::EQUIPMENT_BITMASK,
@@ -362,7 +354,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
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::invtype::OTHER_SIZE
),
RoF2::invslot::EQUIPMENT_BITMASK,
@@ -389,7 +381,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,
EntityLimits::NPC::INULL, EntityLimits::NPC::INULL,EntityLimits::NPC::INULL
EntityLimits::NPC::INULL
),
EntityLimits::NPC::INULL,
@@ -416,7 +408,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL,
EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL, EntityLimits::NPCMerchant::INULL
EntityLimits::NPCMerchant::INULL
),
EntityLimits::NPCMerchant::INULL,
@@ -443,7 +435,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL,
EntityLimits::Merc::INULL, EntityLimits::Merc::INULL, EntityLimits::Merc::INULL
EntityLimits::Merc::INULL
),
EntityLimits::Merc::INULL,
@@ -470,7 +462,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL,
EntityLimits::Bot::INULL, EntityLimits::Bot::INULL, EntityLimits::Bot::INULL
EntityLimits::Bot::INULL
),
EntityLimits::Bot::invslot::EQUIPMENT_BITMASK,
@@ -497,7 +489,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL,
EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL, EntityLimits::ClientPet::INULL
EntityLimits::ClientPet::INULL
),
EntityLimits::ClientPet::INULL,
@@ -524,7 +516,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL,
EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL, EntityLimits::NPCPet::INULL
EntityLimits::NPCPet::INULL
),
EntityLimits::NPCPet::INULL,
@@ -551,7 +543,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL,
EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL, EntityLimits::MercPet::INULL
EntityLimits::MercPet::INULL
),
EntityLimits::MercPet::INULL,
@@ -578,7 +570,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL,
EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL, EntityLimits::BotPet::INULL
EntityLimits::BotPet::INULL
),
EntityLimits::BotPet::INULL,
@@ -605,7 +597,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
Titanium::invtype::VIEW_MOD_PC_SIZE, Titanium::invtype::VIEW_MOD_BANK_SIZE, Titanium::invtype::VIEW_MOD_SHARED_BANK_SIZE,
Titanium::invtype::VIEW_MOD_LIMBO_SIZE, Titanium::INULL, Titanium::INULL,
Titanium::INULL, Titanium::INULL, Titanium::INULL,
Titanium::INULL, Titanium::INULL, Titanium::INULL
Titanium::INULL
),
Titanium::INULL,
@@ -632,7 +624,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoF::invtype::VIEW_MOD_PC_SIZE, SoF::invtype::VIEW_MOD_BANK_SIZE, SoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoF::invtype::VIEW_MOD_LIMBO_SIZE, SoF::INULL, SoF::INULL,
SoF::INULL, SoF::INULL, SoF::INULL,
SoF::INULL, SoF::INULL, SoF::INULL
SoF::INULL
),
SoF::INULL,
@@ -659,7 +651,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
SoD::invtype::VIEW_MOD_PC_SIZE, SoD::invtype::VIEW_MOD_BANK_SIZE, SoD::invtype::VIEW_MOD_SHARED_BANK_SIZE,
SoD::invtype::VIEW_MOD_LIMBO_SIZE, SoD::INULL, SoD::INULL,
SoD::INULL, SoD::INULL, SoD::INULL,
SoD::INULL, SoD::INULL, SoD::INULL
SoD::INULL
),
SoD::INULL,
@@ -686,7 +678,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
UF::invtype::VIEW_MOD_PC_SIZE, UF::invtype::VIEW_MOD_BANK_SIZE, UF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
UF::invtype::VIEW_MOD_LIMBO_SIZE, UF::INULL, UF::INULL,
UF::INULL, UF::INULL, UF::INULL,
UF::INULL, UF::INULL, UF::INULL
UF::INULL
),
UF::INULL,
@@ -713,7 +705,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
RoF::invtype::VIEW_MOD_PC_SIZE, RoF::invtype::VIEW_MOD_BANK_SIZE, RoF::invtype::VIEW_MOD_SHARED_BANK_SIZE,
RoF::invtype::VIEW_MOD_LIMBO_SIZE, RoF::INULL, RoF::INULL,
RoF::INULL, RoF::INULL, RoF::INULL,
RoF::INULL, RoF::INULL, RoF::INULL
RoF::INULL
),
RoF::INULL,
@@ -740,7 +732,7 @@ static const EQ::inventory::LookupEntry inventory_static_lookup_entries[EQ::vers
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,
+5 -8
View File
@@ -42,7 +42,6 @@ namespace EQ
uint32 ExpansionsMask;
int16 CharacterCreationLimit;
size_t SayLinkBodySize;
uint32 BazaarTraderLimit;
LookupEntry(const LookupEntry *lookup_entry) { }
LookupEntry(
@@ -50,15 +49,13 @@ namespace EQ
uint32 ExpansionBit,
uint32 ExpansionsMask,
int16 CharacterCreationLimit,
size_t SayLinkBodySize,
uint32 BazaarTraderLimit
size_t SayLinkBodySize
) :
Expansion(Expansion),
ExpansionBit(ExpansionBit),
ExpansionsMask(ExpansionsMask),
CharacterCreationLimit(CharacterCreationLimit),
SayLinkBodySize(SayLinkBodySize),
BazaarTraderLimit(BazaarTraderLimit)
SayLinkBodySize(SayLinkBodySize)
{ }
};
@@ -87,7 +84,7 @@ namespace EQ
int16 ViewMODPC, ViewMODBank, ViewMODSharedBank;
int16 ViewMODLimbo, AltStorage, Archived;
int16 Mail, GuildTrophyTribute, Krono;
int16 GuildBankMain,GuildBankDeposit, Other;
int16 Other;
InventoryTypeSize_Struct(
int16 Possessions, int16 Bank, int16 SharedBank,
@@ -98,7 +95,7 @@ namespace EQ
int16 ViewMODPC, int16 ViewMODBank, int16 ViewMODSharedBank,
int16 ViewMODLimbo, int16 AltStorage, int16 Archived,
int16 Mail, int16 GuildTrophyTribute, int16 Krono,
int16 GuildBankMain,int16 GuildBankDeposit, int16 Other
int16 Other
) :
Possessions(Possessions), Bank(Bank), SharedBank(SharedBank),
Trade(Trade), World(World), Limbo(Limbo),
@@ -108,7 +105,7 @@ namespace EQ
ViewMODPC(ViewMODPC), ViewMODBank(ViewMODBank), ViewMODSharedBank(ViewMODSharedBank),
ViewMODLimbo(ViewMODLimbo), AltStorage(AltStorage), Archived(Archived),
Mail(Mail), GuildTrophyTribute(GuildTrophyTribute), Krono(Krono),
GuildBankMain(GuildBankMain), GuildBankDeposit(GuildBankDeposit), Other(Other)
Other(Other)
{ }
};
+143 -828
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -545,13 +545,13 @@ void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p)
uint32 chunksize, used;
uint32 length;
if (EQEmuLogSys::Instance()->log_settings[Logs::Server_Client_Packet].is_category_enabled == 1){
if (LogSys.log_settings[Logs::Server_Client_Packet].is_category_enabled == 1){
if (p->GetOpcode() != OP_SpecialMesg){
Log(Logs::General, Logs::Server_Client_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size());
}
}
if (EQEmuLogSys::Instance()->log_settings[Logs::Server_Client_Packet_With_Dump].is_category_enabled == 1){
if (LogSys.log_settings[Logs::Server_Client_Packet_With_Dump].is_category_enabled == 1){
if (p->GetOpcode() != OP_SpecialMesg){
Log(Logs::General, Logs::Server_Client_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str());
}
+6 -6
View File
@@ -6,7 +6,7 @@
#include <string>
#include "emu_versions.h"
#include "eq_packet.h"
#include "net/reliable_stream_connection.h"
#include "net/daybreak_connection.h"
typedef enum {
ESTABLISHED,
@@ -33,18 +33,18 @@ struct EQStreamManagerInterfaceOptions
//Login I had trouble getting to recognize compression at all
//but that might be because it was still a bit buggy when i was testing that.
if (compressed) {
reliable_stream_options.encode_passes[0] = EQ::Net::EncodeCompression;
daybreak_options.encode_passes[0] = EQ::Net::EncodeCompression;
}
else if (encoded) {
reliable_stream_options.encode_passes[0] = EQ::Net::EncodeXOR;
daybreak_options.encode_passes[0] = EQ::Net::EncodeXOR;
}
reliable_stream_options.port = port;
daybreak_options.port = port;
}
int opcode_size;
bool track_opcode_stats;
EQ::Net::ReliableStreamConnectionManagerOptions reliable_stream_options;
EQ::Net::DaybreakConnectionManagerOptions daybreak_options;
};
class EQStreamManagerInterface
@@ -80,7 +80,7 @@ public:
struct Stats
{
EQ::Net::ReliableStreamConnectionStats ReliableStreamStats;
EQ::Net::DaybreakConnectionStats DaybreakStats;
int RecvCount[_maxEmuOpcode];
int SentCount[_maxEmuOpcode];
};
+3 -21
View File
@@ -94,7 +94,7 @@ void EQEmuConfig::parse_config()
auto_database_updates = true;
}
WorldIP = _root["server"]["world"]["tcp"].get("ip", "127.0.0.1").asString();
WorldIP = _root["server"]["world"]["tcp"].get("host", "127.0.0.1").asString();
WorldTCPPort = Strings::ToUnsignedInt(_root["server"]["world"]["tcp"].get("port", "9000").asString());
TelnetIP = _root["server"]["world"]["telnet"].get("ip", "127.0.0.1").asString();
@@ -147,8 +147,6 @@ void EQEmuConfig::parse_config()
QSDatabaseUsername = _root["server"]["qsdatabase"].get("username", "eq").asString();
QSDatabasePassword = _root["server"]["qsdatabase"].get("password", "eq").asString();
QSDatabaseDB = _root["server"]["qsdatabase"].get("db", "eq").asString();
QSHost = _root["server"]["queryserver"].get("host", "localhost").asString();
QSPort = Strings::ToUnsignedInt(_root["server"]["queryserver"].get("port", "9500").asString());
/**
* Zones
@@ -173,25 +171,9 @@ void EQEmuConfig::parse_config()
PluginDir = _root["server"]["directories"].get("plugins", "plugins/").asString();
LuaModuleDir = _root["server"]["directories"].get("lua_modules", "lua_modules/").asString();
PatchDir = _root["server"]["directories"].get("patches", "./").asString();
OpcodeDir = _root["server"]["directories"].get("opcodes", "./").asString();
SharedMemDir = _root["server"]["directories"].get("shared_memory", "shared/").asString();
LogDir = _root["server"]["directories"].get("logs", "logs/").asString();
auto load_paths = [&](const std::string& key, std::vector<std::string>& target) {
const auto& paths = _root["server"]["directories"][key];
if (paths.isArray()) {
for (const auto& dir : paths) {
if (dir.isString()) {
target.push_back(dir.asString());
}
}
}
};
load_paths("quest_paths", m_quest_directories);
load_paths("plugin_paths", m_plugin_directories);
load_paths("lua_module_paths", m_lua_module_directories);
/**
* Logs
*/
@@ -436,11 +418,11 @@ void EQEmuConfig::CheckUcsConfigConversion()
LogInfo("Migrating old [eqemu_config] UCS configuration to new configuration");
std::string config_file_path = std::filesystem::path{
PathManager::Instance()->GetServerPath() + "/eqemu_config.json"
path.GetServerPath() + "/eqemu_config.json"
}.string();
std::string config_file_bak_path = std::filesystem::path{
PathManager::Instance()->GetServerPath() + "/eqemu_config.ucs-migrate-json.bak"
path.GetServerPath() + "/eqemu_config.ucs-migrate-json.bak"
}.string();
// copy eqemu_config.json to eqemu_config.json.bak
+2 -26
View File
@@ -82,8 +82,6 @@ class EQEmuConfig
std::string QSDatabasePassword;
std::string QSDatabaseDB;
uint16 QSDatabasePort;
std::string QSHost;
int QSPort;
// From <files/>
std::string SpellsFile;
@@ -97,7 +95,6 @@ class EQEmuConfig
std::string PluginDir;
std::string LuaModuleDir;
std::string PatchDir;
std::string OpcodeDir;
std::string SharedMemDir;
std::string LogDir;
@@ -120,22 +117,6 @@ class EQEmuConfig
const std::string &GetUCSHost() const;
uint16 GetUCSPort() const;
std::vector<std::string> GetQuestDirectories() const
{
return m_quest_directories;
}
std::vector<std::string> GetPluginsDirectories() const
{
return m_plugin_directories;
}
std::vector<std::string> GetLuaModuleDirectories() const
{
return m_lua_module_directories;
}
// uint16 DynamicCount;
// map<string,uint16> StaticZones;
@@ -149,20 +130,15 @@ class EQEmuConfig
Json::Value _root;
static std::string ConfigFile;
std::vector<std::string> m_quest_directories = {};
std::vector<std::string> m_plugin_directories = {};
std::vector<std::string> m_lua_module_directories = {};
protected:
void parse_config();
EQEmuConfig()
{
}
virtual ~EQEmuConfig() {}
public:
virtual ~EQEmuConfig() {}
// Produce a const singleton
static const EQEmuConfig *get()
@@ -191,7 +167,7 @@ class EQEmuConfig
std::string file = fmt::format(
"{}/{}",
(file_path.empty() ? PathManager::Instance()->GetServerPath() : file_path),
(file_path.empty() ? path.GetServerPath() : file_path),
EQEmuConfig::ConfigFile
);
+10 -49
View File
@@ -25,8 +25,6 @@
#include "repositories/discord_webhooks_repository.h"
#include "repositories/logsys_categories_repository.h"
#include "termcolor/rang.hpp"
#include "path_manager.h"
#include "file.h"
#include <iostream>
#include <string>
@@ -87,7 +85,6 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
* Set Defaults
*/
log_settings[Logs::Crash].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::Crash].log_to_file = static_cast<uint8>(Logs::General);
log_settings[Logs::MySQLError].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::NPCScaling].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::HotReload].log_to_gmsay = static_cast<uint8>(Logs::General);
@@ -105,8 +102,6 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::QuestErrors].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::EqTime].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::EqTime].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::NpcHandin].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::NpcHandin].log_to_gmsay = static_cast<uint8>(Logs::General);
/**
* RFC 5424
@@ -537,11 +532,6 @@ void EQEmuLogSys::StartFileLogs(const std::string &log_name)
{
EQEmuLogSys::CloseFileLogs();
if (!File::Exists(PathManager::Instance()->GetLogPath())) {
LogInfo("Logs directory not found, creating [{}]", PathManager::Instance()->GetLogPath());
File::Makedir(PathManager::Instance()->GetLogPath());
}
/**
* When loading settings, we must have been given a reason in category based logging to output to a file in order to even create or open one...
*/
@@ -601,8 +591,6 @@ void EQEmuLogSys::SilenceConsoleLogging()
log_settings[log_index].is_category_enabled = 0;
}
log_settings[Logs::MySQLError].log_to_console = static_cast<uint8>(Logs::MySQLError);
log_settings[Logs::Error].log_to_console = static_cast<uint8>(Logs::Error);
log_settings[Logs::Crash].log_to_console = static_cast<uint8>(Logs::General);
}
@@ -614,7 +602,7 @@ void EQEmuLogSys::EnableConsoleLogging()
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
}
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
{
InjectTablesIfNotExist();
@@ -656,7 +644,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
// If we go through this whole loop and nothing is set to any debug level, there
// is no point to create a file or keep anything open
if (log_settings[c.log_category_id].log_to_file > 0) {
m_file_logs_enabled = true;
LogSys.m_file_logs_enabled = true;
}
db_categories.emplace_back(c.log_category_id);
@@ -682,33 +670,14 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
if (is_missing_in_database && !is_deprecated_category) {
LogInfo("Automatically adding new log category [{}] ({})", Logs::LogCategoryName[i], i);
auto e = LogsysCategoriesRepository::NewEntity();
e.log_category_id = i;
e.log_category_description = Strings::Escape(Logs::LogCategoryName[i]);
e.log_to_console = log_settings[i].log_to_console;
e.log_to_gmsay = log_settings[i].log_to_gmsay;
e.log_to_file = log_settings[i].log_to_file;
e.log_to_discord = log_settings[i].log_to_discord;
db_categories_to_add.emplace_back(e);
}
// look to see if the category name is different in the database
auto it = std::find_if(
categories.begin(),
categories.end(),
[i](const auto &c) { return c.log_category_id == i; }
);
if (it != categories.end()) {
if (it->log_category_description != Logs::LogCategoryName[i]) {
LogInfo(
"Updating log category [{}] ({}) to new name [{}]",
it->log_category_description,
i,
Logs::LogCategoryName[i]
);
it->log_category_description = Logs::LogCategoryName[i];
LogsysCategoriesRepository::ReplaceOne(*m_database, *it);
}
auto new_category = LogsysCategoriesRepository::NewEntity();
new_category.log_category_id = i;
new_category.log_category_description = Strings::Escape(Logs::LogCategoryName[i]);
new_category.log_to_console = log_settings[i].log_to_console;
new_category.log_to_gmsay = log_settings[i].log_to_gmsay;
new_category.log_to_file = log_settings[i].log_to_file;
new_category.log_to_discord = log_settings[i].log_to_discord;
db_categories_to_add.emplace_back(new_category);
}
}
@@ -718,10 +687,6 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
return this;
}
if (silent_load) {
SilenceConsoleLogging();
}
LogInfo("Loaded [{}] log categories", categories.size());
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
@@ -739,10 +704,6 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
if (silent_load) {
SilenceConsoleLogging();
}
return this;
}
+13 -39
View File
@@ -74,7 +74,7 @@ namespace Logs {
Spawns,
Spells,
Status, // deprecated
TCPConnection, // deprecated
TCPConnection,
Tasks,
Tradeskills,
Trading,
@@ -141,17 +141,6 @@ namespace Logs {
Zoning,
EqTime,
Corpses,
XTargets,
EvolveItem,
PositionUpdate,
KSM,
BotSettings,
BotSpellChecks,
BotSpellTypeChecks,
NpcHandin,
ZoneState,
NetClient,
NetTCP,
MaxCategoryID /* Don't Remove this */
};
@@ -185,7 +174,7 @@ namespace Logs {
"Spawns",
"Spells",
"Status (Deprecated)",
"TCP Connection (Deprecated)",
"TCP Connection",
"Tasks",
"Tradeskills",
"Trading",
@@ -194,8 +183,8 @@ namespace Logs {
"Web Interface (Deprecated)",
"World Server (Deprecated)",
"Zone Server (Deprecated)",
"MySQL Error",
"MySQL Query",
"QueryErr",
"Query",
"Mercenaries",
"Quest Debug",
"Legacy Packet Logging (Deprecated)",
@@ -218,8 +207,8 @@ namespace Logs {
"Emergency (Deprecated)",
"Alert (Deprecated)",
"Notice (Deprecated)",
"AI Scan Close",
"AI Yell For Help",
"AI Scan",
"AI Yell",
"AI CastBeneficial",
"AOE Cast",
"Entity Management",
@@ -237,7 +226,7 @@ namespace Logs {
"DialogueWindow",
"HTTP",
"Saylink",
"Checksum Verification",
"ChecksumVer",
"CombatRecord",
"Hate",
"Discord",
@@ -252,20 +241,11 @@ namespace Logs {
"Zoning",
"EqTime",
"Corpses",
"XTargets",
"EvolveItem",
"PositionUpdate",
"KSM", // Kernel Samepage Merging
"Bot Settings",
"Bot Spell Checks",
"Bot Spell Type Checks",
"NpcHandin",
"ZoneState",
"Net Server <-> Client",
"Net TCP"
};
}
#include "eqemu_logsys_log_aliases.h"
class Database;
constexpr uint16 MAX_DISCORD_WEBHOOK_ID = 300;
@@ -281,13 +261,7 @@ public:
*/
void CloseFileLogs();
EQEmuLogSys *LoadLogSettingsDefaults();
EQEmuLogSys *LoadLogDatabaseSettings(bool silent_load = false);
static EQEmuLogSys *Instance()
{
static EQEmuLogSys instance;
return &instance;
}
EQEmuLogSys *LoadLogDatabaseSettings();
/**
* @param directory_name
@@ -354,7 +328,7 @@ public:
/**
* Internally used memory reference for all log settings per category
* These are loaded via DB and have defaults loaded in LoadLogSettingsDefaults
* Database loaded via EQEmuLogSys::Instance()->SetDatabase(&database)->LoadLogDatabaseSettings();
* Database loaded via LogSys.SetDatabase(&database)->LoadLogDatabaseSettings();
*/
LogSettings log_settings[Logs::LogCategory::MaxCategoryID]{};
@@ -438,7 +412,7 @@ private:
void InjectTablesIfNotExist();
};
#include "eqemu_logsys_log_aliases.h"
extern EQEmuLogSys LogSys;
/**
template<typename... Args>
@@ -460,7 +434,7 @@ void OutF(
#define OutF(ls, debug_level, log_category, file, func, line, formatStr, ...) \
do { \
ls->Out(debug_level, log_category, file, func, line, fmt::format(formatStr, ##__VA_ARGS__).c_str()); \
ls.Out(debug_level, log_category, file, func, line, fmt::format(formatStr, ##__VA_ARGS__).c_str()); \
} while(0)
#endif
File diff suppressed because it is too large Load Diff
@@ -714,18 +714,6 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
h.charges > 1 ? fmt::format(" Charges: {}", h.charges) : "",
h.attuned ? " (Attuned)" : ""
);
for (int i = 0; i < h.augment_ids.size(); i++) {
if (!h.augment_names[i].empty()) {
const uint8 slot_id = (i + 1);
handin_items_info += fmt::format(
"Augment {}: {} ({})\n",
slot_id,
h.augment_names[i],
h.augment_ids[i]
);
}
}
}
}
@@ -739,18 +727,6 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
r.charges > 1 ? fmt::format(" Charges: {}", r.charges) : "",
r.attuned ? " (Attuned)" : ""
);
for (int i = 0; i < r.augment_ids.size(); i++) {
if (!r.augment_names[i].empty()) {
const uint8 slot_id = (i + 1);
return_items_info += fmt::format(
"Augment {}: {} ({})\n",
slot_id,
r.augment_names[i],
r.augment_ids[i]
);
}
}
}
}
@@ -813,36 +789,50 @@ std::string PlayerEventDiscordFormatter::FormatNPCHandinEvent(
);
}
std::string npc_info = fmt::format(
"{} ({})\n",
e.npc_name,
e.npc_id
);
npc_info += fmt::format(
"Is Quest Handin: {}",
e.is_quest_handin ? "Yes" : "No"
);
std::vector<DiscordField> f = {};
BuildDiscordField(&f, "NPC", npc_info);
if (!handin_items_info.empty()) {
BuildDiscordField(&f, "Handin Items", handin_items_info);
BuildDiscordField(
&f,
"Handin Items",
fmt::format(
"{}",
handin_items_info
)
);
}
if (!handin_money_info.empty()) {
BuildDiscordField(&f, "Handin Money", handin_money_info);
BuildDiscordField(
&f,
"Handin Money",
fmt::format(
"{}",
handin_money_info
)
);
}
if (!return_items_info.empty()) {
BuildDiscordField(&f, "Return Items", return_items_info);
BuildDiscordField(
&f,
"Return Items",
fmt::format(
"{}",
return_items_info
)
);
}
if (!return_money_info.empty()) {
BuildDiscordField(&f, "Return Money", return_money_info);
BuildDiscordField(
&f,
"Return Money",
fmt::format(
"{}",
return_money_info
)
);
}
std::vector<DiscordEmbed> embeds = {};
@@ -1081,51 +1071,51 @@ std::string PlayerEventDiscordFormatter::FormatTradeEvent(
if (!e.character_1_give_items.empty()) {
for (const auto &i: e.character_1_give_items) {
std::string augment_info;
if (i.augment_1_id > 0) {
if (i.aug_1_item_id > 0) {
augment_info += fmt::format(
"Augment 1: {} ({})",
i.augment_1_name,
i.augment_1_id
i.aug_1_item_name,
i.aug_1_item_id
);
}
if (i.augment_2_id > 0) {
if (i.aug_2_item_id > 0) {
augment_info += fmt::format(
"Augment 2: {} ({})",
i.augment_2_name,
i.augment_2_id
i.aug_2_item_name,
i.aug_2_item_id
);
}
if (i.augment_3_id > 0) {
if (i.aug_3_item_id > 0) {
augment_info += fmt::format(
"Augment 3: {} ({})",
i.augment_3_name,
i.augment_3_id
i.aug_3_item_name,
i.aug_3_item_id
);
}
if (i.augment_4_id > 0) {
if (i.aug_4_item_id > 0) {
augment_info += fmt::format(
"Augment 4: {} ({})\n",
i.augment_4_name,
i.augment_4_id
i.aug_4_item_name,
i.aug_4_item_id
);
}
if (i.augment_5_id > 0) {
if (i.aug_5_item_id > 0) {
augment_info += fmt::format(
"Augment 5: {} ({})\n",
i.augment_5_name,
i.augment_5_id
i.aug_5_item_name,
i.aug_5_item_id
);
}
if (i.augment_6_id > 0) {
if (i.aug_6_item_id > 0) {
augment_info += fmt::format(
"Augment 6: {} ({})",
i.augment_6_name,
i.augment_6_id
i.aug_6_item_name,
i.aug_6_item_id
);
}
@@ -1146,51 +1136,51 @@ std::string PlayerEventDiscordFormatter::FormatTradeEvent(
if (!e.character_2_give_items.empty()) {
for (const auto &i: e.character_2_give_items) {
std::string augment_info;
if (i.augment_1_id > 0) {
if (i.aug_1_item_id > 0) {
augment_info += fmt::format(
"Augment 1: {} ({})",
i.augment_1_name,
i.augment_1_id
i.aug_1_item_name,
i.aug_1_item_id
);
}
if (i.augment_2_id > 0) {
if (i.aug_2_item_id > 0) {
augment_info += fmt::format(
"Augment 2: {} ({})",
i.augment_2_name,
i.augment_2_id
i.aug_2_item_name,
i.aug_2_item_id
);
}
if (i.augment_3_id > 0) {
if (i.aug_3_item_id > 0) {
augment_info += fmt::format(
"Augment 3: {} ({})",
i.augment_3_name,
i.augment_3_id
i.aug_3_item_name,
i.aug_3_item_id
);
}
if (i.augment_4_id > 0) {
if (i.aug_4_item_id > 0) {
augment_info += fmt::format(
"Augment 4: {} ({})\n",
i.augment_4_name,
i.augment_4_id
i.aug_4_item_name,
i.aug_4_item_id
);
}
if (i.augment_5_id > 0) {
if (i.aug_5_item_id > 0) {
augment_info += fmt::format(
"Augment 5: {} ({})\n",
i.augment_5_name,
i.augment_5_id
i.aug_5_item_name,
i.aug_5_item_id
);
}
if (i.augment_6_id > 0) {
if (i.aug_6_item_id > 0) {
augment_info += fmt::format(
"Augment 6: {} ({})",
i.augment_6_name,
i.augment_6_id
i.aug_6_item_name,
i.aug_6_item_id
);
}
+20 -584
View File
@@ -1,14 +1,8 @@
#include "player_event_logs.h"
#include <cereal/archives/json.hpp>
#include "player_event_logs.h"
#include "player_event_discord_formatter.h"
#include "../platform.h"
#include "../rulesys.h"
#include "player_event_discord_formatter.h"
#include "../repositories/player_event_loot_items_repository.h"
#include "../repositories/player_event_merchant_sell_repository.h"
#include "../repositories/player_event_merchant_purchase_repository.h"
#include "../repositories/player_event_npc_handin_repository.h"
#include "../repositories/player_event_npc_handin_entries_repository.h"
const uint32 PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL = 60 * 60 * 1000; // 1 hour
@@ -17,7 +11,6 @@ void PlayerEventLogs::Init()
{
m_process_batch_events_timer.SetTimer(RuleI(Logging, BatchPlayerEventProcessIntervalSeconds) * 1000);
m_process_retention_truncation_timer.SetTimer(PROCESS_RETENTION_TRUNCATION_TIMER_INTERVAL);
m_database_ping_timer.SetTimer(10 * 1000); // 10 seconds
ValidateDatabaseConnection();
@@ -28,7 +21,6 @@ void PlayerEventLogs::Init()
m_settings[i].event_enabled = 1;
m_settings[i].retention_days = 0;
m_settings[i].discord_webhook_id = 0;
m_settings[i].etl_enabled = false;
}
SetSettingsDefaults();
@@ -73,7 +65,6 @@ void PlayerEventLogs::Init()
c.event_name = PlayerEvent::EventName[i];
c.event_enabled = m_settings[i].event_enabled;
c.retention_days = m_settings[i].retention_days;
c.etl_enabled = false;
settings_to_insert.emplace_back(c);
}
}
@@ -87,7 +78,6 @@ void PlayerEventLogs::Init()
// on initial boot process truncation
if (processing_in_world || processing_in_qs) {
LoadEtlIds();
ProcessRetentionTruncation();
}
}
@@ -131,328 +121,23 @@ void PlayerEventLogs::ProcessBatchQueue()
return;
}
static std::map<uint32, uint32> counter{};
counter.clear();
for (auto const &e: m_record_batch_queue) {
counter[e.event_type_id]++;
}
BenchTimer benchmark;
EtlQueues etl_queues{};
for (const auto &[type, count]: counter) {
if (count > 0) {
switch (type) {
case PlayerEvent::TRADE:
etl_queues.trade.reserve(count);
break;
case PlayerEvent::SPEECH:
etl_queues.speech.reserve(count);
break;
case PlayerEvent::LOOT_ITEM:
etl_queues.loot_items.reserve(count);
break;
case PlayerEvent::KILLED_NPC:
etl_queues.killed_npc.reserve(count);
break;
case PlayerEvent::NPC_HANDIN:
etl_queues.npc_handin.reserve(count);
break;
case PlayerEvent::AA_PURCHASE:
etl_queues.aa_purchase.reserve(count);
break;
case PlayerEvent::MERCHANT_SELL:
etl_queues.merchant_sell.reserve(count);
break;
case PlayerEvent::KILLED_RAID_NPC:
etl_queues.killed_raid_npc.reserve(count);
break;
case PlayerEvent::KILLED_NAMED_NPC:
etl_queues.killed_named_npc.reserve(count);
break;
case PlayerEvent::MERCHANT_PURCHASE:
etl_queues.merchant_purchase.reserve(count);
break;
default:
break;
}
}
}
// Helper to deserialize event data
auto Deserialize = [](const std::string &data, auto &out) {
if (!Strings::IsValidJson(data)) {
return;
}
// cpp exceptions are terrible, don't ever use them
try {
std::stringstream ss(data);
cereal::JSONInputArchive ar(ss);
out.serialize(ar);
}
catch (const std::exception &e) {}
};
// Helper to assign ETL table ID
auto AssignEtlId = [&](
PlayerEventLogsRepository::PlayerEventLogs& r,
PlayerEvent::EventType type
)
{
if (m_etl_settings.contains(type)) {
r.etl_table_id = m_etl_settings.at(type).next_id++;
}
};
// Define event processors
std::unordered_map<PlayerEvent::EventType, std::function<void(PlayerEventLogsRepository::PlayerEventLogs &)>> event_processors = {
{
PlayerEvent::EventType::LOOT_ITEM, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::LootItemEvent in{};
PlayerEventLootItemsRepository::PlayerEventLootItems out{};
Deserialize(r.event_data, in);
out.charges = in.charges;
out.corpse_name = in.corpse_name;
out.item_id = in.item_id;
out.item_name = in.item_name;
out.augment_1_id = in.augment_1_id;
out.augment_2_id = in.augment_2_id;
out.augment_3_id = in.augment_3_id;
out.augment_4_id = in.augment_4_id;
out.augment_5_id = in.augment_5_id;
out.augment_6_id = in.augment_6_id;
out.npc_id = in.npc_id;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::LOOT_ITEM);
etl_queues.loot_items.push_back(out);
}
},
{
PlayerEvent::EventType::MERCHANT_SELL, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::MerchantSellEvent in{};
PlayerEventMerchantSellRepository::PlayerEventMerchantSell out{};
Deserialize(r.event_data, in);
out.npc_id = in.npc_id;
out.merchant_name = in.merchant_name;
out.merchant_type = in.merchant_type;
out.item_id = in.item_id;
out.item_name = in.item_name;
out.charges = in.charges;
out.cost = in.cost;
out.alternate_currency_id = in.alternate_currency_id;
out.player_money_balance = in.player_money_balance;
out.player_currency_balance = in.player_currency_balance;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::MERCHANT_SELL);
etl_queues.merchant_sell.push_back(out);
}},
{
PlayerEvent::EventType::MERCHANT_PURCHASE, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::MerchantPurchaseEvent in{};
PlayerEventMerchantPurchaseRepository::PlayerEventMerchantPurchase out{};
Deserialize(r.event_data, in);
out.npc_id = in.npc_id;
out.merchant_name = in.merchant_name;
out.merchant_type = in.merchant_type;
out.item_id = in.item_id;
out.item_name = in.item_name;
out.charges = in.charges;
out.cost = in.cost;
out.alternate_currency_id = in.alternate_currency_id;
out.player_money_balance = in.player_money_balance;
out.player_currency_balance = in.player_currency_balance;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::MERCHANT_PURCHASE);
etl_queues.merchant_purchase.push_back(out);
}},
{
PlayerEvent::EventType::NPC_HANDIN, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::HandinEvent in{};
PlayerEventNpcHandinRepository::PlayerEventNpcHandin out{};
Deserialize(r.event_data, in);
out.npc_id = in.npc_id;
out.npc_name = in.npc_name;
out.handin_copper = in.handin_money.copper;
out.handin_silver = in.handin_money.silver;
out.handin_gold = in.handin_money.gold;
out.handin_platinum = in.handin_money.platinum;
out.return_copper = in.return_money.copper;
out.return_silver = in.return_money.silver;
out.return_gold = in.return_money.gold;
out.return_platinum = in.return_money.platinum;
out.is_quest_handin = in.is_quest_handin;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::NPC_HANDIN);
etl_queues.npc_handin.push_back(out);
for (const auto &i: in.handin_items) {
PlayerEventNpcHandinEntriesRepository::PlayerEventNpcHandinEntries entry{};
entry.player_event_npc_handin_id = r.etl_table_id;
entry.item_id = i.item_id;
entry.charges = i.charges;
entry.type = 1;
etl_queues.npc_handin_entries.push_back(entry);
}
for (const auto &i: in.return_items) {
PlayerEventNpcHandinEntriesRepository::PlayerEventNpcHandinEntries entry{};
entry.player_event_npc_handin_id = r.etl_table_id;
entry.item_id = i.item_id;
entry.charges = i.charges;
entry.type = 2;
etl_queues.npc_handin_entries.push_back(entry);
}
}},
{
PlayerEvent::EventType::TRADE, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::TradeEvent in{};
PlayerEventTradeRepository::PlayerEventTrade out{};
Deserialize(r.event_data, in);
out.char1_id = in.character_1_id;
out.char2_id = in.character_2_id;
out.char1_copper = in.character_1_give_money.copper;
out.char1_silver = in.character_1_give_money.silver;
out.char1_gold = in.character_1_give_money.gold;
out.char1_platinum = in.character_1_give_money.platinum;
out.char2_copper = in.character_2_give_money.copper;
out.char2_silver = in.character_2_give_money.silver;
out.char2_gold = in.character_2_give_money.gold;
out.char2_platinum = in.character_2_give_money.platinum;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::TRADE);
etl_queues.trade.push_back(out);
for (const auto &i: in.character_1_give_items) {
PlayerEventTradeEntriesRepository::PlayerEventTradeEntries entry{};
entry.player_event_trade_id = r.etl_table_id;
entry.char_id = in.character_1_id;
entry.item_id = i.item_id;
entry.charges = i.charges;
entry.slot = i.slot;
etl_queues.trade_entries.push_back(entry);
}
for (const auto &i: in.character_2_give_items) {
PlayerEventTradeEntriesRepository::PlayerEventTradeEntries entry{};
entry.player_event_trade_id = r.etl_table_id;
entry.char_id = in.character_2_id;
entry.item_id = i.item_id;
entry.charges = i.charges;
entry.slot = i.slot;
etl_queues.trade_entries.push_back(entry);
}
}},
{
PlayerEvent::EventType::SPEECH, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::PlayerSpeech in{};
PlayerEventSpeechRepository::PlayerEventSpeech out{};
Deserialize(r.event_data, in);
out.from_char_id = in.from;
out.to_char_id = in.to;
out.type = in.type;
out.min_status = in.min_status;
out.message = in.message;
out.guild_id = in.guild_id;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::SPEECH);
etl_queues.speech.push_back(out);
}},
{
PlayerEvent::EventType::KILLED_NPC, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::KilledNPCEvent in{};
PlayerEventKilledNpcRepository::PlayerEventKilledNpc out{};
Deserialize(r.event_data, in);
out.npc_id = in.npc_id;
out.npc_name = in.npc_name;
out.combat_time_seconds = in.combat_time_seconds;
out.total_damage_per_second_taken = in.total_damage_per_second_taken;
out.total_heal_per_second_taken = in.total_heal_per_second_taken;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::KILLED_NPC);
etl_queues.killed_npc.push_back(out);
}},
{
PlayerEvent::EventType::AA_PURCHASE, [&](PlayerEventLogsRepository::PlayerEventLogs &r) {
PlayerEvent::AAPurchasedEvent in{};
PlayerEventAaPurchaseRepository::PlayerEventAaPurchase out{};
Deserialize(r.event_data, in);
out.aa_ability_id = in.aa_id;
out.cost = in.aa_cost;
out.previous_id = in.aa_previous_id;
out.next_id = in.aa_next_id;
out.created_at = r.created_at;
AssignEtlId(r, PlayerEvent::EventType::AA_PURCHASE);
etl_queues.aa_purchase.push_back(out);
}},
};
// Process the batch queue
for (auto &r: m_record_batch_queue) {
if (m_settings[r.event_type_id].etl_enabled) {
auto it = event_processors.find(static_cast<PlayerEvent::EventType>(r.event_type_id));
if (it != event_processors.end()) {
it->second(r); // Call the appropriate lambda
}
else {
LogPlayerEventsDetail("Non-Implemented ETL routing [{}]", r.event_type_id);
}
}
}
// Helper to flush and clear queues
auto flush_queue = [&](auto insert_many, auto &queue) {
if (!queue.empty()) {
insert_many(*m_database, queue);
queue.clear();
}
};
// flush many
PlayerEventLogsRepository::InsertMany(*m_database, m_record_batch_queue);
// flush etl queues
flush_queue(PlayerEventLootItemsRepository::InsertMany, etl_queues.loot_items);
flush_queue(PlayerEventMerchantSellRepository::InsertMany, etl_queues.merchant_sell);
flush_queue(PlayerEventMerchantPurchaseRepository::InsertMany, etl_queues.merchant_purchase);
flush_queue(PlayerEventNpcHandinRepository::InsertMany, etl_queues.npc_handin);
flush_queue(PlayerEventNpcHandinEntriesRepository::InsertMany, etl_queues.npc_handin_entries);
flush_queue(PlayerEventTradeRepository::InsertMany, etl_queues.trade);
flush_queue(PlayerEventTradeEntriesRepository::InsertMany, etl_queues.trade_entries);
flush_queue(PlayerEventSpeechRepository::InsertMany, etl_queues.speech);
flush_queue(PlayerEventKilledNpcRepository::InsertMany, etl_queues.killed_npc);
flush_queue(PlayerEventKilledNamedNpcRepository::InsertMany, etl_queues.killed_named_npc);
flush_queue(PlayerEventKilledRaidNpcRepository::InsertMany, etl_queues.killed_raid_npc);
flush_queue(PlayerEventAaPurchaseRepository::InsertMany, etl_queues.aa_purchase);
LogPlayerEvents(
LogPlayerEventsDetail(
"Processing batch player event log queue of [{}] took [{}]",
m_record_batch_queue.size(),
benchmark.elapsed()
);
// empty
m_record_batch_queue.clear();
m_record_batch_queue = {};
m_batch_queue_lock.unlock();
}
// adds a player event to the queue
void PlayerEventLogs::AddToQueue(PlayerEventLogsRepository::PlayerEventLogs &log)
void PlayerEventLogs::AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &log)
{
m_batch_queue_lock.lock();
m_record_batch_queue.emplace_back(log);
@@ -509,7 +194,7 @@ bool PlayerEventLogs::IsEventDiscordEnabled(int32_t event_type_id)
}
// ensure there is a matching webhook to begin with
if (!EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
return true;
}
@@ -529,27 +214,13 @@ std::string PlayerEventLogs::GetDiscordWebhookUrlFromEventType(int32_t event_typ
}
// ensure there is a matching webhook to begin with
if (!EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
return EQEmuLogSys::Instance()->GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
if (!LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url.empty()) {
return LogSys.GetDiscordWebhooks()[m_settings[event_type_id].discord_webhook_id].webhook_url;
}
return "";
}
void PlayerEventLogs::LoadPlayerEventSettingsFromQS(
const std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings> &settings
)
{
for (const auto &e : settings) {
if (e.id >= PlayerEvent::MAX || e.id < 0) {
continue;
}
m_settings[e.id] = e;
}
LogInfo("Applied [{}] player event log settings from QS", settings.size());
}
// GM_COMMAND | [x] Implemented Formatter
// ZONING | [x] Implemented Formatter
// AA_GAIN | [x] Implemented Formatter
@@ -917,7 +588,7 @@ std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::Playe
break;
}
default: {
LogPlayerEventsDetail(
LogInfo(
"Player event [{}] ({}) Discord formatter not implemented",
e.player_event_log.event_type_name,
e.player_event_log.event_type_id
@@ -931,12 +602,7 @@ std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::Playe
// general process function, used in world or QS depending on rule Logging:PlayerEventsQSProcess
void PlayerEventLogs::Process()
{
if (m_database_ping_timer.Check()) {
m_database->ping();
}
if (m_process_batch_events_timer.Check() ||
m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
if (m_process_batch_events_timer.Check() || m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
ProcessBatchQueue();
}
@@ -947,125 +613,35 @@ void PlayerEventLogs::Process()
void PlayerEventLogs::ProcessRetentionTruncation()
{
LogPlayerEventsDetail("Running truncation");
LogPlayerEvents("Running truncation");
// Map of repository-specific deletion functions
std::unordered_map<PlayerEvent::EventType, std::function<uint32(const std::string &)>> repository_deleters = {
{
PlayerEvent::LOOT_ITEM, [&](const std::string &condition) {
return PlayerEventLootItemsRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::MERCHANT_SELL, [&](const std::string &condition) {
return PlayerEventMerchantSellRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::MERCHANT_PURCHASE, [&](const std::string &condition) {
return PlayerEventMerchantPurchaseRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::NPC_HANDIN, [&](const std::string &condition) {
uint32 deleted_count = PlayerEventNpcHandinRepository::DeleteWhere(*m_database, condition);
deleted_count += PlayerEventNpcHandinEntriesRepository::DeleteWhere(*m_database, condition);
return deleted_count;
}},
{
PlayerEvent::TRADE, [&](const std::string &condition) {
uint32 deleted_count = PlayerEventTradeRepository::DeleteWhere(*m_database, condition);
deleted_count += PlayerEventTradeEntriesRepository::DeleteWhere(*m_database, condition);
return deleted_count;
}},
{
PlayerEvent::SPEECH, [&](const std::string &condition) {
return PlayerEventSpeechRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::KILLED_NPC, [&](const std::string &condition) {
return PlayerEventKilledNpcRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::KILLED_NAMED_NPC, [&](const std::string &condition) {
return PlayerEventKilledNamedNpcRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::KILLED_RAID_NPC, [&](const std::string &condition) {
return PlayerEventKilledRaidNpcRepository::DeleteWhere(*m_database, condition);
}},
{
PlayerEvent::AA_PURCHASE, [&](const std::string &condition) {
return PlayerEventAaPurchaseRepository::DeleteWhere(*m_database, condition);
}}
};
// Group event types by retention interval
std::unordered_map<int, std::vector<int>> retention_groups;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
if (m_settings[i].retention_days > 0) {
retention_groups[m_settings[i].retention_days].push_back(i);
}
}
for (const auto &[retention_days, event_types]: retention_groups) {
std::string condition = fmt::format(
"created_at < (NOW() - INTERVAL {} DAY)",
retention_days
);
// Handle ETL deletions for each event type in the group
uint32 total_deleted_count = 0;
for (int event_type_id: event_types) {
if (m_settings[event_type_id].etl_enabled) {
auto it = repository_deleters.find(static_cast<PlayerEvent::EventType>(m_settings[event_type_id].id));
if (it != repository_deleters.end()) {
total_deleted_count += it->second(condition);
}
else {
LogError("Non-Implemented ETL Event Type [{}]", static_cast<uint32>(m_settings[event_type_id].id));
}
}
}
if (total_deleted_count > 0) {
LogInfo(
"Truncated [{}] ETL events older than [{}] days",
total_deleted_count,
retention_days
);
}
// Batch deletion for player_event_logs
std::string event_type_ids = fmt::format(
"({})",
fmt::join(event_types, ", ")
);
uint32 deleted_count = PlayerEventLogsRepository::DeleteWhere(
int deleted_count = PlayerEventLogsRepository::DeleteWhere(
*m_database,
fmt::format(
"event_type_id IN {} AND {}",
event_type_ids,
condition
"event_type_id = {} AND created_at < (NOW() - INTERVAL {} DAY)",
i,
m_settings[i].retention_days
)
);
if (deleted_count > 0) {
LogInfo(
"Truncated [{}] events of types [{}] older than [{}] days",
"Truncated [{}] events of type [{}] ({}) older than [{}] days",
deleted_count,
event_type_ids,
retention_days
PlayerEvent::EventName[i],
i,
m_settings[i].retention_days
);
}
}
}
}
void PlayerEventLogs::ReloadSettings()
{
for (auto &e: PlayerEventLogSettingsRepository::All(*m_database)) {
if (e.id >= PlayerEvent::MAX || e.id < 0) {
continue;
}
m_settings[e.id] = e;
}
}
@@ -1123,148 +699,8 @@ void PlayerEventLogs::SetSettingsDefaults()
m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1;
m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM].event_enabled = 1;
m_settings[PlayerEvent::GUILD_TRIBUTE_DONATE_PLAT].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1;
m_settings[PlayerEvent::EVOLVE_ITEM].event_enabled = 1;
m_settings[PlayerEvent::SPEECH].event_enabled = 0;
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
}
}
void PlayerEventLogs::LoadEtlIds()
{
auto e = [&](auto p) -> bool {
for (PlayerEventLogSettingsRepository::PlayerEventLogSettings const &c: m_settings) {
if (c.id == p) {
return c.etl_enabled ? true : false;
}
}
return false;
};
m_etl_settings.clear();
m_etl_settings = {
{
PlayerEvent::LOOT_ITEM,
{
.enabled = e(PlayerEvent::LOOT_ITEM),
.table_name = "player_event_loot_items",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventLootItemsRepository::TableName()))
}
},
{
PlayerEvent::MERCHANT_SELL,
{
.enabled = e(PlayerEvent::MERCHANT_SELL),
.table_name = "player_event_merchant_sell",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventMerchantSellRepository::TableName()))
}
},
{
PlayerEvent::MERCHANT_PURCHASE,
{
.enabled = e(PlayerEvent::MERCHANT_PURCHASE),
.table_name = "player_event_merchant_purchase",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventMerchantPurchaseRepository::TableName()))
}
},
{
PlayerEvent::NPC_HANDIN,
{
.enabled = e(PlayerEvent::NPC_HANDIN),
.table_name = "player_event_npc_handin",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventNpcHandinRepository::TableName()))
}
},
{
PlayerEvent::TRADE,
{
.enabled = e(PlayerEvent::TRADE),
.table_name = "player_event_trade",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventTradeRepository::TableName()))
}
},
{
PlayerEvent::SPEECH,
{
.enabled = e(PlayerEvent::SPEECH),
.table_name = "player_event_speech",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventSpeechRepository::TableName()))
}
},
{
PlayerEvent::KILLED_NPC,
{
.enabled = e(PlayerEvent::KILLED_NPC),
.table_name = "player_event_killed_npc",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventKilledNpcRepository::TableName()))
}
},
{
PlayerEvent::KILLED_NAMED_NPC,
{
.enabled = e(PlayerEvent::KILLED_NAMED_NPC),
.table_name = "player_event_killed_named_npc",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventKilledNamedNpcRepository::TableName()))
}
},
{
PlayerEvent::KILLED_RAID_NPC,
{
.enabled = e(PlayerEvent::KILLED_RAID_NPC),
.table_name = "player_event_killed_raid_npc",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventKilledRaidNpcRepository::TableName()))
}
},
{
PlayerEvent::AA_PURCHASE,
{
.enabled = e(PlayerEvent::AA_PURCHASE),
.table_name = "player_event_aa_purchase",
.next_id = static_cast<int64>(m_database->GetNextTableId(PlayerEventAaPurchaseRepository::TableName()))
}
}
};
for (auto &e: m_etl_settings) {
LogPlayerEventsDetail(
"ETL Settings [{}] Enabled [{}] Table [{}] NextId [{}]",
PlayerEvent::EventName[e.first],
e.second.enabled,
e.second.table_name,
e.second.next_id
);
}
}
bool PlayerEventLogs::LoadDatabaseConnection()
{
const auto c = EQEmuConfig::get();
LogInfo(
"Connecting to MySQL for PlayerEvents [{}]@[{}]:[{}]",
c->DatabaseUsername.c_str(),
c->DatabaseHost.c_str(),
c->DatabasePort
);
if (!player_event_database.Connect(
c->DatabaseHost.c_str(),
c->DatabaseUsername.c_str(),
c->DatabasePassword.c_str(),
c->DatabaseDB.c_str(),
c->DatabasePort
)) {
LogError("Cannot continue without a database connection for player events.");
return false;
}
SetDatabase(&player_event_database);
return true;
}
+9 -62
View File
@@ -1,38 +1,19 @@
#ifndef EQEMU_PLAYER_EVENT_LOGS_H
#define EQEMU_PLAYER_EVENT_LOGS_H
#include "../repositories/player_event_log_settings_repository.h"
#include "player_events.h"
#include "../servertalk.h"
#include "../repositories/player_event_logs_repository.h"
#include "../timer.h"
#include "../json/json_archive_single_line.h"
#include <cereal/archives/json.hpp>
#include <mutex>
#include "../json/json_archive_single_line.h"
#include "../servertalk.h"
#include "../timer.h"
#include "../eqemu_config.h"
#include "../repositories/player_event_log_settings_repository.h"
#include "../repositories/player_event_logs_repository.h"
#include "../repositories/player_event_loot_items_repository.h"
#include "../repositories/player_event_merchant_purchase_repository.h"
#include "../repositories/player_event_merchant_sell_repository.h"
#include "../repositories/player_event_npc_handin_repository.h"
#include "../repositories/player_event_npc_handin_entries_repository.h"
#include "../repositories/player_event_trade_repository.h"
#include "../repositories/player_event_trade_entries_repository.h"
#include "../repositories/player_event_speech_repository.h"
#include "../repositories/player_event_killed_npc_repository.h"
#include "../repositories/player_event_killed_named_npc_repository.h"
#include "../repositories/player_event_killed_raid_npc_repository.h"
#include "../repositories/player_event_aa_purchase_repository.h"
class PlayerEventLogs {
public:
Database player_event_database{};
void Init();
bool LoadDatabaseConnection();
void ReloadSettings();
void LoadEtlIds();
PlayerEventLogs *SetDatabase(Database *db);
bool ValidateDatabaseConnection();
bool IsEventEnabled(PlayerEvent::EventType event);
@@ -40,7 +21,7 @@ public:
void Process();
// batch queue
void AddToQueue(PlayerEventLogsRepository::PlayerEventLogs &logs);
void AddToQueue(const PlayerEventLogsRepository::PlayerEventLogs &logs);
// main event record generic function
// can ingest any struct event types
@@ -77,38 +58,8 @@ public:
bool IsEventDiscordEnabled(int32_t event_type_id);
std::string GetDiscordWebhookUrlFromEventType(int32_t event_type_id);
void LoadPlayerEventSettingsFromQS(const std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings>& settings);
static std::string GetDiscordPayloadFromEvent(const PlayerEvent::PlayerEventContainer &e);
struct EtlQueues {
std::vector<PlayerEventLootItemsRepository::PlayerEventLootItems> loot_items;
std::vector<PlayerEventMerchantPurchaseRepository::PlayerEventMerchantPurchase> merchant_purchase;
std::vector<PlayerEventMerchantSellRepository::PlayerEventMerchantSell> merchant_sell;
std::vector<PlayerEventNpcHandinRepository::PlayerEventNpcHandin> npc_handin;
std::vector<PlayerEventNpcHandinEntriesRepository::PlayerEventNpcHandinEntries> npc_handin_entries;
std::vector<PlayerEventTradeRepository::PlayerEventTrade> trade;
std::vector<PlayerEventTradeEntriesRepository::PlayerEventTradeEntries> trade_entries;
std::vector<PlayerEventSpeechRepository::PlayerEventSpeech> speech;
std::vector<PlayerEventKilledNpcRepository::PlayerEventKilledNpc> killed_npc;
std::vector<PlayerEventKilledNamedNpcRepository::PlayerEventKilledNamedNpc> killed_named_npc;
std::vector<PlayerEventKilledRaidNpcRepository::PlayerEventKilledRaidNpc> killed_raid_npc;
std::vector<PlayerEventAaPurchaseRepository::PlayerEventAaPurchase> aa_purchase;
};
static PlayerEventLogs* Instance()
{
static PlayerEventLogs instance;
return &instance;
}
private:
struct EtlSettings {
bool enabled;
std::string table_name;
int64 next_id;
};
Database *m_database; // reference to database
PlayerEventLogSettingsRepository::PlayerEventLogSettings m_settings[PlayerEvent::EventType::MAX]{};
@@ -118,10 +69,7 @@ private:
static std::unique_ptr<ServerPacket>
BuildPlayerEventPacket(const PlayerEvent::PlayerEventContainer &e);
std::map<PlayerEvent::EventType, EtlSettings> m_etl_settings{};
// timers
Timer m_database_ping_timer; // database ping timer
Timer m_process_batch_events_timer; // events processing timer
Timer m_process_retention_truncation_timer; // timer for truncating events based on retention settings
@@ -130,9 +78,8 @@ private:
void ProcessBatchQueue();
void ProcessRetentionTruncation();
void SetSettingsDefaults();
public:
std::map<PlayerEvent::EventType, EtlSettings> &GetEtlSettings() { return m_etl_settings;}
};
extern PlayerEventLogs player_event_logs;
#endif //EQEMU_PLAYER_EVENT_LOGS_H
File diff suppressed because it is too large Load Diff
-330
View File
@@ -1,330 +0,0 @@
#include "evolving_items.h"
#include "item_instance.h"
#include "events/player_event_logs.h"
#include "repositories/character_evolving_items_repository.h"
EvolvingItemsManager::EvolvingItemsManager()
{
m_db = nullptr;
m_content_db = nullptr;
}
void EvolvingItemsManager::LoadEvolvingItems() const
{
auto const &results = ItemsEvolvingDetailsRepository::All(*m_content_db);
if (results.empty()) {
return;
}
std::ranges::transform(
results.begin(),
results.end(),
std::inserter(
EvolvingItemsManager::Instance()->GetEvolvingItemsCache(),
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().end()
),
[](const ItemsEvolvingDetailsRepository::ItemsEvolvingDetails &x) {
return std::make_pair(x.item_id, x);
}
);
}
void EvolvingItemsManager::SetDatabase(Database *db)
{
m_db = db;
}
void EvolvingItemsManager::SetContentDatabase(Database *db)
{
m_content_db = db;
}
double EvolvingItemsManager::CalculateProgression(const uint64 current_amount, const uint32 item_id)
{
if (!EvolvingItemsManager::Instance()->GetEvolvingItemsCache().contains(item_id)) {
return 0;
}
return EvolvingItemsManager::Instance()->GetEvolvingItemsCache().at(item_id).required_amount > 0
? static_cast<double>(current_amount)
/ static_cast<double>(EvolvingItemsManager::Instance()->GetEvolvingItemsCache().at(item_id).required_amount) * 100
: 0;
}
void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_id, const EQ::ItemInstance &inst) const
{
if (!inst) {
return;
}
inst.SetEvolveEquipped(false);
if (inst.IsEvolving() && slot_id <= EQ::invslot::EQUIPMENT_END && slot_id >= EQ::invslot::EQUIPMENT_BEGIN) {
inst.SetEvolveEquipped(true);
}
if (!inst.IsEvolving()) {
return;
}
if (!inst.GetEvolveUniqueID()) {
auto e = CharacterEvolvingItemsRepository::NewEntity();
e.character_id = char_id;
e.item_id = inst.GetID();
e.equipped = inst.GetEvolveEquipped();
e.final_item_id = EvolvingItemsManager::Instance()->GetFinalItemID(inst);
if (inst.GetEvolveCurrentAmount() > 0) {
e.current_amount = inst.GetEvolveCurrentAmount();
inst.CalculateEvolveProgression();
}
auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e);
e.id = r.id;
inst.SetEvolveUniqueID(e.id);
inst.SetEvolveCharID(e.character_id);
inst.SetEvolveItemID(e.item_id);
inst.SetEvolveFinalItemID(e.final_item_id);
return;
}
CharacterEvolvingItemsRepository::SetEquipped(*m_db, inst.GetEvolveUniqueID(), inst.GetEvolveEquipped());
}
uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const
{
if (!inst) {
return 0;
}
const auto start_iterator = std::ranges::find_if(
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cbegin(),
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
return a.second.item_evo_id == inst.GetEvolveLoreID();
}
);
if (start_iterator == std::end(EvolvingItemsManager::Instance()->GetEvolvingItemsCache())) {
return 0;
}
const auto final_id = std::ranges::max_element(
start_iterator,
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
[&](
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a,
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &b
) {
return a.second.item_evo_id == b.second.item_evo_id &&
a.second.item_evolve_level < b.second.item_evolve_level;
}
);
return final_id->first;
}
uint32 EvolvingItemsManager::GetNextEvolveItemID(const EQ::ItemInstance &inst) const
{
if (!inst) {
return 0;
}
int8 const current_level = inst.GetEvolveLvl();
const auto iterator = std::ranges::find_if(
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cbegin(),
EvolvingItemsManager::Instance()->GetEvolvingItemsCache().cend(),
[&](const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a) {
return a.second.item_evo_id == inst.GetEvolveLoreID() &&
a.second.item_evolve_level == current_level + 1;
}
);
if (iterator == std::end(EvolvingItemsManager::Instance()->GetEvolvingItemsCache())) {
return 0;
}
return iterator->first;
}
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails EvolvingItemsManager::GetEvolveItemDetails(const uint64 unique_id)
{
if (GetEvolvingItemsCache().contains(unique_id)) {
return GetEvolvingItemsCache().at(unique_id);
}
return ItemsEvolvingDetailsRepository::NewEntity();
}
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> EvolvingItemsManager::GetEvolveIDItems(
const uint32 evolve_id
)
{
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> e{};
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_evo_id == evolve_id) {
e.push_back(value);
}
}
std::ranges::sort(
e.begin(),
e.end(),
[&](
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &a,
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &b
) {
return a.item_evolve_level < b.item_evolve_level;
}
);
return e;
}
uint64 EvolvingItemsManager::GetTotalEarnedXP(const EQ::ItemInstance &inst)
{
if (!inst) {
return 0;
}
uint64 xp = inst.GetEvolveCurrentAmount();
auto evolve_id_item_cache = GetEvolveIDItems(inst.GetEvolveLoreID());
auto current_level = inst.GetEvolveLvl();
for (auto const &i: evolve_id_item_cache) {
if (i.item_evolve_level < current_level) {
xp += i.required_amount;
}
}
return xp;
}
EvolveGetNextItem EvolvingItemsManager::GetNextItemByXP(const EQ::ItemInstance &inst_in, const int64 in_xp)
{
EvolveGetNextItem ets{};
if (!inst_in) {
return ets;
}
const auto evolve_items = GetEvolveIDItems(inst_in.GetEvolveLoreID());
uint32 max_transfer_level = 0;
int64 xp = in_xp;
for (auto const &e: evolve_items) {
if (e.item_evolve_level < inst_in.GetEvolveLvl()) {
continue;
}
int64 have = 0;
if (e.item_evolve_level == inst_in.GetEvolveLvl()) {
have = inst_in.GetEvolveCurrentAmount();
}
const auto required = e.required_amount;
const int64 need = required - have;
const int64 balance = xp - need;
if (balance <= 0) {
ets.new_current_amount = have + xp;
ets.new_item_id = e.item_id;
ets.from_current_amount = 0;
ets.max_transfer_level = max_transfer_level;
return ets;
}
xp = balance;
max_transfer_level += 1;
ets.new_current_amount = required;
ets.new_item_id = e.item_id;
ets.from_current_amount = balance - required;
ets.max_transfer_level = max_transfer_level;
}
return ets;
}
EvolveTransfer EvolvingItemsManager::DetermineTransferResults(
const EQ::ItemInstance &inst_from,
const EQ::ItemInstance &inst_to
)
{
EvolveTransfer ets{};
if (!inst_from || !inst_to) {
return ets;
}
auto evolving_details_inst_from = EvolvingItemsManager::Instance()->GetEvolveItemDetails(inst_from.GetID());
auto evolving_details_inst_to = EvolvingItemsManager::Instance()->GetEvolveItemDetails(inst_to.GetID());
if (!evolving_details_inst_from.id || !evolving_details_inst_to.id) {
return ets;
}
if (evolving_details_inst_from.type == evolving_details_inst_to.type) {
uint32 compatibility = 0;
uint64 xp = 0;
if (evolving_details_inst_from.sub_type == evolving_details_inst_to.sub_type) {
compatibility = 100;
}
else {
compatibility = 30;
}
xp = EvolvingItemsManager::Instance()->GetTotalEarnedXP(inst_from) * compatibility / 100;
auto results = EvolvingItemsManager::Instance()->GetNextItemByXP(inst_to, xp);
ets.item_from_id = EvolvingItemsManager::Instance()->GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID());
ets.item_from_current_amount = results.from_current_amount;
ets.item_to_id = results.new_item_id;
ets.item_to_current_amount = results.new_current_amount;
ets.compatibility = compatibility;
ets.max_transfer_level = results.max_transfer_level;
}
return ets;
}
uint32 EvolvingItemsManager::GetFirstItemInLoreGroup(const uint32 lore_id)
{
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_evo_id == lore_id && value.item_evolve_level == 1) {
return key;
}
}
return 0;
}
uint32 EvolvingItemsManager::GetFirstItemInLoreGroupByItemID(const uint32 item_id)
{
for (auto const &[key, value]: GetEvolvingItemsCache()) {
if (value.item_id == item_id) {
for (auto const &[key2, value2]: GetEvolvingItemsCache()) {
if (value2.item_evo_id == value.item_evo_id && value2.item_evolve_level == 1) {
return key;
}
}
}
}
return 0;
}
void EvolvingItemsManager::LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e)
{
if (!inst) {
return;
}
e.item_id = inst.GetID();
e.item_name = inst.GetItem() ? inst.GetItem()->Name : std::string();
e.level = inst.GetEvolveLvl();
e.progression = inst.GetEvolveProgression();
e.unique_id = inst.GetEvolveUniqueID();
}
-70
View File
@@ -1,70 +0,0 @@
#ifndef EVOLVING_H
#define EVOLVING_H
#include "shareddb.h"
#include "events/player_events.h"
#include "repositories/items_evolving_details_repository.h"
namespace EQ {
class ItemInstance;
}
namespace EvolvingItems {
namespace Actions {
constexpr int8 UPDATE_ITEMS = 0;
constexpr int8 TRANSFER_WINDOW_OPEN = 1;
constexpr int8 TRANSFER_WINDOW_DETAILS = 2;
constexpr int8 TRANSFER_XP = 3;
constexpr int8 FINAL_RESULT = 4;
}
namespace Types {
constexpr int8 AMOUNT_OF_EXP = 1;
constexpr int8 NUMBER_OF_KILLS = 2;
constexpr int8 SPECIFIC_MOB_RACE = 3;
constexpr int8 SPECIFIC_ZONE_ID = 4;
}
namespace SubTypes {
constexpr int8 ALL_EXP = 0;
constexpr int8 SOLO_EXP = 1;
constexpr int8 GROUP_EXP = 2;
constexpr int8 RAID_EXP = 3;
}
}
class EvolvingItemsManager
{
public:
EvolvingItemsManager();
void SetDatabase(Database *db);
void SetContentDatabase(Database *db);
void LoadEvolvingItems() const;
void DoLootChecks(uint32 char_id, uint16 slot_id, const EQ::ItemInstance &inst) const;
uint32 GetFinalItemID(const EQ::ItemInstance &inst) const;
uint32 GetNextEvolveItemID(const EQ::ItemInstance &inst) const;
uint32 GetFirstItemInLoreGroup(uint32 lore_id);
uint32 GetFirstItemInLoreGroupByItemID(uint32 item_id);
uint64 GetTotalEarnedXP(const EQ::ItemInstance &inst);
static double CalculateProgression(uint64 current_amount, uint32 item_id);
static void LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e);
ItemsEvolvingDetailsRepository::ItemsEvolvingDetails GetEvolveItemDetails(uint64 id);
EvolveTransfer DetermineTransferResults(const EQ::ItemInstance& inst_from, const EQ::ItemInstance& inst_to);
EvolveGetNextItem GetNextItemByXP(const EQ::ItemInstance &inst_in, int64 in_xp);
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails>& GetEvolvingItemsCache() { return m_evolving_items_cache; }
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> GetEvolveIDItems(uint32 evolve_id);
static EvolvingItemsManager* Instance()
{
static EvolvingItemsManager instance;
return &instance;
}
private:
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> m_evolving_items_cache;
Database * m_db;
Database * m_content_db;
};
#endif //EVOLVING_H
+101
View File
@@ -0,0 +1,101 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "expedition_lockout_timer.h"
#include "../common/strings.h"
#include "../common/rulesys.h"
#include "../common/util/uuid.h"
#include <fmt/format.h>
const char* const DZ_REPLAY_TIMER_NAME = "Replay Timer"; // see December 14, 2016 patch notes
ExpeditionLockoutTimer::ExpeditionLockoutTimer(
std::string expedition_uuid, std::string expedition_name,
std::string event_name, uint64_t expire_time, uint32_t duration
) :
m_expedition_uuid{std::move(expedition_uuid)},
m_expedition_name{std::move(expedition_name)},
m_event_name{std::move(event_name)},
m_expire_time(std::chrono::system_clock::from_time_t(expire_time)),
m_duration(duration)
{
if (m_event_name == DZ_REPLAY_TIMER_NAME)
{
m_is_replay_timer = true;
}
}
ExpeditionLockoutTimer ExpeditionLockoutTimer::CreateLockout(
const std::string& expedition_name, const std::string& event_name, uint32_t seconds, std::string uuid)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
if (uuid.empty())
{
uuid = EQ::Util::UUID::Generate().ToString();
}
ExpeditionLockoutTimer lockout{uuid, expedition_name, event_name, 0, seconds};
lockout.Reset(); // sets expire time
return lockout;
}
uint32_t ExpeditionLockoutTimer::GetSecondsRemaining() const
{
auto now = std::chrono::system_clock::now();
if (m_expire_time > now)
{
auto remaining = m_expire_time - now;
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::seconds>(remaining).count());
}
return 0;
}
ExpeditionLockoutTimer::DaysHoursMinutes ExpeditionLockoutTimer::GetDaysHoursMinutesRemaining() const
{
auto seconds = GetSecondsRemaining();
return ExpeditionLockoutTimer::DaysHoursMinutes{
fmt::format_int(seconds / 86400).str(), // days
fmt::format_int((seconds / 3600) % 24).str(), // hours
fmt::format_int((seconds / 60) % 60).str() // minutes
};
}
bool ExpeditionLockoutTimer::IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const
{
return compare_lockout.IsSameLockout(GetExpeditionName(), GetEventName());
}
bool ExpeditionLockoutTimer::IsSameLockout(
const std::string& expedition_name, const std::string& event_name) const
{
return GetExpeditionName() == expedition_name && GetEventName() == event_name;
}
void ExpeditionLockoutTimer::AddLockoutTime(int seconds)
{
seconds = static_cast<uint32_t>(seconds * RuleR(Expedition, LockoutDurationMultiplier));
auto new_duration = std::max(0, static_cast<int>(m_duration.count()) + seconds);
auto start_time = m_expire_time - m_duration;
m_duration = std::chrono::seconds(new_duration);
m_expire_time = start_time + m_duration;
}
+76
View File
@@ -0,0 +1,76 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2020 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* 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; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EXPEDITION_LOCKOUT_TIMER_H
#define EXPEDITION_LOCKOUT_TIMER_H
#include <chrono>
#include <string>
extern const char* const DZ_REPLAY_TIMER_NAME;
class ExpeditionLockoutTimer
{
public:
ExpeditionLockoutTimer() = default;
ExpeditionLockoutTimer(
std::string expedition_uuid, std::string expedition_name,
std::string event_name, uint64_t expire_time, uint32_t duration);
static ExpeditionLockoutTimer CreateLockout(
const std::string& expedition_name, const std::string& event_name,
uint32_t seconds, std::string uuid = {});
struct DaysHoursMinutes
{
std::string days;
std::string hours;
std::string mins;
};
void AddLockoutTime(int seconds);
uint32_t GetDuration() const { return static_cast<uint32_t>(m_duration.count()); }
uint64_t GetExpireTime() const { return std::chrono::system_clock::to_time_t(m_expire_time); }
uint64_t GetStartTime() const { return std::chrono::system_clock::to_time_t(m_expire_time - m_duration); }
uint32_t GetSecondsRemaining() const;
DaysHoursMinutes GetDaysHoursMinutesRemaining() const;
const std::string& GetExpeditionName() const { return m_expedition_name; }
const std::string& GetExpeditionUUID() const { return m_expedition_uuid; }
const std::string& GetEventName() const { return m_event_name; }
bool IsExpired() const { return GetSecondsRemaining() == 0; }
bool IsFromExpedition(const std::string& uuid) const { return uuid == m_expedition_uuid; }
bool IsReplayTimer() const { return m_is_replay_timer; }
bool IsSameLockout(const ExpeditionLockoutTimer& compare_lockout) const;
bool IsSameLockout(const std::string& expedition_name, const std::string& event_name) const;
void Reset() { m_expire_time = std::chrono::system_clock::now() + m_duration; }
void SetDuration(uint32_t seconds) { m_duration = std::chrono::seconds(seconds); }
void SetExpireTime(uint64_t expire_time) { m_expire_time = std::chrono::system_clock::from_time_t(expire_time); }
void SetUUID(const std::string& uuid) { m_expedition_uuid = uuid; }
private:
bool m_is_replay_timer = false;
std::string m_expedition_uuid; // expedition received in
std::string m_expedition_name;
std::string m_event_name;
std::chrono::seconds m_duration;
std::chrono::time_point<std::chrono::system_clock> m_expire_time;
};
#endif
+29 -29
View File
@@ -95,12 +95,12 @@ bool IsOfEqualRace(int r1, int r2)
}
// TODO: add more values
switch (r1) {
case Race::DarkElf:
case DARK_ELF:
if (r2 == Race::NeriakCitizen) {
return true;
}
break;
case Race::Barbarian:
case BARBARIAN:
if (r2 == Race::HalasCitizen) {
return true;
}
@@ -116,49 +116,49 @@ bool IsOfIndiffRace(int r1, int r2)
}
// TODO: add more values
switch (r1) {
case Race::DarkElf:
case Race::Ogre:
case Race::Troll:
if (r2 == Race::Ogre || r2 == Race::Troll || r2 == Race::DarkElf) {
case DARK_ELF:
case OGRE:
case TROLL:
if (r2 == OGRE || r2 == TROLL || r2 == DARK_ELF) {
return true;
}
break;
case Race::Human:
case Race::Barbarian:
case Race::HalfElf:
case Race::Gnome:
case Race::Halfling:
case Race::WoodElf:
if (r2 == Race::Human ||
r2 == Race::Barbarian ||
r2 == Race::Erudite ||
r2 == Race::HalfElf ||
r2 == Race::Gnome ||
r2 == Race::Halfling ||
r2 == Race::Dwarf ||
r2 == Race::HighElf ||
r2 == Race::WoodElf) {
case HUMAN:
case BARBARIAN:
case HALF_ELF:
case GNOME:
case HALFLING:
case WOOD_ELF:
if (r2 == HUMAN ||
r2 == BARBARIAN ||
r2 == ERUDITE ||
r2 == HALF_ELF ||
r2 == GNOME ||
r2 == HALFLING ||
r2 == DWARF ||
r2 == HIGH_ELF ||
r2 == WOOD_ELF) {
return true;
}
break;
case Race::Erudite:
if (r2 == Race::Human || r2 == Race::HalfElf) {
case ERUDITE:
if (r2 == HUMAN || r2 == HALF_ELF) {
return true;
}
break;
case Race::Dwarf:
if (r2 == Race::Halfling || r2 == Race::Gnome) {
case DWARF:
if (r2 == HALFLING || r2 == GNOME) {
return true;
}
break;
case Race::HighElf:
if (r2 == Race::WoodElf) {
case HIGH_ELF:
if (r2 == WOOD_ELF) {
return true;
}
break;
case Race::VahShir:
case VAHSHIR:
return true;
case Race::Iksar:
case IKSAR:
return false;
}
return false;
+13 -12
View File
@@ -39,7 +39,6 @@
#include <filesystem>
#include <iostream>
#include <sys/stat.h>
#include <vector>
namespace fs = std::filesystem;
@@ -91,21 +90,23 @@ std::string File::GetCwd()
FileContentsResult File::GetContents(const std::string &file_name)
{
std::ifstream f(file_name, std::ios::in | std::ios::binary);
if (!f) {
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
}
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
std::string error;
std::ifstream f;
f.open(file_name);
std::string line;
std::string lines;
std::vector<char> buffer(CHUNK_SIZE);
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
lines.append(buffer.data(), f.gcount());
if (f.is_open()) {
while (f) {
std::getline(f, line);
lines += line + "\n";
}
}
else {
error = fmt::format("Couldn't open file [{}]", file_name);
}
return FileContentsResult{
.contents = lines,
.error = {}
.error = error,
};
}
+76 -57
View File
@@ -92,20 +92,10 @@ BaseGuildManager::~BaseGuildManager()
bool BaseGuildManager::LoadGuilds()
{
ClearGuilds();
auto guilds = GuildsRepository::GetWhere(
*m_db,
fmt::format("`id` < '{}'", RoF2::constants::MAX_GUILD_ID)
);
auto guilds_ranks = GuildRanksRepository::LoadAll(*m_db);
auto guilds_permissions = GuildPermissionsRepository::LoadAll(*m_db);
auto guilds_tributes = GuildTributesRepository::GetWhere(
*m_db,
fmt::format(
"`guild_id` < '{}'",
RoF2::constants::MAX_GUILD_ID
)
);
auto guilds = GuildsRepository::All(*m_db);
auto guilds_ranks = GuildRanksRepository::All(*m_db);
auto guilds_permissions = GuildPermissionsRepository::All(*m_db);
auto guilds_tributes = GuildTributesRepository::All(*m_db);
if (guilds.empty()) {
LogGuilds("No Guilds found in database.");
@@ -118,28 +108,23 @@ bool BaseGuildManager::LoadGuilds()
_CreateGuild(g.id, g.name, g.leader, g.minstatus, g.motd, g.motd_setter, g.channel, g.url, g.favor);
for (int i = 1; i <= GUILD_MAX_RANK; i++) {
auto key = fmt::format("{}-{}", g.id, i);
if (guilds_ranks.contains(key)) {
m_guilds[g.id]->rank_names[i] = guilds_ranks.find(key)->second;
for (auto const &r: guilds_ranks) {
if (r.guild_id == g.id) {
m_guilds[g.id]->rank_names[r.rank_] = r.title;
}
}
auto count = 0;
for (int i = 1; i <= GUILD_MAX_FUNCTIONS; i++) {
auto key = fmt::format("{}-{}", g.id, i);
if (guilds_permissions.contains(key)) {
auto p = guilds_permissions.find(key)->second;
for (auto const &p: guilds_permissions) {
if (p.guild_id == g.id) {
m_guilds[g.id]->functions[p.perm_id].id = p.id;
m_guilds[g.id]->functions[p.perm_id].guild_id = p.guild_id;
m_guilds[g.id]->functions[p.perm_id].perm_id = p.perm_id;
m_guilds[g.id]->functions[p.perm_id].perm_value = p.permission;
}
count++;
}
}
if (count < GUILD_MAX_FUNCTIONS) {
store_to_db = true;
@@ -357,12 +342,39 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id)
return true;
}
uint32 BaseGuildManager::_GetFreeGuildID()
{
GuildsRepository::DeleteWhere(*m_db, "`name` = ''");
GuildsRepository::Guilds out;
out.id = 0;
out.leader = 0;
out.minstatus = 0;
out.tribute = 0;
out.name = "";
out.motd = "";
out.motd_setter = "";
out.url = "";
out.channel = "";
auto last_insert_id = GuildsRepository::InsertOne(*m_db, out);
if (last_insert_id.id > 0) {
LogGuilds("Located a free guild ID [{}] in the database", last_insert_id.id);
return last_insert_id.id;
}
LogGuilds("Unable to find a free guild ID in the database");
return GUILD_NONE;
}
uint32 BaseGuildManager::CreateGuild(std::string name, uint32 leader_char_id)
{
uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id);
if (guild_id == GUILD_NONE) {
return (GUILD_NONE);
}
//RefreshGuild(guild_id);
//SendGuildRefresh(guild_id, true, false, false, false);
//SendCharRefresh(GUILD_NONE, guild_id, leader_char_id);
return guild_id;
}
@@ -527,8 +539,8 @@ bool BaseGuildManager::SetPublicNote(uint32 charid, std::string public_note)
uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
{
auto new_id = GuildsRepository::GetMaxId(*m_db) + 1;
if (!new_id) {
auto new_id = _GetFreeGuildID();
if (new_id == GUILD_NONE) {
return GUILD_NONE;
}
@@ -547,54 +559,52 @@ uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
bool BaseGuildManager::UpdateDbDeleteGuild(uint32 guild_id, bool local_delete, bool db_delete)
{
auto const where_filter = fmt::format("guild_id = {}", guild_id);
auto const bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (local_delete) {
auto where_filter = fmt::format("guildid = {}", guild_id);
auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (!bank_items.empty()) {
LogError(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
guild_id,
bank_items.size()
);
LogGuilds(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
guild_id,
bank_items.size()
);
return false;
}
auto res = m_guilds.find(guild_id);
else {
std::map<uint32, GuildInfo *>::iterator res;
res = m_guilds.find(guild_id);
if (res != m_guilds.end()) {
safe_delete(res->second);
delete res->second;
m_guilds.erase(res);
LogGuilds("Deleted guild [{}] from memory", guild_id);
//Does this need to be sent to world?
}
}
}
if (db_delete) {
auto where_filter = fmt::format("guildid = {}", guild_id);
auto bank_items = GuildBankRepository::GetWhere(*m_db, where_filter);
if (!bank_items.empty()) {
LogError(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
guild_id,
bank_items.size()
);
LogGuilds(
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try "
"again.",
"Attempt to delete guild id [{}] that still has [{}] items in the bank. Please remove them and try again.",
guild_id,
bank_items.size()
);
return false;
}
else {
auto where_filter = fmt::format("guild_id = {}", guild_id);
GuildTributesRepository::DeleteOne(*m_db, guild_id);
GuildsRepository::DeleteOne(*m_db, guild_id);
GuildRanksRepository::DeleteWhere(*m_db, where_filter);
@@ -602,7 +612,7 @@ bool BaseGuildManager::UpdateDbDeleteGuild(uint32 guild_id, bool local_delete, b
GuildMembersRepository::DeleteWhere(*m_db, where_filter);
LogGuilds("Deleted guild [{}] from the database", guild_id);
}
}
return true;
}
@@ -946,24 +956,33 @@ bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into)
}
GuildsListMessaging_Struct BaseGuildManager::MakeGuildList()
//returns ownership of the buffer.
uint8 *BaseGuildManager::MakeGuildList(const char *head_name, uint32 &length) const
{
GuildsListMessaging_Struct guild_list_messaging{};
uint32 string_length = 0;
//dynamic structs will make this a lot less painful.
for (auto const &g: m_guilds) {
GuildsListMessagingEntry_Struct guild_entry{};
length = sizeof(GuildsList_Struct);
auto buffer = new uint8[length];
guild_entry.guild_id = g.first;
guild_entry.guild_name = g.second->name;
string_length += g.second->name.length() + 1;
guild_list_messaging.guild_detail.push_back(guild_entry);
//a bit little better than memsetting the whole thing...
uint32 r, pos;
for (r = 0, pos = 0; r <= MAX_NUMBER_GUILDS; r++, pos += 64) {
//strcpy((char *) buffer+pos, "BAD GUILD");
// These 'BAD GUILD' entries were showing in the drop-downs for selecting guilds in the LFP window,
// so just fill unused entries with an empty string instead.
buffer[pos] = '\0';
}
guild_list_messaging.no_of_guilds = m_guilds.size();
guild_list_messaging.string_length = string_length;
strn0cpy((char *) buffer, head_name, 64);
return guild_list_messaging;
std::map<uint32, GuildInfo *>::const_iterator cur, end;
cur = m_guilds.begin();
end = m_guilds.end();
for (; cur != end; ++cur) {
pos = 64 + (64 * cur->first);
strn0cpy((char *) buffer + pos, cur->second->name.c_str(), 64);
}
return (buffer);
}
const char *BaseGuildManager::GetRankName(uint32 guild_id, uint8 rank) const
+2 -1
View File
@@ -134,7 +134,7 @@ class BaseGuildManager
bool CheckGMStatus(uint32 guild_id, uint8 status) const;
bool CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const;
bool UpdateDbBankerFlag(uint32 charid, bool is_banker);
GuildsListMessaging_Struct MakeGuildList();
uint8* MakeGuildList(const char* head_name, uint32& length) const; //make a guild list packet, returns ownership of the buffer.
uint8 GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const;
uint32 GetGuildIDByName(const char *GuildName);
uint32 GetGuildIDByCharacterID(uint32 character_id);
@@ -208,6 +208,7 @@ class BaseGuildManager
bool _StoreGuildDB(uint32 guild_id);
GuildInfo* _CreateGuild(uint32 guild_id, std::string guild_name, uint32 leader_char_id, uint8 minstatus, std::string guild_motd, std::string motd_setter, std::string Channel, std::string URL, uint32 favour);
uint32 _GetFreeGuildID();
GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in);
};
#endif /*GUILD_BASE_H_*/
File diff suppressed because it is too large Load Diff
+11 -16
View File
@@ -57,8 +57,6 @@ public:
inline std::list<EQ::ItemInstance*>::const_iterator cbegin() { return m_list.cbegin(); }
inline std::list<EQ::ItemInstance*>::const_iterator cend() { return m_list.cend(); }
inline std::list<EQ::ItemInstance*>::iterator begin() { return m_list.begin(); }
inline std::list<EQ::ItemInstance*>::iterator end() { return m_list.end(); }
inline int size() { return static_cast<int>(m_list.size()); } // TODO: change to size_t
inline bool empty() { return m_list.empty(); }
@@ -134,7 +132,7 @@ namespace EQ
// Swap items in inventory
enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel };
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = Race::Doug, uint8 class_id = Class::None, uint32 deity_id = Deity::Unknown, uint8 level = 0);
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = Race::Doug, uint8 class_id = Class::None, uint16 deity_id = deity::DeityType::DeityUnknown, uint8 level = 0);
// Remove item from inventory
bool DeleteItem(int16 slot_id, int16 quantity = 0);
@@ -149,13 +147,13 @@ namespace EQ
bool HasItemEquippedByID(uint32 item_id);
// Check how many of a specific item the player has equipped by Item ID
uint32 CountItemEquippedByID(uint32 item_id);
int CountItemEquippedByID(uint32 item_id);
// Check if player has a specific augment equipped by Item ID
bool HasAugmentEquippedByID(uint32 item_id);
// Check how many of a specific augment the player has equipped by Item ID
uint32 CountAugmentEquippedByID(uint32 item_id);
int CountAugmentEquippedByID(uint32 item_id);
// Get a list of augments from a specific slot ID
std::vector<uint32> GetAugmentIDsBySlotID(int16 slot_id);
@@ -178,8 +176,6 @@ namespace EQ
// Locate an available inventory slot
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
int16 FindFirstFreeSlotThatFitsItemWithStacking(ItemInstance *inst) const;
// Calculate slot_id for an item within a bag
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
@@ -201,25 +197,26 @@ namespace EQ
uint8 FindBrightestLightType();
void dumpEntireInventory();
void dumpWornItems();
void dumpInventory();
void dumpBankItems();
void dumpSharedBankItems();
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, const std::string& value);
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, int value);
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, float value);
void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, bool value);
std::string GetCustomItemData(int16 slot_id, const std::string& identifier);
static const int GetItemStatValue(uint32 item_id, const std::string& identifier);
std::map<int16, ItemInstance*>& GetWorn() { return m_worn; }
std::map<int16, ItemInstance*>& GetPersonal() { return m_inv; }
int16 HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where);
inline int16 PushItem(int16 slot_id, ItemInstance* inst) { return _PutItem(slot_id, inst); }
protected:
///////////////////////////////
// Protected Methods
///////////////////////////////
int GetSlotByItemInstCollection(const std::map<int16, ItemInstance*> &collection, ItemInstance *inst);
void dumpItemCollection(const std::map<int16, ItemInstance*> &collection);
void dumpBagContents(ItemInstance *inst, std::map<int16, ItemInstance*>::const_iterator *it);
// Retrieves item within an inventory bucket
ItemInstance* _GetItem(const std::map<int16, ItemInstance*>& bucket, int16 slot_id) const;
@@ -234,8 +231,6 @@ namespace EQ
int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity);
int16 _HasItemByLoreGroup(std::map<int16, ItemInstance*>& bucket, uint32 loregroup);
int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup);
int16 _HasEvolvingItem(std::map<int16, ItemInstance*>& bucket, uint64 evolve_unique_id, uint8 quantity);
int16 _HasEvolvingItem(ItemInstQueue& iqueue, uint64 evolve_unique_id, uint8 quantity);
// Player inventory
+19 -134
View File
@@ -81,79 +81,45 @@ 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>
/**
* Gets local address - pings google to inspect what interface was used locally
* @return
*/
std::string IpUtil::GetLocalIPAddress()
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return "";
}
#endif
char my_ip_address[INET_ADDRSTRLEN];
char my_ip_address[16];
unsigned int my_port;
struct sockaddr_in server_address{};
struct sockaddr_in my_address{};
int sockfd;
// Create a UDP socket
#ifdef _WIN32
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == INVALID_SOCKET) {
WSACleanup();
// Connect to server
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return "";
}
#else
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
return "";
}
#endif
// Set server_addr (dummy address)
// Set server_addr
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
server_address.sin_port = htons(53); // DNS port
server_address.sin_addr.s_addr = inet_addr("172.217.160.99");
server_address.sin_port = htons(80);
// Perform a dummy connection to the server (UDP)
connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
// Connect to server
if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
close(sockfd);
return "";
}
// Get my IP address
// Get my ip address and port
memset(&my_address, 0, sizeof(my_address));
socklen_t len = sizeof(my_address);
getsockname(sockfd, (struct sockaddr *) &my_address, &len);
inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address));
my_port = ntohs(my_address.sin_port);
#ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif
LogInfo("Local IP Address [{}]", my_ip_address);
return std::string(my_ip_address);
return fmt::format("{}", my_ip_address);
}
/**
* Gets public address
* Uses various websites as options to return raw public IP back to the client
@@ -259,84 +225,3 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
}
#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 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
}
#endif
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
#ifdef _WIN32
WSACleanup();
#endif
return true; // Assume in use on failure
}
#ifdef _WIN32
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&opt, sizeof(opt)); // Windows-specific
#else
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Linux/macOS
#endif
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
// Convert IP address from string to binary format
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
std::cerr << "Invalid IP address format: " << ip << std::endl;
#ifdef _WIN32
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
return true; // Assume in use on failure
}
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
in_use = true; // Bind failed, port is in use
}
#ifdef _WIN32
closesocket(sock);
WSACleanup();
#else
close(sock);
#endif
return in_use;
}
-1
View File
@@ -37,7 +37,6 @@ public:
int port
);
static bool IsIPAddress(const std::string &ip_address);
static bool IsPortInUse(const std::string& ip, int port);
};
+1 -1
View File
@@ -55,7 +55,7 @@ namespace EQ {
EQ_EXCEPT("IPC Mutex", "Could not create mutex.");
}
#else
std::string final_name = fmt::format("{}/{}.lock", PathManager::Instance()->GetSharedMemoryPath(), name);
std::string final_name = fmt::format("{}/{}.lock", path.GetSharedMemoryPath(), name);
#ifdef __DARWIN
#if __DARWIN_C_LEVEL < 200809L
-28
View File
@@ -220,34 +220,6 @@ bool EQ::ItemData::IsType1HWeapon() const
return ((ItemType == item::ItemType1HBlunt) || (ItemType == item::ItemType1HSlash) || (ItemType == item::ItemType1HPiercing) || (ItemType == item::ItemTypeMartial));
}
bool EQ::ItemData::IsPetUsable() const
{
if (ItemClass == item::ItemClassBag) {
return true;
}
// if it's a misc item and has slots, it's wearable
// this item type is conflated with many other item types
if (ItemClass == item::ItemTypeMisc && Slots != 0) {
return true;
}
switch (ItemType) {
case item::ItemType1HBlunt:
case item::ItemType1HSlash:
case item::ItemType1HPiercing:
case item::ItemType2HBlunt:
case item::ItemType2HSlash:
case item::ItemTypeMartial:
case item::ItemTypeShield:
case item::ItemTypeArmor:
case item::ItemTypeJewelry:
return true;
default:
return false;
}
}
bool EQ::ItemData::IsType2HWeapon() const
{
return ((ItemType == item::ItemType2HBlunt) || (ItemType == item::ItemType2HSlash) || (ItemType == item::ItemType2HPiercing));
+5 -9
View File
@@ -104,10 +104,10 @@ namespace EQ
/*----*/ ItemTypeUnknown6,
/*----*/ ItemTypeUnknown7,
/*5522*/ ItemTypeMartial, // 45
/*----*/ ItemTypeAllEffects,
/*----*/ ItemTypeUnknown8,
/*----*/ ItemTypeUnknown9,
/*----*/ ItemTypeUnknown10,
/*----*/ ItemTypeFocusEffect,
/*----*/ ItemTypeUnknown11,
/*----*/ ItemTypeSinging, // 50
/*5750*/ ItemTypeAllInstrumentTypes,
/*5776*/ ItemTypeCharm,
@@ -115,7 +115,7 @@ namespace EQ
/*----*/ ItemTypeAugmentation,
/*----*/ ItemTypeAugmentationSolvent, // 55
/*----*/ ItemTypeAugmentationDistiller,
/*----*/ ItemTypeAlternateAbility,
/*----*/ ItemTypeUnknown12,
/*----*/ ItemTypeFellowshipKit,
/*----*/ ItemTypeUnknown13,
/*----*/ ItemTypeRecipe, // 60
@@ -123,10 +123,7 @@ namespace EQ
/*----*/ ItemTypeJournal, // only one(1) database entry
/*----*/ ItemTypeAltCurrency, // alt-currency (as opposed to coinage)
/*5881*/ ItemTypePerfectedAugmentationDistiller,
/*----*/ ItemTypeCount,
/*----*/ ItemTypeCollectible,
/*----*/ ItemTypeContainer,
/*----*/ ItemTypeAll = 0xFF
/*----*/ ItemTypeCount
/*
Unknowns:
@@ -438,7 +435,7 @@ namespace EQ
//uint32 Unk054 {};
int16 MaxCharges {}; // Maximum charges items can hold: -1 if not a chargeable item
uint8 ItemType {}; // Item Type/Skill (itemClass* from above)
int32 SubType {}; // Some items have sub types that can be used for other things (unbreakable fishing poles, SpellEffect::FFItemClass)
int32 SubType {}; // Some items have sub types that can be used for other things (unbreakable fishing poles, SE_FFItemClass)
uint8 Material {}; // Item material type
uint32 HerosForgeModel {};// Hero's Forge Armor Model Type (2-13?)
float SellRate {}; // Sell rate
@@ -550,7 +547,6 @@ namespace EQ
bool IsType1HWeapon() const;
bool IsType2HWeapon() const;
bool IsTypeShield() const;
bool IsPetUsable() const;
bool IsQuestItem() const;
static bool CheckLoreConflict(const ItemData* l_item, const ItemData* r_item);
+206
View File
@@ -0,0 +1,206 @@
/*
These fields must be in the order of how they are serialized!
*/
F(itemclass)
F(name)
F(lore)
F(idfile)
F(id)
F(weight)
F(norent)
F(nodrop)
F(size)
F(slots)
F(price)
F(icon)
F(UNK012)
F(UNK013)
F(benefitflag)
F(tradeskills)
F(cr)
F(dr)
F(pr)
F(mr)
F(fr)
F(astr)
F(asta)
F(aagi)
F(adex)
F(acha)
F(aint)
F(awis)
F(hp)
F(mana)
F(ac)
F(deity)
F(skillmodvalue)
F(UNK033)
F(skillmodmax)
F(skillmodtype)
F(banedmgrace)
F(banedmgamt)
F(banedmgbody)
F(magic)
F(casttime_)
F(reqlevel)
F(bardtype)
F(bardvalue)
F(light)
F(delay)
F(reclevel)
F(recskill)
F(elemdmgtype)
F(elemdmgamt)
F(range)
F(damage)
F(color)
F(classes)
F(races)
F(UNK054)
F(maxcharges)
F(itemtype)
F(material)
F(herosforgemodel)
F(sellrate)
F(UNK059)
F(casttime)
F(elitematerial)
F(procrate)
F(combateffects)
F(shielding)
F(stunresist)
F(strikethrough)
F(extradmgskill)
F(extradmgamt)
F(spellshield)
F(avoidance)
F(accuracy)
F(charmfileid)
F(factionmod1)
F(factionmod2)
F(factionmod3)
F(factionmod4)
F(factionamt1)
F(factionamt2)
F(factionamt3)
F(factionamt4)
F(charmfile)
F(augtype)
F(augslot1type)
F(augslot1visible)
F(augslot2type)
F(augslot2visible)
F(augslot3type)
F(augslot3visible)
F(augslot4type)
F(augslot4visible)
F(augslot5type)
F(augslot5visible)
F(augslot6type)
F(augslot6visible)
F(ldontheme)
F(ldonprice)
F(ldonsold)
F(bagtype)
F(bagslots)
F(bagsize)
F(bagwr)
F(book)
F(booktype)
F(filename)
F(banedmgraceamt)
F(augrestrict)
F(loregroup)
F(pendingloreflag)
F(artifactflag)
F(summonedflag)
F(favor)
F(fvnodrop)
F(endur)
F(dotshielding)
F(attack)
F(regen)
F(manaregen)
F(enduranceregen)
F(haste)
F(damageshield)
F(recastdelay)
F(recasttype)
F(guildfavor)
F(augdistiller)
F(UNK123)
F(UNK124)
F(attuneable)
F(nopet)
F(UNK127)
F(pointtype)
F(potionbelt)
F(potionbeltslots)
F(stacksize)
F(notransfer)
F(stackable)
F(UNK134)
F(clickeffect)
F(clicktype)
F(clicklevel)
F(clicklevel2)
F(proceffect)
F(proctype)
F(proclevel)
F(proclevel2)
F(worneffect)
F(worntype)
F(wornlevel)
F(wornlevel2)
F(focuseffect)
F(focustype)
F(focuslevel)
F(focuslevel2)
F(scrolleffect)
F(scrolltype)
F(scrolllevel)
F(scrolllevel2)
F(bardeffect)
F(bardeffecttype)
F(bardlevel2)
F(bardlevel)
F(questitemflag)
F(svcorruption)
F(purity)
F(evoitem)
F(evoid)
F(evolvinglevel)
F(evomax)
F(backstabdmg)
F(dsmitigation)
F(heroic_str)
F(heroic_int)
F(heroic_wis)
F(heroic_agi)
F(heroic_dex)
F(heroic_sta)
F(heroic_cha)
F(heroic_mr)
F(heroic_fr)
F(heroic_cr)
F(heroic_dr)
F(heroic_pr)
F(heroic_svcorrup)
F(healamt)
F(spelldmg)
F(ldonsellbackrate)
F(scriptfileid)
F(expendablearrow)
F(clairvoyance)
F(clickname)
F(procname)
F(wornname)
F(focusname)
F(scrollname)
F(subtype)
+92 -256
View File
@@ -25,7 +25,6 @@
#include "rulesys.h"
#include "shareddb.h"
#include "strings.h"
#include "evolving_items.h"
//#include "../common/light_source.h"
@@ -33,11 +32,10 @@
//#include <iostream>
int32 next_item_serial_number = 1;
std::unordered_set<uint64> guids{};
int32 NextItemInstSerialNumber = 1;
static inline int32 GetNextItemInstSerialNumber() {
static inline int32 GetNextItemInstSerialNumber()
{
// The Bazaar relies on each item a client has up for Trade having a unique
// identifier. This 'SerialNumber' is sent in Serialized item packets and
// is used in Bazaar packets to identify the item a player is buying or inspecting.
@@ -48,18 +46,12 @@ static inline int32 GetNextItemInstSerialNumber()
// NextItemInstSerialNumber is the next one to hand out.
//
// It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1.
if (next_item_serial_number >= INT32_MAX) {
next_item_serial_number = 1;
}
else {
next_item_serial_number++;
}
if(NextItemInstSerialNumber >= INT_MAX)
NextItemInstSerialNumber = 1;
else
NextItemInstSerialNumber++;
while (guids.contains(next_item_serial_number)) {
next_item_serial_number++;
}
return next_item_serial_number;
return NextItemInstSerialNumber;
}
//
@@ -77,10 +69,6 @@ EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) {
m_color = m_item->Color;
}
if (IsEvolving()) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
}
m_SerialNumber = GetNextItemInstSerialNumber();
}
@@ -100,10 +88,6 @@ EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges
m_color = 0;
}
if (IsEvolving()) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
}
m_SerialNumber = GetNextItemInstSerialNumber();
}
@@ -155,6 +139,7 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy)
m_exp = copy.m_exp;
m_evolveLvl = copy.m_evolveLvl;
m_activated = copy.m_activated;
if (copy.m_scaledItem) {
m_scaledItem = new ItemData(*copy.m_scaledItem);
@@ -162,7 +147,12 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy)
m_scaledItem = nullptr;
}
m_evolving_details = copy.m_evolving_details;
if (copy.m_evolveInfo) {
m_evolveInfo = new EvolveInfo(*copy.m_evolveInfo);
} else {
m_evolveInfo = nullptr;
}
m_scaling = copy.m_scaling;
m_ornamenticon = copy.m_ornamenticon;
m_ornamentidfile = copy.m_ornamentidfile;
@@ -177,6 +167,7 @@ EQ::ItemInstance::~ItemInstance()
Clear();
safe_delete(m_item);
safe_delete(m_scaledItem);
safe_delete(m_evolveInfo);
}
// Query item type
@@ -312,34 +303,47 @@ int8 EQ::ItemInstance::AvailableAugmentSlot(int32 augment_type) const
return INVALID_INDEX;
}
for (int16 slot_id = invaug::SOCKET_BEGIN; slot_id <= invaug::SOCKET_END; ++slot_id) {
if (IsAugmentSlotAvailable(augment_type, slot_id)) {
return slot_id;
auto i = invaug::SOCKET_BEGIN;
for (; i <= invaug::SOCKET_END; ++i) {
if (GetItem(i)) {
continue;
}
if (
augment_type == -1 ||
(
m_item->AugSlotType[i] &&
((1 << (m_item->AugSlotType[i] - 1)) & augment_type)
)
) {
break;
}
}
return INVALID_INDEX;
return (i <= invaug::SOCKET_END) ? i : INVALID_INDEX;
}
bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augment_type, uint8 slot) const
{
if (!m_item || !m_item->IsClassCommon() || GetItem(slot)) {
if (!m_item || !m_item->IsClassCommon()) {
return false;
}
return (
if (
(
!GetItem(slot) &&
m_item->AugSlotVisible[slot]
) &&
augment_type == -1 ||
(
m_item->AugSlotType[slot] &&
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
)
) &&
(
RuleB(Items, AugmentItemAllowInvisibleAugments) ||
m_item->AugSlotVisible[slot]
)
);
) {
return true;
}
return false;
}
// Retrieve item inside container
@@ -574,7 +578,7 @@ EQ::ItemInstance* EQ::ItemInstance::GetOrnamentationAugment() const
uint32 EQ::ItemInstance::GetOrnamentHeroModel(int32 material_slot) const
{
// Not a Hero Forge item.
if (m_ornament_hero_model == 0) {
if (m_ornament_hero_model == 0 || material_slot < 0) {
return 0;
}
@@ -906,34 +910,26 @@ bool EQ::ItemInstance::IsSlotAllowed(int16 slot_id) const {
bool EQ::ItemInstance::IsDroppable(bool recurse) const
{
if (!m_item) {
if (!m_item)
return false;
}
/*if (m_ornamentidfile) // not implemented
return false;*/
if (m_attuned) {
if (m_attuned)
return false;
}
if (RuleI(World, FVNoDropFlag) == FVNoDropFlagRule::Enabled && m_item->FVNoDrop == 0) {
return true;
}
if (m_item->NoDrop == 0) {
/*if (m_item->FVNoDrop != 0) // not implemented
return false;*/
if (m_item->NoDrop == 0)
return false;
}
if (recurse) {
for (auto iter : m_contents) {
if (!iter.second) {
if (!iter.second)
continue;
}
if (!iter.second->IsDroppable(recurse)) {
if (!iter.second->IsDroppable(recurse))
return false;
}
}
}
return true;
}
@@ -1040,6 +1036,29 @@ void EQ::ItemInstance::ScaleItem() {
m_scaledItem->CharmFileID = 0; // this stops the client from trying to scale the item itself.
}
bool EQ::ItemInstance::EvolveOnAllKills() const {
return (m_evolveInfo && m_evolveInfo->AllKills);
}
int8 EQ::ItemInstance::GetMaxEvolveLvl() const {
if (m_evolveInfo)
return m_evolveInfo->MaxLvl;
else
return 0;
}
uint32 EQ::ItemInstance::GetKillsNeeded(uint8 currentlevel) {
uint32 kills = -1; // default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero.
if (m_evolveInfo)
if (currentlevel != m_evolveInfo->MaxLvl)
kills = m_evolveInfo->LvlKills[currentlevel - 1];
if (kills == 0)
kills = -1;
return kills;
}
void EQ::ItemInstance::SetTimer(std::string name, uint32 time) {
Timer t(time);
t.Start(time, false);
@@ -1273,7 +1292,7 @@ int EQ::ItemInstance::GetItemBaneDamageRace(bool augments) const
return race;
}
int EQ::ItemInstance::GetItemBaneDamageBody(uint8 against, bool augments) const
int EQ::ItemInstance::GetItemBaneDamageBody(bodyType against, bool augments) const
{
int64 damage = 0;
const auto item = GetItem();
@@ -1793,211 +1812,28 @@ std::vector<uint32> EQ::ItemInstance::GetAugmentIDs() const
return augments;
}
std::vector<std::string> EQ::ItemInstance::GetAugmentNames() const
{
std::vector<std::string> augment_names;
for (uint8 slot_id = invaug::SOCKET_BEGIN; slot_id <= invaug::SOCKET_END; slot_id++) {
const auto augment = GetAugment(slot_id);
augment_names.push_back(augment ? augment->GetItem()->Name : "");
//
// class EvolveInfo
//
EvolveInfo::EvolveInfo() {
// nothing here yet
}
return augment_names;
EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) {
FirstItem = first;
MaxLvl = max;
AllKills = allkills;
LvlKills[0] = L2;
LvlKills[1] = L3;
LvlKills[2] = L4;
LvlKills[3] = L5;
LvlKills[4] = L6;
LvlKills[5] = L7;
LvlKills[6] = L8;
LvlKills[7] = L9;
LvlKills[8] = L10;
}
int EQ::ItemInstance::GetItemRegen(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->Regen;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemRegen();
}
}
}
}
return stat;
}
EvolveInfo::~EvolveInfo() {
int EQ::ItemInstance::GetItemManaRegen(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->ManaRegen;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemManaRegen();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemDamageShield(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->DamageShield;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemDamageShield();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemDSMitigation(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->DSMitigation;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemDSMitigation();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemHealAmt(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->HealAmt;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemHealAmt();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemSpellDamage(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->SpellDmg;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemSpellDamage();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemClairvoyance(bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->Clairvoyance;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemClairvoyance();
}
}
}
}
return stat;
}
int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augments) const
{
int stat = 0;
const auto item = GetItem();
if (item) {
stat = item->ExtraDmgSkill == skill ? item->ExtraDmgAmt : 0;
if (augments) {
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
if (GetAugment(i)) {
stat += GetAugment(i)->GetItemSkillsStat(skill);
}
}
}
}
return stat;
}
void EQ::ItemInstance::AddGUIDToMap(uint64 existing_serial_number)
{
guids.emplace(existing_serial_number);
}
void EQ::ItemInstance::ClearGUIDMap()
{
guids.clear();
}
bool EQ::ItemInstance::TransferOwnership(Database &db, const uint32 to_char_id) const
{
if (!to_char_id || !IsEvolving()) {
return false;
}
SetEvolveCharID(to_char_id);
CharacterEvolvingItemsRepository::UpdateCharID(db, GetEvolveUniqueID(), to_char_id);
return true;
}
uint32 EQ::ItemInstance::GetAugmentEvolveUniqueID(uint8 augment_index) const
{
if (!m_item || !m_item->IsClassCommon()) {
return 0;
}
const auto item = GetItem(augment_index);
if (item) {
return item->GetEvolveUniqueID();
}
return 0;
}
void EQ::ItemInstance::SetTimer(std::string name, uint32 time) const{
Timer t(time);
t.Start(time, false);
m_timers[name] = t;
}
void EQ::ItemInstance::SetEvolveEquipped(const bool in) const
{
if (!IsEvolving()) {
return;
}
m_evolving_details.equipped = in;
if (in && !GetTimers().contains("evolve")) {
SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping));
return;
}
if (in) {
GetTimers().at("evolve").SetTimer(RuleI(EvolvingItems, DelayUponEquipping));
return;
}
GetTimers().at("evolve").Disable();
}
+27 -46
View File
@@ -23,7 +23,6 @@
#ifndef COMMON_ITEM_INSTANCE_H
#define COMMON_ITEM_INSTANCE_H
#include "evolving_items.h"
class ItemParse; // Parses item packets
@@ -35,7 +34,6 @@ class EvolveInfo; // Stores information about an evolving item family
#include "../common/bodytypes.h"
#include "../common/deity.h"
#include "../common/memory_buffer.h"
#include "../common/repositories/character_evolving_items_repository.h"
#include <map>
@@ -207,9 +205,13 @@ namespace EQ
bool IsDroppable(bool recurse = true) const;
bool IsScaling() const { return m_scaling; }
bool IsEvolving() const { return (m_evolveLvl >= 1); }
uint32 GetExp() const { return m_exp; }
void SetExp(uint32 exp) { m_exp = exp; }
void AddExp(uint32 exp) { m_exp += exp; }
bool IsActivated() { return m_activated; }
void SetActivated(bool activated) { m_activated = activated; }
int8 GetEvolveLvl() const { return m_evolveLvl; }
void SetScaling(bool v) { m_scaling = v; }
uint32 GetOrnamentationIcon() const { return m_ornamenticon; }
void SetOrnamentIcon(uint32 ornament_icon) { m_ornamenticon = ornament_icon; }
@@ -224,6 +226,9 @@ namespace EQ
void Initialize(SharedDatabase *db = nullptr);
void ScaleItem();
bool EvolveOnAllKills() const;
int8 GetMaxEvolveLvl() const;
uint32 GetKillsNeeded(uint8 currentlevel);
std::string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id = slot_id; s.inst = (const void*)this; std::string ser; ser.assign((char*)&s, sizeof(InternalSerializedItem_Struct)); return ser; }
void Serialize(OutBuffer& ob, int16 slot_id) const { InternalSerializedItem_Struct isi; isi.slot_id = slot_id; isi.inst = (const void*)this; ob.write((const char*)&isi, sizeof(isi)); }
@@ -231,9 +236,8 @@ namespace EQ
inline int32 GetSerialNumber() const { return m_SerialNumber; }
inline void SetSerialNumber(int32 id) { m_SerialNumber = id; }
std::map<std::string, ::Timer>& GetTimers() const { return m_timers; }
std::map<std::string, ::Timer>& GetTimers() { return m_timers; }
void SetTimer(std::string name, uint32 time);
void SetTimer(std::string name, uint32 time) const;
void StopTimer(std::string name);
void ClearTimers();
@@ -261,12 +265,11 @@ namespace EQ
// these two are just quick checks
int GetItemBaneDamageBody(bool augments = false) const;
int GetItemBaneDamageRace(bool augments = false) const;
int GetItemBaneDamageBody(uint8 against, bool augments = false) const;
int GetItemBaneDamageBody(bodyType against, bool augments = false) const;
int GetItemBaneDamageRace(uint16 against, bool augments = false) const;
int GetItemMagical(bool augments = false) const;
int GetItemHP(bool augments = false) const;
int GetItemMana(bool augments = false) const;
int GetItemManaRegen(bool augments = false) const;
int GetItemEndur(bool augments = false) const;
int GetItemAttack(bool augments = false) const;
int GetItemStr(bool augments = false) const;
@@ -296,46 +299,8 @@ namespace EQ
int GetItemHeroicDR(bool augments = false) const;
int GetItemHeroicCorrup(bool augments = false) const;
int GetItemHaste(bool augments = false) const;
int GetItemRegen(bool augments = false) const;
int GetItemDamageShield(bool augments = false) const;
int GetItemDSMitigation(bool augments = false) const;
int GetItemHealAmt(bool augments = false) const;
int GetItemSpellDamage(bool augments = false) const;
int GetItemClairvoyance(bool augments = false) const;
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
uint32 GetItemGuildFavor() const;
std::vector<uint32> GetAugmentIDs() const;
std::vector<std::string> GetAugmentNames() const;
static void AddGUIDToMap(uint64 existing_serial_number);
static void ClearGUIDMap();
// evolving items stuff
CharacterEvolvingItemsRepository::CharacterEvolvingItems &GetEvolvingDetails() const { return m_evolving_details; }
int8 GetEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingLevel; } return false; }
bool IsEvolving() const { if (GetItem()) { return GetItem()->EvolvingItem; } return false; }
int8 GetMaxEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingMax; } return false; }
bool GetEvolveActivated() const { return m_evolving_details.activated ? true : false; }
bool GetEvolveEquipped() const { return m_evolving_details.equipped ? true : false; }
double GetEvolveProgression() const { return m_evolving_details.progression; }
uint64 GetEvolveUniqueID() const { return m_evolving_details.id; }
uint32 GetEvolveCharID() const { return m_evolving_details.character_id; }
uint32 GetEvolveItemID() const { return m_evolving_details.item_id; }
uint32 GetEvolveLoreID() const { if (GetItem()) { return GetItem()->EvolvingID; } return false; }
uint64 GetEvolveCurrentAmount() const { return m_evolving_details.current_amount; }
uint32 GetEvolveFinalItemID() const { return m_evolving_details.final_item_id; }
uint32 GetAugmentEvolveUniqueID(uint8 augment_index) const;
void SetEvolveEquipped(const bool in) const;
void SetEvolveActivated(const bool in) const { m_evolving_details.activated = in; }
void SetEvolveProgression(const double in) const { m_evolving_details.progression = in; }
void SetEvolveUniqueID(const uint64 in) const { m_evolving_details.id = in; }
void SetEvolveCharID(const uint32 in) const { m_evolving_details.character_id = in; }
void SetEvolveItemID(const uint32 in) const { m_evolving_details.item_id = in; }
void SetEvolveCurrentAmount(const uint64 in) const { m_evolving_details.current_amount = in; }
void SetEvolveAddToCurrentAmount(const uint64 in) const { m_evolving_details.current_amount += in; }
void SetEvolveFinalItemID(const uint32 in) const { m_evolving_details.final_item_id = in; }
bool TransferOwnership(Database& db, const uint32 to_char_id) const;
void CalculateEvolveProgression() const { m_evolving_details.progression = EvolvingItemsManager::Instance()->CalculateProgression(GetEvolveCurrentAmount(), GetID()); }
protected:
//////////////////////////
@@ -360,7 +325,9 @@ namespace EQ
int32 m_SerialNumber {0}; // Unique identifier for this instance of an item. Needed for Bazaar.
uint32 m_exp {0};
int8 m_evolveLvl {0};
bool m_activated {false};
ItemData* m_scaledItem {nullptr};
::EvolveInfo* m_evolveInfo {nullptr};
bool m_scaling {false};
uint32 m_ornamenticon {0};
uint32 m_ornamentidfile {0};
@@ -368,12 +335,26 @@ namespace EQ
uint32 m_ornament_hero_model {0};
uint32 m_recast_timestamp {0};
int m_task_delivered_count {0};
mutable CharacterEvolvingItemsRepository::CharacterEvolvingItems m_evolving_details{};
// Items inside of this item (augs or contents) {};
std::map<uint8, ItemInstance*> m_contents {}; // Zero-based index: min=0, max=9
std::map<std::string, std::string> m_custom_data {};
mutable std::map<std::string, ::Timer> m_timers {};
std::map<std::string, ::Timer> m_timers {};
};
}
class EvolveInfo {
public:
friend class EQ::ItemInstance;
//temporary
uint16 LvlKills[9];
uint32 FirstItem;
uint8 MaxLvl;
bool AllKills;
EvolveInfo();
EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10);
~EvolveInfo();
};
#endif /*COMMON_ITEM_INSTANCE_H*/
-1
View File
@@ -25,7 +25,6 @@ struct LootItem {
uint16 trivial_max_level;
uint16 npc_min_level;
uint16 npc_max_level;
uint32 lootdrop_id; // required for zone state referencing
};
typedef std::list<LootItem*> LootItems;
-220
View File
@@ -1,220 +0,0 @@
#ifndef EQEMU_KSM_HPP
#define EQEMU_KSM_HPP
#include "../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
}
}
#endif // EQEMU_KSM_HPP
-600
View File
@@ -1,600 +0,0 @@
#include "mysql_stmt.h"
#include "eqemu_logsys.h"
#include "mutex.h"
#include "timer.h"
#include <charconv>
namespace mysql
{
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);
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)
{
LockMutex 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()));
}
m_params.resize(mysql_stmt_param_count(m_stmt.get()));
m_inputs.resize(m_params.size());
}
void PreparedStmt::ThrowError(const std::string& error)
{
LogMySQLError("{}", error);
throw std::runtime_error(error);
}
std::string PreparedStmt::GetStmtError()
{
auto err = mysql_stmt_errno(m_stmt.get());
auto str = mysql_stmt_error(m_stmt.get());
return fmt::format("({}) [{}] for query [{}]", err, str, m_query);
}
template <typename T>
void PreparedStmt::BindInput(size_t index, T value)
{
if (index >= m_inputs.size())
{
ThrowError(fmt::format("Cannot bind input, index {} out of range", index));
}
impl::Bind& arg = m_inputs[index];
arg.is_null = std::is_same_v<T, std::nullptr_t>;
MYSQL_BIND& bind = m_params[index];
bind.is_unsigned = std::is_unsigned_v<T>;
bind.is_null = &arg.is_null;
bind.length = &arg.length;
auto old_type = bind.buffer_type;
if constexpr (std::is_arithmetic_v<T>)
{
if (arg.buffer.size() < sizeof(T))
{
arg.buffer.resize(std::max(sizeof(T), sizeof(int64_t)));
bind.buffer = arg.buffer.data();
m_need_bind = true;
}
memcpy(arg.buffer.data(), &value, sizeof(T));
}
if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, uint8_t> || std::is_same_v<T, bool>)
{
bind.buffer_type = MYSQL_TYPE_TINY;
}
else if constexpr (std::is_same_v<T, int16_t> || std::is_same_v<T, uint16_t>)
{
bind.buffer_type = MYSQL_TYPE_SHORT;
}
else if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>)
{
bind.buffer_type = MYSQL_TYPE_LONG;
}
else if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>)
{
bind.buffer_type = MYSQL_TYPE_LONGLONG;
}
else if constexpr (std::is_same_v<T, float>)
{
bind.buffer_type = MYSQL_TYPE_FLOAT;
}
else if constexpr (std::is_same_v<T, double>)
{
bind.buffer_type = MYSQL_TYPE_DOUBLE;
}
else if constexpr (std::is_same_v<T, std::string_view>)
{
bind.buffer_type = MYSQL_TYPE_STRING;
if (arg.buffer.empty() || arg.buffer.size() < value.size())
{
arg.buffer.resize(static_cast<size_t>((value.size() + 1) * 1.5));
bind.buffer = arg.buffer.data();
bind.buffer_length = static_cast<unsigned long>(arg.buffer.size());
m_need_bind = true;
}
std::copy(value.begin(), value.end(), arg.buffer.begin());
arg.length = static_cast<unsigned long>(value.size());
}
else if constexpr (!std::is_same_v<T, std::nullptr_t>)
{
static_assert(false_v<T>, "Cannot bind unsupported type");
}
if (old_type != bind.buffer_type)
{
m_need_bind = true;
}
}
void PreparedStmt::BindInput(size_t index, const char* str)
{
BindInput(index, std::string_view(str));
}
void PreparedStmt::BindInput(size_t index, const std::string& str)
{
BindInput(index, std::string_view(str));
}
StmtResult PreparedStmt::Execute()
{
CheckArgs(0);
return DoExecute();
}
StmtResult PreparedStmt::Execute(const std::vector<param_t>& args)
{
CheckArgs(args.size());
for (size_t i = 0; i < args.size(); ++i)
{
std::visit([&](const auto& arg) { BindInput(i, arg); }, args[i]);
}
return DoExecute();
}
template <typename T>
StmtResult PreparedStmt::Execute(const std::vector<T>& args)
{
CheckArgs(args.size());
for (size_t i = 0; i < args.size(); ++i)
{
BindInput(i, args[i]);
}
return DoExecute();
}
void PreparedStmt::CheckArgs(size_t argc)
{
if (argc != m_params.size())
{
ThrowError(fmt::format("Bad arg count (got {}, expected {}) for [{}]", argc, m_params.size(), m_query));
}
}
StmtResult PreparedStmt::DoExecute()
{
BenchTimer timer;
LockMutex lock(m_mutex);
if (m_need_bind && mysql_stmt_bind_param(m_stmt.get(), m_params.data()) != 0)
{
ThrowError(fmt::format("Bind param error: {}", GetStmtError()));
}
m_need_bind = false;
if (mysql_stmt_execute(m_stmt.get()) != 0)
{
ThrowError(fmt::format("Execute error: {}", GetStmtError()));
}
my_bool attr = m_options.use_max_length;
mysql_stmt_attr_set(m_stmt.get(), STMT_ATTR_UPDATE_MAX_LENGTH, &attr);
if (m_options.buffer_results && mysql_stmt_store_result(m_stmt.get()) != 0)
{
ThrowError(fmt::format("Store result error: {}", GetStmtError()));
}
// Result buffers are bound on first execute and re-used if needed
if (m_results.empty())
{
BindResults();
}
StmtResult res(m_stmt.get(), m_results.size());
if (m_results.empty())
{
LogMySQLQuery("{} -- ({} row(s) affected) ({:.6f}s)", m_query, res.RowsAffected(), timer.elapsed());
}
else
{
LogMySQLQuery("{} -- ({} row(s) returned) ({:.6f}s)", m_query, res.RowCount(), timer.elapsed());
}
return res;
}
void PreparedStmt::BindResults()
{
MYSQL_RES* res = mysql_stmt_result_metadata(m_stmt.get());
if (!res)
{
return; // did not produce a result set
}
MYSQL_FIELD* fields = mysql_fetch_fields(res);
m_columns.resize(mysql_num_fields(res));
m_results.resize(m_columns.size());
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
{
impl::BindColumn& col = m_columns[i].m_col;
MYSQL_BIND& bind = m_results[i];
col.index = i;
col.name = fields[i].name;
col.buffer_type = fields[i].type;
col.is_unsigned = (fields[i].flags & UNSIGNED_FLAG) != 0;
col.buffer.resize(GetResultBufferSize(fields[i]));
bind.buffer_type = col.buffer_type;
bind.buffer = col.buffer.data();
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
bind.is_unsigned = col.is_unsigned;
bind.is_null = &col.is_null;
bind.length = &col.length;
bind.error = &col.error;
}
mysql_free_result(res);
if (!m_results.empty() && mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
{
ThrowError(fmt::format("Bind result error: {}", GetStmtError()));
}
}
int PreparedStmt::GetResultBufferSize(const MYSQL_FIELD& field) const
{
switch (field.type)
{
case MYSQL_TYPE_TINY:
return sizeof(int8_t);
case MYSQL_TYPE_SHORT:
return sizeof(int16_t);
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
return sizeof(int32_t);
case MYSQL_TYPE_LONGLONG:
return sizeof(int64_t);
case MYSQL_TYPE_FLOAT:
return sizeof(float);
case MYSQL_TYPE_DOUBLE:
return sizeof(double);
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
return sizeof(MYSQL_TIME);
default: // if max_length is unavailable for strings buffers are resized on fetch
return field.max_length + 1; // ensure valid buffer created
}
}
StmtRow PreparedStmt::Fetch()
{
StmtRow row;
if (!m_columns.empty())
{
int rc = mysql_stmt_fetch(m_stmt.get());
if (rc == 1)
{
ThrowError(fmt::format("Fetch error: {}", GetStmtError()));
}
if (rc != MYSQL_NO_DATA)
{
if (rc == MYSQL_DATA_TRUNCATED)
{
FetchTruncated();
}
row = StmtRow(m_columns);
}
}
return row;
}
void PreparedStmt::FetchTruncated()
{
for (int i = 0; i < static_cast<int>(m_columns.size()); ++i)
{
impl::BindColumn& col = m_columns[i].m_col;
if (col.error)
{
MYSQL_BIND& bind = m_results[i];
col.buffer.resize(static_cast<size_t>(col.length * 1.5));
bind.buffer = col.buffer.data();
bind.buffer_length = static_cast<unsigned long>(col.buffer.size());
mysql_stmt_fetch_column(m_stmt.get(), &bind, i, 0);
}
}
if (mysql_stmt_bind_result(m_stmt.get(), m_results.data()) != 0)
{
ThrowError(fmt::format("Fetch rebind result error: {}", GetStmtError()));
}
}
// ---------------------------------------------------------------------------
StmtResult::StmtResult(MYSQL_STMT* stmt, size_t columns)
{
m_num_cols = static_cast<int>(columns);
m_num_rows = mysql_stmt_num_rows(stmt); // requires buffered results
m_affected = mysql_stmt_affected_rows(stmt);
m_insert_id = mysql_stmt_insert_id(stmt);
}
// ---------------------------------------------------------------------------
const StmtColumn* StmtRow::GetColumn(size_t index) const
{
return index < m_columns.size() ? &m_columns[index] : nullptr;
}
const StmtColumn* StmtRow::GetColumn(std::string_view name) const
{
auto it = std::ranges::find_if(m_columns,
[name](const StmtColumn& col) { return col.Name() == name; });
return it != m_columns.end() ? &(*it) : nullptr;
}
std::optional<std::string> StmtRow::operator[](size_t index) const
{
return GetStr(index);
}
std::optional<std::string> StmtRow::operator[](std::string_view name) const
{
return GetStr(name);
}
std::optional<std::string> StmtRow::GetStr(size_t index) const
{
const StmtColumn* col = GetColumn(index);
return col ? col->GetStr() : std::nullopt;
}
std::optional<std::string> StmtRow::GetStr(std::string_view name) const
{
const StmtColumn* col = GetColumn(name);
return col ? col->GetStr() : std::nullopt;
}
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> StmtRow::Get(size_t index) const
{
const StmtColumn* col = GetColumn(index);
return col ? col->Get<T>() : std::nullopt;
}
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> StmtRow::Get(std::string_view name) const
{
const StmtColumn* col = GetColumn(name);
return col ? col->Get<T>() : std::nullopt;
}
// ---------------------------------------------------------------------------
static time_t MakeTime(const MYSQL_TIME& mt)
{
// buffer mt given in mysql session time zone (assumes local)
std::tm tm{};
tm.tm_year = mt.year - 1900;
tm.tm_mon = mt.month - 1;
tm.tm_mday = mt.day;
tm.tm_hour = mt.hour;
tm.tm_min = mt.minute;
tm.tm_sec = mt.second;
tm.tm_isdst = -1;
return std::mktime(&tm);
}
static int MakeSeconds(const MYSQL_TIME& mt)
{
return (mt.neg ? -1 : 1) * static_cast<int>(mt.hour * 3600 + mt.minute * 60 + mt.second);
}
static uint64_t MakeBits(std::span<const uint8_t> data)
{
// byte stream for bits is in big endian
uint64_t bits = 0;
for (size_t i = 0; i < data.size() && i < sizeof(uint64_t); ++i)
{
bits |= static_cast<uint64_t>(data[data.size() - i - 1] & 0xff) << (i * 8);
}
return bits;
}
template <typename T>
concept has_from_chars = requires (const char* first, const char* last, T value)
{
std::from_chars(first, last, value);
};
template <typename T>
static T FromString(std::string_view sv)
{
if constexpr (std::is_same_v<T, bool>)
{
// return false for empty (zero-length) strings
return !sv.empty();
}
else if constexpr (std::is_same_v<T, float> && !has_from_chars<T>)
{
return std::strtof(std::string(sv).c_str(), nullptr);
}
else if constexpr (std::is_same_v<T, double> && !has_from_chars<T>)
{
return std::strtod(std::string(sv).c_str(), nullptr);
}
else
{
// non numbers return a zero initialized T (could return nullopt instead)
T value = {};
std::from_chars(sv.data(), sv.data() + sv.size(), value);
return value;
}
}
static std::string FormatTime(enum_field_types type, const MYSQL_TIME& mt)
{
switch (type)
{
case MYSQL_TYPE_TIME: // hhh:mm:ss '-838:59:59' to '838:59:59'
return fmt::format("{}{:02d}:{:02d}:{:02d}", mt.neg ? "-" : "", mt.hour, mt.minute, mt.second);
case MYSQL_TYPE_DATE: // YYYY-MM-DD '1000-01-01' to '9999-12-31'
return fmt::format("{}-{:02d}-{:02d}", mt.year, mt.month, mt.day);
case MYSQL_TYPE_DATETIME: // YYYY-MM-DD hh:mm:ss '1000-01-01 00:00:00' to '9999-12-31 23:59:59'
case MYSQL_TYPE_TIMESTAMP: // YYYY-MM-DD hh:mm:ss '1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC
return fmt::format("{}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}", mt.year, mt.month, mt.day, mt.hour, mt.minute, mt.second);
default:
return std::string();
}
}
std::optional<std::string_view> StmtColumn::GetStrView() const
{
if (m_col.is_null)
{
return std::nullopt;
}
switch (m_col.buffer_type)
{
case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
return std::make_optional<std::string_view>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
default:
return std::nullopt;
}
}
std::optional<std::string> StmtColumn::GetStr() const
{
if (m_col.is_null)
{
return std::nullopt;
}
switch (m_col.buffer_type)
{
case MYSQL_TYPE_TINY:
return m_col.is_unsigned ? fmt::format_int(BitCast<uint8_t>()).c_str() : fmt::format_int(BitCast<int8_t>()).c_str();
case MYSQL_TYPE_SHORT:
return m_col.is_unsigned ? fmt::format_int(BitCast<uint16_t>()).c_str() : fmt::format_int(BitCast<int16_t>()).c_str();
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
return m_col.is_unsigned ? fmt::format_int(BitCast<uint32_t>()).c_str() : fmt::format_int(BitCast<int32_t>()).c_str();
case MYSQL_TYPE_LONGLONG:
return m_col.is_unsigned ? fmt::format_int(BitCast<uint64_t>()).c_str() : fmt::format_int(BitCast<int64_t>()).c_str();
case MYSQL_TYPE_FLOAT:
return fmt::format("{}", BitCast<float>());
case MYSQL_TYPE_DOUBLE:
return fmt::format("{}", BitCast<double>());
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
return FormatTime(m_col.buffer_type, BitCast<MYSQL_TIME>());
case MYSQL_TYPE_BIT:
return fmt::format_int(*Get<uint64_t>()).c_str();
case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
return std::make_optional<std::string>(reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length);
default:
return std::nullopt;
}
}
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> StmtColumn::Get() const
{
if (m_col.is_null)
{
return std::nullopt;
}
switch (m_col.buffer_type)
{
case MYSQL_TYPE_TINY:
return m_col.is_unsigned ? static_cast<T>(BitCast<uint8_t>()) : static_cast<T>(BitCast<int8_t>());
case MYSQL_TYPE_SHORT:
return m_col.is_unsigned ? static_cast<T>(BitCast<uint16_t>()) : static_cast<T>(BitCast<int16_t>());
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
return m_col.is_unsigned ? static_cast<T>(BitCast<uint32_t>()) : static_cast<T>(BitCast<int32_t>());
case MYSQL_TYPE_LONGLONG:
return m_col.is_unsigned ? static_cast<T>(BitCast<uint64_t>()) : static_cast<T>(BitCast<int64_t>());
case MYSQL_TYPE_FLOAT:
return static_cast<T>(BitCast<float>());
case MYSQL_TYPE_DOUBLE:
return static_cast<T>(BitCast<double>());
case MYSQL_TYPE_TIME: // return as total seconds
return static_cast<T>(MakeSeconds(BitCast<MYSQL_TIME>()));
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP: // return as epoch timestamp
return static_cast<T>(MakeTime(BitCast<MYSQL_TIME>()));
case MYSQL_TYPE_BIT:
return static_cast<T>(MakeBits({ m_col.buffer.data(), m_col.length }));
case MYSQL_TYPE_NEWDECIMAL:
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_VAR_STRING:
case MYSQL_TYPE_STRING:
return FromString<T>({ reinterpret_cast<const char*>(m_col.buffer.data()), m_col.length });
default:
return std::nullopt;
}
}
// ---------------------------------------------------------------------------
// explicit template instantiations for supported types
template void PreparedStmt::BindInput(size_t, std::string_view);
template void PreparedStmt::BindInput(size_t, std::nullptr_t);
template StmtResult PreparedStmt::Execute(const std::vector<std::string_view>&);
template StmtResult PreparedStmt::Execute(const std::vector<std::string>&);
template StmtResult PreparedStmt::Execute(const std::vector<const char*>&);
#define INSTANTIATE(T) \
template void PreparedStmt::BindInput(size_t, T); \
template StmtResult PreparedStmt::Execute(const std::vector<T>&); \
template std::optional<T> StmtRow::Get(size_t) const; \
template std::optional<T> StmtRow::Get(std::string_view) const; \
template std::optional<T> StmtColumn::Get() const;
INSTANTIATE(bool);
INSTANTIATE(int8_t);
INSTANTIATE(uint8_t);
INSTANTIATE(int16_t);
INSTANTIATE(uint16_t);
INSTANTIATE(int32_t);
INSTANTIATE(uint32_t);
INSTANTIATE(int64_t);
INSTANTIATE(uint64_t);
INSTANTIATE(float);
INSTANTIATE(double);
} // namespace mysql
-221
View File
@@ -1,221 +0,0 @@
#pragma once
#include "mysql.h"
#include <cassert>
#include <cstring>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
class Mutex;
namespace mysql
{
// support MySQL 8.0.1+ API which removed the my_bool type
#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80001
using my_bool = bool;
#endif
template <typename>
inline constexpr bool false_v = false;
namespace impl
{
struct Bind
{
std::vector<uint8_t> buffer;
unsigned long length = 0;
my_bool is_null = false;
my_bool error = false;
};
struct BindColumn : Bind
{
int index = 0;
std::string name;
bool is_unsigned = false;
enum_field_types buffer_type = {};
};
} // namespace impl
// ---------------------------------------------------------------------------
struct StmtOptions
{
// Enable buffering (storing) entire result set after executing a statement
bool buffer_results = true;
// Enable MySQL to update max_length of fields in execute result set (requires buffering)
bool use_max_length = true;
};
// ---------------------------------------------------------------------------
// Holds ownership of bound column value buffer
class StmtColumn
{
public:
int Index() const { return m_col.index; }
bool IsNull() const { return m_col.is_null; }
bool IsUnsigned() const { return m_col.is_unsigned; }
enum_field_types Type() const { return m_col.buffer_type; }
const std::string& Name() const { return m_col.name; }
// Get view of column value buffer
std::span<const uint8_t> GetBuf() const { return { m_col.buffer.data(), m_col.length }; }
// Get view of column string value. Returns nullopt if value is NULL or not a string
std::optional<std::string_view> GetStrView() const;
// Get column value as string. Returns nullopt if value is NULL or field type unsupported
std::optional<std::string> GetStr() const;
// Get column value as numeric T. Returns nullopt if value NULL or field type unsupported
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> Get() const;
private:
// uses memcpy for type punning buffer data to avoid UB with strict aliasing
template <typename T>
T BitCast() const
{
T val;
assert(sizeof(T) == m_col.length);
memcpy(&val, m_col.buffer.data(), sizeof(T));
return val;
}
friend class PreparedStmt; // access to allocate and bind buffers
friend class StmtResult; // access to resize truncated buffers
impl::BindColumn m_col;
};
// ---------------------------------------------------------------------------
// Provides a non-owning view of PreparedStmt column value buffers
// Evaluates false if it does not contain a valid row
class StmtRow
{
public:
StmtRow() = default;
StmtRow(std::span<const StmtColumn> columns) : m_columns(columns) {};
explicit operator bool() const noexcept { return !m_columns.empty(); }
int ColumnCount() const { return static_cast<int>(m_columns.size()); }
const StmtColumn* GetColumn(size_t index) const;
const StmtColumn* GetColumn(std::string_view name) const;
// Get specified column value as string
// Returns nullopt if column invalid, value is NULL, or field type unsupported
std::optional<std::string> operator[](size_t index) const;
std::optional<std::string> operator[](std::string_view name) const;
std::optional<std::string> GetStr(size_t index) const;
std::optional<std::string> GetStr(std::string_view name) const;
// Get specified column value as numeric T
// Returns nullopt if column invalid, value is NULL, or field type unsupported
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> Get(size_t index) const;
template <typename T> requires std::is_arithmetic_v<T>
std::optional<T> Get(std::string_view name) const;
auto begin() const { return m_columns.begin(); }
auto end() const { return m_columns.end(); }
private:
std::span<const StmtColumn> m_columns;
};
// ---------------------------------------------------------------------------
// Result meta data for an executed prepared statement
class StmtResult
{
public:
StmtResult() = default;
StmtResult(MYSQL_STMT* stmt, size_t columns);
int ColumnCount() const { return m_num_cols; }
uint64_t RowCount() const { return m_num_rows; }
uint64_t RowsAffected() const { return m_affected; }
uint64_t LastInsertID() const { return m_insert_id; }
private:
int m_num_cols = 0;
uint64_t m_num_rows = 0;
uint64_t m_affected = 0;
uint64_t m_insert_id = 0;
};
// ---------------------------------------------------------------------------
class PreparedStmt
{
public:
// Supported argument types for execute
using param_t = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t,
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 = {});
const std::string& GetQuery() const { return m_query; }
StmtOptions GetOptions() const { return m_options; }
void SetOptions(StmtOptions options) { m_options = options; }
void FreeResult() { mysql_stmt_free_result(m_stmt.get()); }
// Execute the prepared statement with specified arguments
// Throws exception on error
template <typename T>
StmtResult Execute(const std::vector<T>& args);
StmtResult Execute(const std::vector<param_t>& args);
StmtResult Execute();
// Fetch the next row into column buffers (overwrites previous row values)
// Return value evaluates false if no more rows to fetch
// Throws exception on error
StmtRow Fetch();
private:
void CheckArgs(size_t argc);
StmtResult DoExecute();
void BindResults();
void FetchTruncated();
int GetResultBufferSize(const MYSQL_FIELD& field) const;
void ThrowError(const std::string& error);
std::string GetStmtError();
// bind an input value to a query parameter by index
template <typename T>
void BindInput(size_t index, T value);
void BindInput(size_t index, const char* str);
void BindInput(size_t index, const std::string& str);
struct StmtDeleter
{
Mutex* mutex = nullptr;
void operator()(MYSQL_STMT* stmt) noexcept;
};
private:
std::unique_ptr<MYSQL_STMT, StmtDeleter> m_stmt;
std::vector<MYSQL_BIND> m_params; // input binds
std::vector<MYSQL_BIND> m_results; // result binds
std::vector<impl::Bind> m_inputs; // execute buffers (addresses bound)
std::vector<StmtColumn> m_columns; // fetch buffers (addresses bound)
std::string m_query;
StmtOptions m_options = {};
bool m_need_bind = true;
Mutex* m_mutex = nullptr; // connection mutex
};
} // namespace mysql
File diff suppressed because it is too large Load Diff
@@ -2,8 +2,7 @@
#include "../random.h"
#include "packet.h"
#include "reliable_stream_structs.h"
#include "reliable_stream_pooling.h"
#include "daybreak_structs.h"
#include <uv.h>
#include <chrono>
#include <functional>
@@ -16,7 +15,7 @@ namespace EQ
{
namespace Net
{
enum ReliableStreamProtocolOpcode
enum DaybreakProtocolOpcode
{
OP_Padding = 0x00,
OP_SessionRequest = 0x01,
@@ -55,7 +54,7 @@ namespace EQ
StatusDisconnected
};
enum ReliableStreamEncodeType
enum DaybreakEncodeType
{
EncodeNone = 0,
EncodeCompression = 1,
@@ -72,9 +71,9 @@ namespace EQ
typedef std::chrono::steady_clock::time_point Timestamp;
typedef std::chrono::steady_clock Clock;
struct ReliableStreamConnectionStats
struct DaybreakConnectionStats
{
ReliableStreamConnectionStats() {
DaybreakConnectionStats() {
recv_bytes = 0;
sent_bytes = 0;
recv_packets = 0;
@@ -134,14 +133,14 @@ namespace EQ
uint64_t bytes_before_encode;
};
class ReliableStreamConnectionManager;
class ReliableStreamConnection;
class ReliableStreamConnection
class DaybreakConnectionManager;
class DaybreakConnection;
class DaybreakConnection
{
public:
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const ReliableStreamConnect &connect, const std::string &endpoint, int port);
ReliableStreamConnection(ReliableStreamConnectionManager *owner, const std::string &endpoint, int port);
~ReliableStreamConnection();
DaybreakConnection(DaybreakConnectionManager *owner, const DaybreakConnect &connect, const std::string &endpoint, int port);
DaybreakConnection(DaybreakConnectionManager *owner, const std::string &endpoint, int port);
~DaybreakConnection();
const std::string& RemoteEndpoint() const { return m_endpoint; }
int RemotePort() const { return m_port; }
@@ -151,23 +150,23 @@ namespace EQ
void QueuePacket(Packet &p, int stream);
void QueuePacket(Packet &p, int stream, bool reliable);
ReliableStreamConnectionStats GetStats();
DaybreakConnectionStats GetStats();
void ResetStats();
size_t GetRollingPing() const { return m_rolling_ping; }
DbProtocolStatus GetStatus() const { return m_status; }
const ReliableStreamEncodeType* GetEncodePasses() const { return m_encode_passes; }
const ReliableStreamConnectionManager* GetManager() const { return m_owner; }
ReliableStreamConnectionManager* GetManager() { return m_owner; }
const DaybreakEncodeType* GetEncodePasses() const { return m_encode_passes; }
const DaybreakConnectionManager* GetManager() const { return m_owner; }
DaybreakConnectionManager* GetManager() { return m_owner; }
private:
ReliableStreamConnectionManager *m_owner;
DaybreakConnectionManager *m_owner;
std::string m_endpoint;
int m_port;
uint32_t m_connect_code;
uint32_t m_encode_key;
uint32_t m_max_packet_size;
uint32_t m_crc_bytes;
ReliableStreamEncodeType m_encode_passes[2];
DaybreakEncodeType m_encode_passes[2];
Timestamp m_last_send;
Timestamp m_last_recv;
@@ -176,19 +175,13 @@ namespace EQ
std::list<DynamicPacket> m_buffered_packets;
size_t m_buffered_packets_length;
std::unique_ptr<char[]> m_combined;
ReliableStreamConnectionStats m_stats;
DaybreakConnectionStats m_stats;
Timestamp m_last_session_stats;
size_t m_rolling_ping;
Timestamp m_close_time;
double m_outgoing_budget;
// resend tracking
size_t m_resend_packets_sent = 0;
size_t m_resend_bytes_sent = 0;
bool m_acked_since_last_resend = false;
Timestamp m_last_ack;
struct ReliableStreamSentPacket
struct DaybreakSentPacket
{
DynamicPacket packet;
Timestamp last_sent;
@@ -197,9 +190,9 @@ namespace EQ
size_t resend_delay;
};
struct ReliableStream
struct DaybreakStream
{
ReliableStream() {
DaybreakStream() {
sequence_in = 0;
sequence_out = 0;
fragment_current_bytes = 0;
@@ -214,11 +207,11 @@ namespace EQ
uint32_t fragment_current_bytes;
uint32_t fragment_total_bytes;
std::map<uint16_t, ReliableStreamSentPacket> sent_packets;
std::map<uint16_t, DaybreakSentPacket> sent_packets;
};
ReliableStream m_streams[4];
std::weak_ptr<ReliableStreamConnection> m_self;
DaybreakStream m_streams[4];
std::weak_ptr<DaybreakConnection> m_self;
void Process();
void ProcessPacket(Packet &p);
@@ -251,12 +244,12 @@ namespace EQ
void FlushBuffer();
SequenceOrder CompareSequence(uint16_t expected, uint16_t actual) const;
friend class ReliableStreamConnectionManager;
friend class DaybreakConnectionManager;
};
struct ReliableStreamConnectionManagerOptions
struct DaybreakConnectionManagerOptions
{
ReliableStreamConnectionManagerOptions() {
DaybreakConnectionManagerOptions() {
max_connection_count = 0;
keepalive_delay_ms = 9000;
resend_delay_ms = 30;
@@ -268,8 +261,8 @@ namespace EQ
connect_stale_ms = 5000;
crc_length = 2;
max_packet_size = 512;
encode_passes[0] = ReliableStreamEncodeType::EncodeNone;
encode_passes[1] = ReliableStreamEncodeType::EncodeNone;
encode_passes[0] = DaybreakEncodeType::EncodeNone;
encode_passes[1] = DaybreakEncodeType::EncodeNone;
port = 0;
hold_size = 512;
hold_length_ms = 50;
@@ -299,28 +292,28 @@ namespace EQ
double tic_rate_hertz;
size_t resend_timeout;
size_t connection_close_time;
ReliableStreamEncodeType encode_passes[2];
DaybreakEncodeType encode_passes[2];
int port;
double outgoing_data_rate;
};
class ReliableStreamConnectionManager
class DaybreakConnectionManager
{
public:
ReliableStreamConnectionManager();
ReliableStreamConnectionManager(const ReliableStreamConnectionManagerOptions &opts);
~ReliableStreamConnectionManager();
DaybreakConnectionManager();
DaybreakConnectionManager(const DaybreakConnectionManagerOptions &opts);
~DaybreakConnectionManager();
void Connect(const std::string &addr, int port);
void Process();
void UpdateDataBudget();
void ProcessResend();
void OnNewConnection(std::function<void(std::shared_ptr<ReliableStreamConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet &)> func) { m_on_packet_recv = func; }
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
void OnErrorMessage(std::function<void(const std::string&)> func) { m_on_error_message = func; }
ReliableStreamConnectionManagerOptions& GetOptions() { return m_options; }
DaybreakConnectionManagerOptions& GetOptions() { return m_options; }
private:
void Attach(uv_loop_t *loop);
void Detach();
@@ -329,18 +322,18 @@ namespace EQ
uv_timer_t m_timer;
uv_udp_t m_socket;
uv_loop_t *m_attached;
ReliableStreamConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<ReliableStreamConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<ReliableStreamConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<ReliableStreamConnection>, const Packet&)> m_on_packet_recv;
DaybreakConnectionManagerOptions m_options;
std::function<void(std::shared_ptr<DaybreakConnection>)> m_on_new_connection;
std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::function<void(std::shared_ptr<DaybreakConnection>, const Packet&)> m_on_packet_recv;
std::function<void(const std::string&)> m_on_error_message;
std::map<std::pair<std::string, int>, std::shared_ptr<ReliableStreamConnection>> m_connections;
std::map<std::pair<std::string, int>, std::shared_ptr<DaybreakConnection>> m_connections;
void ProcessPacket(const std::string &endpoint, int port, const char *data, size_t size);
std::shared_ptr<ReliableStreamConnection> FindConnectionByEndpoint(std::string addr, int port);
std::shared_ptr<DaybreakConnection> FindConnectionByEndpoint(std::string addr, int port);
void SendDisconnect(const std::string &addr, int port);
friend class ReliableStreamConnection;
friend class DaybreakConnection;
};
}
}
@@ -8,7 +8,7 @@ namespace EQ
{
namespace Net
{
struct ReliableStreamHeader
struct DaybreakHeader
{
static size_t size() { return 2; }
uint8_t zero;
@@ -22,7 +22,7 @@ namespace EQ
}
};
struct ReliableStreamConnect
struct DaybreakConnect
{
static size_t size() { return 14; }
uint8_t zero;
@@ -42,7 +42,7 @@ namespace EQ
}
};
struct ReliableStreamConnectReply
struct DaybreakConnectReply
{
static size_t size() { return 17; }
uint8_t zero;
@@ -68,7 +68,7 @@ namespace EQ
}
};
struct ReliableStreamDisconnect
struct DaybreakDisconnect
{
static size_t size() { return 8; }
uint8_t zero;
@@ -84,7 +84,7 @@ namespace EQ
}
};
struct ReliableStreamReliableHeader
struct DaybreakReliableHeader
{
static size_t size() { return 4; }
uint8_t zero;
@@ -100,10 +100,10 @@ namespace EQ
}
};
struct ReliableStreamReliableFragmentHeader
struct DaybreakReliableFragmentHeader
{
static size_t size() { return 4 + ReliableStreamReliableHeader::size(); }
ReliableStreamReliableHeader reliable;
static size_t size() { return 4 + DaybreakReliableHeader::size(); }
DaybreakReliableHeader reliable;
uint32_t total_size;
template <class Archive>
@@ -114,7 +114,7 @@ namespace EQ
}
};
struct ReliableStreamSessionStatRequest
struct DaybreakSessionStatRequest
{
static size_t size() { return 40; }
uint8_t zero;
@@ -144,7 +144,7 @@ namespace EQ
}
};
struct ReliableStreamSessionStatResponse
struct DaybreakSessionStatResponse
{
static size_t size() { return 40; }
uint8_t zero;
@@ -171,4 +171,3 @@ namespace EQ
};
}
}
+12 -12
View File
@@ -1,11 +1,11 @@
#include "eqstream.h"
#include "../eqemu_logsys.h"
EQ::Net::EQStreamManager::EQStreamManager(const EQStreamManagerInterfaceOptions &options) : EQStreamManagerInterface(options), m_reliable_stream(options.reliable_stream_options)
EQ::Net::EQStreamManager::EQStreamManager(const EQStreamManagerInterfaceOptions &options) : EQStreamManagerInterface(options), m_daybreak(options.daybreak_options)
{
m_reliable_stream.OnNewConnection(std::bind(&EQStreamManager::ReliableStreamNewConnection, this, std::placeholders::_1));
m_reliable_stream.OnConnectionStateChange(std::bind(&EQStreamManager::ReliableStreamConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_reliable_stream.OnPacketRecv(std::bind(&EQStreamManager::ReliableStreamPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
m_daybreak.OnNewConnection(std::bind(&EQStreamManager::DaybreakNewConnection, this, std::placeholders::_1));
m_daybreak.OnConnectionStateChange(std::bind(&EQStreamManager::DaybreakConnectionStateChange, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_daybreak.OnPacketRecv(std::bind(&EQStreamManager::DaybreakPacketRecv, this, std::placeholders::_1, std::placeholders::_2));
}
EQ::Net::EQStreamManager::~EQStreamManager()
@@ -15,11 +15,11 @@ EQ::Net::EQStreamManager::~EQStreamManager()
void EQ::Net::EQStreamManager::SetOptions(const EQStreamManagerInterfaceOptions &options)
{
m_options = options;
auto &opts = m_reliable_stream.GetOptions();
opts = options.reliable_stream_options;
auto &opts = m_daybreak.GetOptions();
opts = options.daybreak_options;
}
void EQ::Net::EQStreamManager::ReliableStreamNewConnection(std::shared_ptr<ReliableStreamConnection> connection)
void EQ::Net::EQStreamManager::DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection)
{
std::shared_ptr<EQStream> stream(new EQStream(this, connection));
m_streams.emplace(std::make_pair(connection, stream));
@@ -28,7 +28,7 @@ void EQ::Net::EQStreamManager::ReliableStreamNewConnection(std::shared_ptr<Relia
}
}
void EQ::Net::EQStreamManager::ReliableStreamConnectionStateChange(std::shared_ptr<ReliableStreamConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
void EQ::Net::EQStreamManager::DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
@@ -42,7 +42,7 @@ void EQ::Net::EQStreamManager::ReliableStreamConnectionStateChange(std::shared_p
}
}
void EQ::Net::EQStreamManager::ReliableStreamPacketRecv(std::shared_ptr<ReliableStreamConnection> connection, const Packet &p)
void EQ::Net::EQStreamManager::DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p)
{
auto iter = m_streams.find(connection);
if (iter != m_streams.end()) {
@@ -53,7 +53,7 @@ void EQ::Net::EQStreamManager::ReliableStreamPacketRecv(std::shared_ptr<Reliable
}
}
EQ::Net::EQStream::EQStream(EQStreamManagerInterface *owner, std::shared_ptr<ReliableStreamConnection> connection)
EQ::Net::EQStream::EQStream(EQStreamManagerInterface *owner, std::shared_ptr<DaybreakConnection> connection)
{
m_owner = owner;
m_connection = connection;
@@ -71,7 +71,7 @@ void EQ::Net::EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req)
OpcodeManager::EmuToName(p->GetOpcode()),
(*m_opcode_manager)->EmuToEQ(p->GetOpcode()),
p->Size(),
(EQEmuLogSys::Instance()->IsLogEnabled(Logs::Detail, Logs::PacketServerClient) ? DumpPacketToString(p) : "")
(LogSys.IsLogEnabled(Logs::Detail, Logs::PacketServerClient) ? DumpPacketToString(p) : "")
);
if (m_opcode_manager && *m_opcode_manager) {
@@ -235,7 +235,7 @@ EQStreamState EQ::Net::EQStream::GetState() {
EQ::Net::EQStream::Stats EQ::Net::EQStream::GetStats() const
{
Stats ret;
ret.ReliableStreamStats = m_connection->GetStats();
ret.DaybreakStats = m_connection->GetStats();
for (int i = 0; i < _maxEmuOpcode; ++i) {
ret.RecvCount[i] = 0;
+8 -8
View File
@@ -3,7 +3,7 @@
#include "../eq_packet.h"
#include "../eq_stream_intf.h"
#include "../opcodemgr.h"
#include "reliable_stream_connection.h"
#include "daybreak_connection.h"
#include <vector>
#include <deque>
#include <unordered_map>
@@ -23,21 +23,21 @@ namespace EQ
void OnNewConnection(std::function<void(std::shared_ptr<EQStream>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
private:
ReliableStreamConnectionManager m_reliable_stream;
DaybreakConnectionManager m_daybreak;
std::function<void(std::shared_ptr<EQStream>)> m_on_new_connection;
std::function<void(std::shared_ptr<EQStream>, DbProtocolStatus, DbProtocolStatus)> m_on_connection_state_change;
std::map<std::shared_ptr<ReliableStreamConnection>, std::shared_ptr<EQStream>> m_streams;
std::map<std::shared_ptr<DaybreakConnection>, std::shared_ptr<EQStream>> m_streams;
void ReliableStreamNewConnection(std::shared_ptr<ReliableStreamConnection> connection);
void ReliableStreamConnectionStateChange(std::shared_ptr<ReliableStreamConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void ReliableStreamPacketRecv(std::shared_ptr<ReliableStreamConnection> connection, const Packet &p);
void DaybreakNewConnection(std::shared_ptr<DaybreakConnection> connection);
void DaybreakConnectionStateChange(std::shared_ptr<DaybreakConnection> connection, DbProtocolStatus from, DbProtocolStatus to);
void DaybreakPacketRecv(std::shared_ptr<DaybreakConnection> connection, const Packet &p);
friend class EQStream;
};
class EQStream : public EQStreamInterface
{
public:
EQStream(EQStreamManagerInterface *parent, std::shared_ptr<ReliableStreamConnection> connection);
EQStream(EQStreamManagerInterface *parent, std::shared_ptr<DaybreakConnection> connection);
~EQStream();
virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req = true);
@@ -67,7 +67,7 @@ namespace EQ
virtual EQStreamManagerInterface* GetManager() const;
private:
EQStreamManagerInterface *m_owner;
std::shared_ptr<ReliableStreamConnection> m_connection;
std::shared_ptr<DaybreakConnection> m_connection;
OpcodeManager **m_opcode_manager;
std::deque<std::unique_ptr<EQ::Net::Packet>> m_packet_queue;
std::unordered_map<int, int> m_packet_recv_count;
-123
View File
@@ -1,123 +0,0 @@
#pragma once
#include <optional>
#include <atomic>
#include <memory>
#include <array>
#include <vector>
#include <mutex>
#include <iostream>
#include "../eqemu_logsys.h"
#include <uv.h>
constexpr size_t UDP_BUFFER_SIZE = 512;
struct EmbeddedContext {
size_t pool_index;
class SendBufferPool* pool;
};
class SendBufferPool {
public:
explicit SendBufferPool(size_t initial_capacity = 64)
: m_capacity(initial_capacity), m_head(0)
{
LogNetClient("[SendBufferPool] Initializing with capacity [{}]", (int)m_capacity);
m_pool.reserve(m_capacity);
m_locks = std::make_unique<std::atomic_bool[]>(m_capacity);
for (size_t i = 0; i < m_capacity; ++i) {
auto* req = new PooledUdpSend();
req->context.pool_index = i;
req->context.pool = this;
req->uv_req.data = &req->context;
m_pool.emplace_back(std::unique_ptr<PooledUdpSend>(req));
m_locks[i].store(false, std::memory_order_relaxed);
}
}
std::optional<std::tuple<uv_udp_send_t*, char*, EmbeddedContext*>> acquire() {
size_t cap = m_capacity.load(std::memory_order_acquire);
for (size_t i = 0; i < cap; ++i) {
size_t index = m_head.fetch_add(1, std::memory_order_relaxed) % cap;
bool expected = false;
if (m_locks[index].compare_exchange_strong(expected, true)) {
auto* req = m_pool[index].get();
LogNetClientDetail("[SendBufferPool] Acquired [{}]", index);
return std::make_tuple(&req->uv_req, req->buffer.data(), &req->context);
}
}
LogNetClient("[SendBufferPool] Growing from [{}] to [{}]", cap, cap * 2);
grow();
return acquireAfterGrowth();
}
void release(EmbeddedContext* ctx) {
if (!ctx || ctx->pool != this || ctx->pool_index >= m_capacity.load(std::memory_order_acquire)) {
LogNetClient("[SendBufferPool] Invalid context release [{}]", ctx ? ctx->pool_index : -1);
return;
}
m_locks[ctx->pool_index].store(false, std::memory_order_release);
LogNetClientDetail("[SendBufferPool] Released [{}]", ctx->pool_index);
}
private:
struct PooledUdpSend {
uv_udp_send_t uv_req;
std::array<char, UDP_BUFFER_SIZE> buffer;
EmbeddedContext context;
};
std::vector<std::unique_ptr<PooledUdpSend>> m_pool;
std::unique_ptr<std::atomic_bool[]> m_locks;
std::atomic<size_t> m_capacity;
std::atomic<size_t> m_head;
std::mutex m_grow_mutex;
void grow() {
std::lock_guard<std::mutex> lock(m_grow_mutex);
size_t old_cap = m_capacity.load(std::memory_order_acquire);
size_t new_cap = old_cap * 2;
m_pool.reserve(new_cap);
for (size_t i = old_cap; i < new_cap; ++i) {
auto* req = new PooledUdpSend();
req->context.pool_index = i;
req->context.pool = this;
req->uv_req.data = &req->context;
m_pool.emplace_back(std::unique_ptr<PooledUdpSend>(req));
}
auto new_locks = std::make_unique<std::atomic_bool[]>(new_cap);
for (size_t i = 0; i < old_cap; ++i) {
new_locks[i].store(m_locks[i].load(std::memory_order_acquire));
}
for (size_t i = old_cap; i < new_cap; ++i) {
new_locks[i].store(false, std::memory_order_relaxed);
}
m_locks = std::move(new_locks);
m_capacity.store(new_cap, std::memory_order_release);
LogNetClient("[SendBufferPool] Grew to [{}] from [{}]", new_cap, old_cap);
}
std::optional<std::tuple<uv_udp_send_t*, char*, EmbeddedContext*>> acquireAfterGrowth() {
size_t cap = m_capacity.load(std::memory_order_acquire);
for (size_t i = 0; i < cap; ++i) {
size_t index = m_head.fetch_add(1, std::memory_order_relaxed) % cap;
bool expected = false;
if (m_locks[index].compare_exchange_strong(expected, true)) {
auto* req = m_pool[index].get();
LogNetClient("[SendBufferPool] Acquired after grow [{}]", index);
return std::make_tuple(&req->uv_req, req->buffer.data(), &req->context);
}
}
return std::nullopt;
}
};
+3 -3
View File
@@ -62,15 +62,15 @@ void EQ::Net::ServertalkClient::Connect()
m_connecting = true;
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
if (connection == nullptr) {
LogNetTCP("Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connecting = false;
return;
}
LogNetTCP("Connected to {0}:{1}", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port);
m_connection = connection;
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
LogNetTCP("Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connection.reset();
});
@@ -23,9 +23,6 @@ namespace EQ
bool Connected() const { return m_connecting != true; }
std::shared_ptr<EQ::Net::TCPConnection> Handle() { return m_connection; }
const std::unique_ptr<EQ::Timer> &GetTimer() const { return m_timer; }
private:
void Connect();
void ProcessData(EQ::Net::TCPConnection *c, const unsigned char *data, size_t length);
@@ -58,15 +58,15 @@ void EQ::Net::ServertalkLegacyClient::Connect()
m_connecting = true;
EQ::Net::TCPConnection::Connect(m_addr, m_port, false, [this](std::shared_ptr<EQ::Net::TCPConnection> connection) {
if (connection == nullptr) {
LogNetTCP("Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Error connecting to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connecting = false;
return;
}
LogNetTCP("Connected to {0}:{1}", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Connected to {0}:{1}", m_addr, m_port);
m_connection = connection;
m_connection->OnDisconnect([this](EQ::Net::TCPConnection *c) {
LogNetTCP("Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
LogF(Logs::General, Logs::TCPConnection, "Connection lost to {0}:{1}, attempting to reconnect...", m_addr, m_port);
m_connection.reset();
});
+6 -6
View File
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
return;
if (opcode == ServerOP_UsertoWorldReq) {
auto req_in = (UsertoWorldRequest*)p.Data();
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
}
if (opcode == ServerOP_LSClientAuth) {
auto req_in = (ClientAuth*)p.Data();
auto req_in = (ClientAuth_Struct*)p.Data();
EQ::Net::DynamicPacket req;
size_t i = 0;
@@ -54,7 +54,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
req.PutData(i, req_in->key, 30); i += 30;
req.PutUInt8(i, req_in->lsadmin); i += 1;
req.PutUInt16(i, req_in->is_world_admin); i += 2;
req.PutUInt32(i, req_in->ip_address); i += 4;
req.PutUInt32(i, req_in->ip); i += 4;
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
EQ::Net::DynamicPacket out;
@@ -123,7 +123,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
{
size_t current = 0;
size_t total = m_buffer.size();
constexpr size_t ls_info_size = sizeof(LoginserverNewWorldRequest);
constexpr size_t ls_info_size = sizeof(ServerNewLSInfo_Struct);
while (current < total) {
auto left = total - current;
@@ -138,7 +138,7 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
//this creates a small edge case where the exact size of a
//packet from the modern protocol can't be "43061256"
//so in send we pad it one byte if that's the case
if (leg_opcode == ServerOP_NewLSInfo && leg_size == sizeof(LoginserverNewWorldRequest)) {
if (leg_opcode == ServerOP_NewLSInfo && leg_size == sizeof(ServerNewLSInfo_Struct)) {
m_legacy_mode = true;
m_identifier = "World";
m_parent->ConnectionIdentified(this);
@@ -319,7 +319,7 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
size_t message_len = length;
EQ::Net::StaticPacket packet(&data[0], message_len);
const auto is_detail_enabled = EQEmuLogSys::Instance()->IsLogEnabled(Logs::Detail, Logs::PacketServerToServer);
const auto is_detail_enabled = LogSys.IsLogEnabled(Logs::Detail, Logs::PacketServerToServer);
if (opcode != ServerOP_KeepAlive || is_detail_enabled) {
LogPacketServerToServer(
"[{:#06x}] Size [{}] {}",
+41 -96
View File
@@ -1,8 +1,5 @@
#include "tcp_connection.h"
#include "../event/event_loop.h"
#include <iostream>
WriteReqPool tcp_write_pool;
void on_close_handle(uv_handle_t* handle) {
delete (uv_tcp_t *)handle;
@@ -67,37 +64,34 @@ void EQ::Net::TCPConnection::Connect(const std::string &addr, int port, bool ipv
});
}
void EQ::Net::TCPConnection::Start()
{
uv_read_start(
(uv_stream_t *) m_socket, [](uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
if (suggested_size > 65536) {
void EQ::Net::TCPConnection::Start() {
uv_read_start((uv_stream_t*)m_socket, [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = new char[suggested_size];
buf->len = suggested_size;
return;
}
static thread_local char temp_buf[65536];
buf->base = temp_buf;
buf->len = 65536;
}, [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
auto *connection = (TCPConnection *) stream->data;
TCPConnection *connection = (TCPConnection*)stream->data;
if (nread > 0) {
connection->Read(buf->base, nread);
}
else if (nread == UV_EOF) {
connection->Disconnect();
}
else if (nread < 0) {
connection->Disconnect();
}
if (buf->len > 65536) {
if (buf->base) {
delete[] buf->base;
}
}
);
else if (nread == UV_EOF) {
if (buf->base) {
delete[] buf->base;
}
}
else if (nread < 0) {
connection->Disconnect();
if (buf->base) {
delete[] buf->base;
}
}
});
}
void EQ::Net::TCPConnection::OnRead(std::function<void(TCPConnection*, const unsigned char*, size_t)> cb)
@@ -134,91 +128,42 @@ void EQ::Net::TCPConnection::Read(const char *data, size_t count)
}
}
void EQ::Net::TCPConnection::Write(const char* data, size_t count) {
if (!m_socket || !data || count == 0) {
std::cerr << "TCPConnection::Write - Invalid socket or data\n";
void EQ::Net::TCPConnection::Write(const char *data, size_t count)
{
if (!m_socket) {
return;
}
if (count <= TCP_BUFFER_SIZE) {
// Fast path: use pooled request with embedded buffer
auto req_opt = tcp_write_pool.acquire();
if (!req_opt) {
std::cerr << "TCPConnection::Write - Out of write requests\n";
return;
}
struct WriteBaton
{
TCPConnection *connection;
char *buffer;
};
TCPWriteReq* write_req = *req_opt;
// Fill buffer and set context
memcpy(write_req->buffer.data(), data, count);
write_req->connection = this;
write_req->magic = 0xC0FFEE;
uv_buf_t buf = uv_buf_init(write_req->buffer.data(), static_cast<unsigned int>(count));
int result = uv_write(
&write_req->req,
reinterpret_cast<uv_stream_t*>(m_socket),
&buf,
1,
[](uv_write_t* req, int status) {
auto* full_req = reinterpret_cast<TCPWriteReq*>(req);
if (full_req->magic != 0xC0FFEE) {
std::cerr << "uv_write callback - invalid magic, skipping release\n";
return;
}
tcp_write_pool.release(full_req);
if (status < 0 && full_req->connection) {
std::cerr << "uv_write failed: " << uv_strerror(status) << std::endl;
full_req->connection->Disconnect();
}
}
);
if (result < 0) {
std::cerr << "uv_write() failed immediately: " << uv_strerror(result) << std::endl;
tcp_write_pool.release(write_req);
}
} else {
// Slow path: allocate heap buffer for large write
LogNetTCP("[TCPConnection] Large write of [{}] bytes, using heap buffer", count);
char* heap_buffer = new char[count];
memcpy(heap_buffer, data, count);
WriteBaton *baton = new WriteBaton;
baton->connection = this;
baton->buffer = new char[count];
uv_write_t *write_req = new uv_write_t;
write_req->data = heap_buffer;
memset(write_req, 0, sizeof(uv_write_t));
write_req->data = baton;
uv_buf_t send_buffers[1];
uv_buf_t buf = uv_buf_init(heap_buffer, static_cast<unsigned int>(count));
memcpy(baton->buffer, data, count);
send_buffers[0] = uv_buf_init(baton->buffer, count);
int result = uv_write(
write_req,
reinterpret_cast<uv_stream_t*>(m_socket),
&buf,
1,
[](uv_write_t* req, int status) {
char* data = static_cast<char*>(req->data);
delete[] data;
uv_write(write_req, (uv_stream_t*)m_socket, send_buffers, 1, [](uv_write_t* req, int status) {
WriteBaton *baton = (WriteBaton*)req->data;
delete[] baton->buffer;
delete req;
if (status < 0) {
std::cerr << "uv_write (large) failed: " << uv_strerror(status) << std::endl;
}
}
);
if (result < 0) {
std::cerr << "uv_write() (large) failed immediately: " << uv_strerror(result) << std::endl;
delete[] heap_buffer;
delete write_req;
}
}
baton->connection->Disconnect();
}
delete baton;
});
}
std::string EQ::Net::TCPConnection::LocalIP() const
{
-1
View File
@@ -1,6 +1,5 @@
#pragma once
#include "tcp_connection_pooling.h"
#include <functional>
#include <string>
#include <memory>

Some files were not shown because too many files have changed in this diff Show More