mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-24 01:42:29 +00:00
Compare commits
243 Commits
hp-fix
...
xackery/elixir
| Author | SHA1 | Date | |
|---|---|---|---|
| cd01ed87df | |||
| 8a79994b4e | |||
| 93a661a434 | |||
| 3e8796bb4c | |||
| c23fc4ade7 | |||
| 3b628c1ace | |||
| 27f8ae3999 | |||
| 80a891e541 | |||
| 776449aa3d | |||
| cef352f0ac | |||
| e8607a0c78 | |||
| 9c55cf9a8e | |||
| a9d1034298 | |||
| 8b83a13560 | |||
| 3c87480553 | |||
| 1a69218045 | |||
| e870ee5e0e | |||
| 908c6c18af | |||
| f591378ed3 | |||
| 0997a8a31e | |||
| 0bf6627fb0 | |||
| fb8539e679 | |||
| f8c2e85f3e | |||
| 110d2a0e10 | |||
| 8d9415191a | |||
| 7178a7e55d | |||
| 9e8d03d92d | |||
| f4a70eff43 | |||
| f9ec45c7ff | |||
| cf8bf9e4fc | |||
| 575237d764 | |||
| e12e8df3ef | |||
| 8d7b7d6cc4 | |||
| 6f79ea117c | |||
| 7b022502da | |||
| 4a376b7859 | |||
| 87cdf7feb1 | |||
| caf32290b8 | |||
| 7d495c56b3 | |||
| dc1c7bb284 | |||
| a6c85babfc | |||
| cbea7045fa | |||
| 099759c477 | |||
| 65197ac027 | |||
| e4bd6f5bd2 | |||
| acf5836253 | |||
| 17c8e8414c | |||
| 239033a269 | |||
| fa07064466 | |||
| 994ef712b2 | |||
| 33c30d3cbb | |||
| d9c8e80bca | |||
| 990729fe21 | |||
| aac0dd2993 | |||
| b17c24d2df | |||
| 32d606c667 | |||
| 6661672e2d | |||
| b5391b9110 | |||
| e306059f43 | |||
| a64e326c68 | |||
| 605b3d3a27 | |||
| 248e6d44db | |||
| 328a94e2d4 | |||
| 211196a722 | |||
| 0b283e60db | |||
| 90871cb3d9 | |||
| bf92845a4a | |||
| f8cbc2faed | |||
| 30fdb18945 | |||
| 062fb73f03 | |||
| 194c71727d | |||
| e1de3d2ae0 | |||
| 7f497f9d32 | |||
| 1cdb1816a2 | |||
| bc82b897c5 | |||
| beb4de0b45 | |||
| 785926a584 | |||
| 5c7972345a | |||
| 886f00ed50 | |||
| b983fac860 | |||
| 7b6decaef3 | |||
| 8d8301fbd7 | |||
| b4aa401210 | |||
| f1bfd6bc2a | |||
| 8c95323728 | |||
| 5874deeffc | |||
| 18cc648c8d | |||
| 9d515b20f2 | |||
| 17aaab1f9d | |||
| 6e26e8953c | |||
| e4138b871b | |||
| 4ac525afc2 | |||
| 05782433b8 | |||
| 9af7122b1d | |||
| 9e7a763482 | |||
| 1231d44b55 | |||
| 873f1f7f34 | |||
| d87db648c3 | |||
| 119c3d14b7 | |||
| 3cda32c213 | |||
| df3161455a | |||
| 4389f84ea5 | |||
| f9855fd097 | |||
| f912814e13 | |||
| 5738958a2a | |||
| d36d11653a | |||
| 7230714cbc | |||
| 6e5bf4b941 | |||
| fb66afd565 | |||
| ef5124d756 | |||
| 987de17e93 | |||
| 060be606e7 | |||
| 1c5f9f2e0f | |||
| 62253cc016 | |||
| 0b18671e91 | |||
| c98f3cfb4c | |||
| 624d11de4e | |||
| 5eb95a95d0 | |||
| da01156673 | |||
| 6a244f16e1 | |||
| 36d10462f7 | |||
| c30dbf6628 | |||
| 657cbbcabe | |||
| 81e7cf5a32 | |||
| edf298685e | |||
| efab0c4b6b | |||
| c838564023 | |||
| d197ee631e | |||
| 3dcddcba04 | |||
| 7823ff5336 | |||
| 11c335a015 | |||
| 07d96ad921 | |||
| 5d522b149b | |||
| 234bd89ed5 | |||
| af5cfb9bed | |||
| 426f9c337b | |||
| 5235dcee95 | |||
| 203ba2d340 | |||
| 9c67421ccc | |||
| 6669fc8214 | |||
| cef873f793 | |||
| 6a962f2591 | |||
| 91adf9c0eb | |||
| 9887580f9a | |||
| b7c62b5242 | |||
| 89a40272c6 | |||
| db369c98c8 | |||
| 10ba5d6046 | |||
| dd1a869531 | |||
| a9e23cf83a | |||
| 783c12590e | |||
| 6689b57a52 | |||
| 7029c699a0 | |||
| 3b9574af14 | |||
| 740f84dc22 | |||
| 55d45f9a98 | |||
| 61d1eeab6f | |||
| 133c1e866c | |||
| b730461894 | |||
| 8b08e22dbc | |||
| fc7c99fb0a | |||
| f1d267bb2d | |||
| 07664eedc0 | |||
| 64b8d7c874 | |||
| ccab07bd66 | |||
| 3a76d9a28e | |||
| 5720a5020d | |||
| b3e9e2099a | |||
| 93acf50bb4 | |||
| ff46a854f9 | |||
| 8c5f26ca5e | |||
| 5560b198ca | |||
| 9a413cf553 | |||
| bb5c491794 | |||
| 2f5d360e53 | |||
| 92e03dccb9 | |||
| 5ffe6284ca | |||
| fb98349bbd | |||
| 00a22ca12e | |||
| 3883adcefc | |||
| 0762ffa3dc | |||
| b70dc64d96 | |||
| 859751f74d | |||
| 91c451b6c5 | |||
| 30c7ed7e45 | |||
| 509b6f2056 | |||
| 558bebe710 | |||
| 08a85c5dae | |||
| d22f9ee294 | |||
| 0aeaf7c3b7 | |||
| c04bcef273 | |||
| 7fcea371c2 | |||
| dd765238f7 | |||
| 2c98a11696 | |||
| d4f14efaa0 | |||
| 27787c247b | |||
| ea9c07aa98 | |||
| 5cd9bfeb70 | |||
| b699196299 | |||
| e89c2aec4a | |||
| 456fb56e82 | |||
| 03ac828134 | |||
| 34b2264d5d | |||
| 933ede40f9 | |||
| 881dc33c9b | |||
| 3faa0d2603 | |||
| 1ce5087e2a | |||
| bf8d94eb35 | |||
| 9aac12f517 | |||
| 7b969173f4 | |||
| ca77d22035 | |||
| ad3bf35397 | |||
| 9b06221be0 | |||
| c0de178173 | |||
| 6a5face0aa | |||
| c15c54e920 | |||
| 24c079dca4 | |||
| 8eef7bb283 | |||
| 80493719f2 | |||
| df9d6bc506 | |||
| 71870cbd1c | |||
| f715ccd368 | |||
| 46edd56acc | |||
| 442850aebb | |||
| d4e752987e | |||
| 9c6a85ff16 | |||
| fa8d8eccc2 | |||
| 6e76f89ca2 | |||
| 9589bf6bf8 | |||
| ce5fa9502f | |||
| 7b1b05a35c | |||
| 38a86edc70 | |||
| 56b9b6f2c4 | |||
| 97dcba70cf | |||
| 6b93130c13 | |||
| 94c1a50cc8 | |||
| 31ab1d4287 | |||
| b6ba493450 | |||
| c613db2338 | |||
| 05ac8499df | |||
| 69244a094d | |||
| 1155673642 | |||
| 930079959c |
Vendored
+2
-1
@@ -9,7 +9,8 @@
|
||||
"defines": [],
|
||||
"compilerPath": "/usr/bin/gcc",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17"
|
||||
"cppStandard": "c++17",
|
||||
"configurationProvider": "ms-vscode.cmake-tools"
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"chrono": "cpp",
|
||||
"xutility": "cpp",
|
||||
"iterator": "cpp",
|
||||
"*.ipp": "cpp"
|
||||
}
|
||||
}
|
||||
Vendored
+14
-14
@@ -6,7 +6,7 @@
|
||||
{
|
||||
"label": "make",
|
||||
"type": "shell",
|
||||
"command": "cd bin && make",
|
||||
"command": "mkdir -p build && cd build && make",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -18,7 +18,7 @@
|
||||
{
|
||||
"label": "make clean",
|
||||
"type": "shell",
|
||||
"command": "cd bin && make clean",
|
||||
"command": "mkdir -p build && cd build && make clean",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -30,7 +30,7 @@
|
||||
{
|
||||
"label": "cmake",
|
||||
"type": "shell",
|
||||
"command": "mkdir -p bin && cd bin && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..",
|
||||
"command": "mkdir -p build && cd build && rm CMakeCache.txt && cmake -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -G 'Unix Makefiles' ..",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -52,7 +52,7 @@
|
||||
{
|
||||
"label": "download maps",
|
||||
"type": "shell",
|
||||
"command": "mkdir -p bin && cd bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip",
|
||||
"command": "mkdir -p build && cd build/bin && wget https://codeload.github.com/Akkadius/EQEmuMaps/zip/master -O maps.zip && unzip -o maps.zip && rm ./maps -rf && mv EQEmuMaps-master maps && rm maps.zip",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -64,7 +64,7 @@
|
||||
{
|
||||
"label": "download quests",
|
||||
"type": "shell",
|
||||
"command": "mkdir -p bin && cd bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests",
|
||||
"command": "mkdir -p build && cd build/bin && cd server && git -C ./quests pull 2> /dev/null || git clone https://github.com/ProjectEQ/projecteqquests.git quests",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -76,7 +76,7 @@
|
||||
{
|
||||
"label": "download eqemu_config",
|
||||
"type": "shell",
|
||||
"command": "mkdir -p bin && cd bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json",
|
||||
"command": "mkdir -p build && cd build/bin && wget --no-check-certificate https://raw.githubusercontent.com/Akkadius/EQEmuInstall/master/eqemu_config_docker.json -O eqemu_config.json",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -88,7 +88,7 @@
|
||||
{
|
||||
"label": "rebuild database (mariadb must be started)",
|
||||
"type": "shell",
|
||||
"command": "mkdir -p bin && cd bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl linux_login_server_setup'",
|
||||
"command": "mkdir -p build && cd build/bin && docker run -i --rm --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu -it eqemu/server:0.0.3 bash -c './eqemu_server.pl source_peq_db && ./eqemu_server.pl check_db_updates && ./eqemu_server.pl assets && ./eqemu_server.pl lua_modules && ./eqemu_server.pl opcodes && ./eqemu_server.pl linux_login_server_setup'",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
@@ -100,7 +100,7 @@
|
||||
{
|
||||
"label": "zone 7000",
|
||||
"type": "shell",
|
||||
"command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000",
|
||||
"command": "docker stop zone7000 | true && docker network create eqemu | true && docker run -i --rm --name zone7000 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7000:7000/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7000:7000",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -109,7 +109,7 @@
|
||||
{
|
||||
"label": "zone 7001",
|
||||
"type": "shell",
|
||||
"command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001",
|
||||
"command": "docker stop zone7001 | true && docker network create eqemu | true && docker run -i --rm --name zone7001 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu -p 7001:7001/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./zone dynamic_zone7001:7001",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -118,7 +118,7 @@
|
||||
{
|
||||
"label": "loginserver",
|
||||
"type": "shell",
|
||||
"command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver",
|
||||
"command": "docker stop loginserver | true && docker network create eqemu | true && docker run -i --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined --privileged -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 --network=eqemu --name loginserver -p 5999:5999/udp -p 5998:5998/udp -e LD_LIBRARY_PATH=/src/ eqemu/server:0.0.3 gdb -ex run --args ./loginserver",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -127,7 +127,7 @@
|
||||
{
|
||||
"label": "shared_memory, world",
|
||||
"type": "shell",
|
||||
"command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world",
|
||||
"command": "docker stop sharedmemory | true && docker stop world | true && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --network=eqemu --name sharedmemory eqemu/server:0.0.3 ./shared_memory && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name world -p 9000:9000 -p 9000:9000/udp -p 9001:9001 -p 9080:9080 eqemu/server:0.0.3 gdb -ex run ./world",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -136,7 +136,7 @@
|
||||
{
|
||||
"label": "queryserv",
|
||||
"type": "shell",
|
||||
"command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv",
|
||||
"command": "docker stop queryserv | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src --ulimit core=10000000 -e LD_LIBRARY_PATH=/src/ --network=eqemu --name queryserv eqemu/server:0.0.3 gdb -ex run ./queryserv",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -145,7 +145,7 @@
|
||||
{
|
||||
"label": "mariadb",
|
||||
"type": "shell",
|
||||
"command": "docker stop mariadb | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest",
|
||||
"command": "docker stop mariadb | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin/db:/bitnami/mariadb -p 3306:3306 -e MARIADB_DATABASE=peq -e MARIADB_USER=eqemu -e MARIADB_PASSWORD=eqemupass -e ALLOW_EMPTY_PASSWORD=yes --name mariadb --network=eqemu bitnami/mariadb:latest",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
@@ -154,7 +154,7 @@
|
||||
{
|
||||
"label": "ucs",
|
||||
"type": "shell",
|
||||
"command": "docker stop ucs | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/bin:/src -p 7778:7778 --name ucs --network=eqemu eqemu/server:0.0.3 gdb -ex run ./ucs",
|
||||
"command": "docker stop ucs | true && cd bin && docker network create eqemu | true && docker run --rm -v ${HOST_PROJECT_PATH}/build/bin:/src -p 7778:7778 --name ucs --network=eqemu eqemu/server:0.0.3 gdb -ex run ./ucs",
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
|
||||
@@ -224,6 +224,9 @@ IF(OpenSSL_FOUND AND MBEDTLS_FOUND)
|
||||
SET(TLS_LIBRARY_LIBS ${OPENSSL_LIBRARIES})
|
||||
SET(TLS_LIBRARY_INCLUDE ${OPENSSL_INCLUDE_DIR})
|
||||
ADD_DEFINITIONS(-DEQEMU_USE_OPENSSL)
|
||||
IF(${OPENSSL_VERSION} VERSION_GREATER_EQUAL "1.1.1")
|
||||
ADD_DEFINITIONS(-DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||
ENDIF()
|
||||
ELSEIF(TLS_LIBRARY_SELECTION STREQUAL "mbedTLS")
|
||||
SET(TLS_LIBRARY_TYPE " mbedTLS")
|
||||
SET(TLS_LIBRARY_ENABLED ON)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
############################################
|
||||
#
|
||||
# New changelog can be found here
|
||||
# https://eqemu.gitbook.io/changelog/
|
||||
# https://docs.eqemu.io/server/changelog/server
|
||||
#
|
||||
############################################
|
||||
# Deprecated
|
||||
|
||||
@@ -526,6 +526,7 @@ SET(common_headers
|
||||
guild_base.h
|
||||
guilds.h
|
||||
http/httplib.h
|
||||
http/uri.h
|
||||
inventory_profile.h
|
||||
inventory_slot.h
|
||||
ipc_mutex.h
|
||||
@@ -638,7 +639,8 @@ SET(common_headers
|
||||
StackWalker/StackWalker.h
|
||||
util/memory_stream.h
|
||||
util/directory.h
|
||||
util/uuid.h)
|
||||
util/uuid.h
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Event FILES
|
||||
event/event_loop.h
|
||||
|
||||
+125
-27
@@ -39,6 +39,7 @@
|
||||
#include "unix.h"
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include "database.h"
|
||||
@@ -46,6 +47,8 @@
|
||||
#include "extprofile.h"
|
||||
#include "string_util.h"
|
||||
#include "database_schema.h"
|
||||
#include "http/httplib.h"
|
||||
#include "http/uri.h"
|
||||
|
||||
extern Client client;
|
||||
|
||||
@@ -2039,62 +2042,64 @@ void Database::ClearRaidLeader(uint32 gid, uint32 rid)
|
||||
QueryDatabase(query);
|
||||
}
|
||||
|
||||
void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win)
|
||||
void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win, bool remove)
|
||||
{
|
||||
|
||||
std::string field;
|
||||
|
||||
switch(theme)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
switch(theme) {
|
||||
case LDoNThemes::GUK: {
|
||||
field = "guk_";
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
case LDoNThemes::MIR: {
|
||||
field = "mir_";
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
case LDoNThemes::MMC: {
|
||||
field = "mmc_";
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
case LDoNThemes::RUJ: {
|
||||
field = "ruj_";
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
case LDoNThemes::TAK: {
|
||||
field = "tak_";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
default: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (win)
|
||||
field += "wins";
|
||||
else
|
||||
field += "losses";
|
||||
field += win ? "wins" : "losses";
|
||||
std::string field_operation = remove ? "-" : "+";
|
||||
|
||||
std::string query = StringFormat("UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u",field.c_str(), field.c_str(), char_id);
|
||||
std::string query = fmt::format(
|
||||
"UPDATE `adventure_stats` SET {} = {} {} 1 WHERE player_id = {}",
|
||||
field,
|
||||
field,
|
||||
field_operation,
|
||||
char_id
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
if (results.RowsAffected() != 0)
|
||||
if (results.RowsAffected() != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
query = StringFormat("INSERT INTO `adventure_stats` SET %s=1, player_id=%u", field.c_str(), char_id);
|
||||
QueryDatabase(query);
|
||||
if (!remove) {
|
||||
query = fmt::format(
|
||||
"INSERT INTO `adventure_stats` SET {} = 1, player_id = {}",
|
||||
field,
|
||||
char_id
|
||||
);
|
||||
QueryDatabase(query);
|
||||
}
|
||||
}
|
||||
|
||||
bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as)
|
||||
{
|
||||
std::string query = StringFormat(
|
||||
std::string query = fmt::format(
|
||||
"SELECT "
|
||||
"`guk_wins`, "
|
||||
"`mir_wins`, "
|
||||
@@ -2109,7 +2114,7 @@ bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as)
|
||||
"FROM "
|
||||
"`adventure_stats` "
|
||||
"WHERE "
|
||||
"player_id = %u ",
|
||||
"player_id = {}",
|
||||
char_id
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
@@ -2268,6 +2273,35 @@ int Database::GetIPExemption(std::string account_ip) {
|
||||
return RuleI(World, MaxClientsPerIP);
|
||||
}
|
||||
|
||||
void Database::SetIPExemption(std::string account_ip, int exemption_amount) {
|
||||
std::string query = fmt::format(
|
||||
"SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'",
|
||||
account_ip
|
||||
);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
uint32 exemption_id = 0;
|
||||
if (results.Success() && results.RowCount() > 0) {
|
||||
auto row = results.begin();
|
||||
exemption_id = atoi(row[0]);
|
||||
}
|
||||
|
||||
query = fmt::format(
|
||||
"INSERT INTO `ip_exemptions` (`exemption_ip`, `exemption_amount`) VALUES ('{}', {})",
|
||||
account_ip,
|
||||
exemption_amount
|
||||
);
|
||||
|
||||
if (exemption_id != 0) {
|
||||
query = fmt::format(
|
||||
"UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'",
|
||||
exemption_amount,
|
||||
account_ip
|
||||
);
|
||||
}
|
||||
QueryDatabase(query);
|
||||
}
|
||||
|
||||
int Database::GetInstanceID(uint32 char_id, uint32 zone_id) {
|
||||
std::string query = StringFormat("SELECT instance_list.id FROM instance_list INNER JOIN instance_list_player ON instance_list.id = instance_list_player.id WHERE instance_list.zone = '%i' AND instance_list_player.charid = '%i'", zone_id, char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
@@ -2418,3 +2452,67 @@ bool Database::CopyCharacter(
|
||||
return true;
|
||||
}
|
||||
|
||||
void Database::SourceDatabaseTableFromUrl(std::string table_name, std::string url)
|
||||
{
|
||||
try {
|
||||
uri request_uri(url);
|
||||
|
||||
LogHTTP(
|
||||
"[SourceDatabaseTableFromUrl] parsing url [{}] path [{}] host [{}] query_string [{}] protocol [{}] port [{}]",
|
||||
url,
|
||||
request_uri.get_path(),
|
||||
request_uri.get_host(),
|
||||
request_uri.get_query(),
|
||||
request_uri.get_scheme(),
|
||||
request_uri.get_port()
|
||||
);
|
||||
|
||||
if (!DoesTableExist(table_name)) {
|
||||
LogMySQLQuery("Table [{}] does not exist. Downloading from Github and installing...", table_name);
|
||||
|
||||
// http get request
|
||||
httplib::Client cli(
|
||||
fmt::format(
|
||||
"{}://{}",
|
||||
request_uri.get_scheme(),
|
||||
request_uri.get_host()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
cli.set_connection_timeout(0, 60000000); // 60 sec
|
||||
cli.set_read_timeout(60, 0); // 60 seconds
|
||||
cli.set_write_timeout(60, 0); // 60 seconds
|
||||
|
||||
int sourced_queries = 0;
|
||||
|
||||
if (auto res = cli.Get(request_uri.get_path().c_str())) {
|
||||
if (res->status == 200) {
|
||||
for (auto &s: SplitString(res->body, ';')) {
|
||||
if (!trim(s).empty()) {
|
||||
auto results = QueryDatabase(s);
|
||||
if (!results.ErrorMessage().empty()) {
|
||||
LogError("Error sourcing SQL [{}]", results.ErrorMessage());
|
||||
return;
|
||||
}
|
||||
sourced_queries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogError("Error retrieving URL [{}]", url);
|
||||
}
|
||||
|
||||
LogMySQLQuery(
|
||||
"Table [{}] installed. Sourced [{}] queries",
|
||||
table_name,
|
||||
sourced_queries
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::invalid_argument iae) {
|
||||
LogError("[SourceDatabaseTableFromUrl] URI parser error [{}]", iae.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -176,7 +176,7 @@ public:
|
||||
|
||||
/* Adventure related. */
|
||||
|
||||
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win);
|
||||
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false);
|
||||
bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as);
|
||||
|
||||
/* Account Related */
|
||||
@@ -198,7 +198,8 @@ public:
|
||||
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
|
||||
void SetAgreementFlag(uint32 acctid);
|
||||
|
||||
int GetIPExemption(std::string account_ip);
|
||||
int GetIPExemption(std::string account_ip);
|
||||
void SetIPExemption(std::string account_ip, int exemption_amount);
|
||||
|
||||
int GetInstanceID(uint32 char_id, uint32 zone_id);
|
||||
|
||||
@@ -269,6 +270,8 @@ public:
|
||||
int CountInvSnapshots();
|
||||
void ClearInvSnapshots(bool from_now = false);
|
||||
|
||||
void SourceDatabaseTableFromUrl(std::string table_name, std::string url);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "emu_constants.h"
|
||||
#include "languages.h"
|
||||
|
||||
|
||||
int16 EQ::invtype::GetInvTypeSize(int16 inv_type) {
|
||||
@@ -152,3 +153,47 @@ int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::map<int, std::string>& EQ::constants::GetLanguageMap()
|
||||
{
|
||||
static const std::map<int, std::string> language_map = {
|
||||
{ LANG_COMMON_TONGUE, "Common Tongue" },
|
||||
{ LANG_BARBARIAN, "Barbarian" },
|
||||
{ LANG_ERUDIAN, "Erudian" },
|
||||
{ LANG_ELVISH, "Elvish" },
|
||||
{ LANG_DARK_ELVISH, "Dark Elvish" },
|
||||
{ LANG_DWARVISH, "Dwarvish" },
|
||||
{ LANG_TROLL, "Troll" },
|
||||
{ LANG_OGRE, "Ogre" },
|
||||
{ LANG_GNOMISH, "Gnomish" },
|
||||
{ LANG_HALFLING, "Halfling" },
|
||||
{ LANG_THIEVES_CANT, "Thieves Cant" },
|
||||
{ LANG_OLD_ERUDIAN, "Old Erudian" },
|
||||
{ LANG_ELDER_ELVISH, "Elder Elvish" },
|
||||
{ LANG_FROGLOK, "Froglok" },
|
||||
{ LANG_GOBLIN, "Goblin" },
|
||||
{ LANG_GNOLL, "Gnoll" },
|
||||
{ LANG_COMBINE_TONGUE, "Combine Tongue" },
|
||||
{ LANG_ELDER_TEIRDAL, "Elder Teirdal" },
|
||||
{ LANG_LIZARDMAN, "Lizardman" },
|
||||
{ LANG_ORCISH, "Orcish" },
|
||||
{ LANG_FAERIE, "Faerie" },
|
||||
{ LANG_DRAGON, "Dragon" },
|
||||
{ LANG_ELDER_DRAGON, "Elder Dragon" },
|
||||
{ LANG_DARK_SPEECH, "Dark Speech" },
|
||||
{ LANG_VAH_SHIR, "Vah Shir" },
|
||||
{ LANG_ALARAN, "Alaran" },
|
||||
{ LANG_HADAL, "Hadal" },
|
||||
{ LANG_UNKNOWN, "Unknown" }
|
||||
};
|
||||
return language_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetLanguageName(int language_id)
|
||||
{
|
||||
if (language_id >= LANG_COMMON_TONGUE && language_id <= LANG_UNKNOWN) {
|
||||
auto languages = EQ::constants::GetLanguageMap();
|
||||
return languages[language_id];
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
@@ -223,6 +223,9 @@ namespace EQ
|
||||
const char *GetStanceName(StanceType stance_type);
|
||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
||||
|
||||
extern const std::map<int, std::string>& GetLanguageMap();
|
||||
std::string GetLanguageName(int language_id);
|
||||
|
||||
const int STANCE_TYPE_FIRST = stancePassive;
|
||||
const int STANCE_TYPE_LAST = stanceBurnAE;
|
||||
const int STANCE_TYPE_COUNT = stanceBurnAE;
|
||||
|
||||
@@ -974,4 +974,39 @@ enum class DynamicZoneMemberStatus : uint8_t
|
||||
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,
|
||||
Halas,
|
||||
Rivervale,
|
||||
Freeport,
|
||||
Neriak,
|
||||
Grobb,
|
||||
Oggok,
|
||||
Kaladim,
|
||||
GreaterFaydark,
|
||||
Felwithe,
|
||||
Akanon,
|
||||
Cabilis,
|
||||
SharVahl
|
||||
};
|
||||
|
||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||
|
||||
@@ -384,7 +384,9 @@ struct NewZone_Struct {
|
||||
/*0716*/ uint32 FastRegenEndurance;
|
||||
/*0720*/ uint32 NPCAggroMaxDist;
|
||||
/*0724*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, if this value is 0, it prevents you from running off edges that would end up underworld
|
||||
/*0728*/
|
||||
/*0728*/ uint32 LavaDamage; // Seen 50
|
||||
/*0732*/ uint32 MinLavaDamage; // Seen 10
|
||||
/*0736*/
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -4340,8 +4342,8 @@ struct AARankPrereq_Struct
|
||||
struct AARankEffect_Struct
|
||||
{
|
||||
int32 effect_id;
|
||||
int32 base1;
|
||||
int32 base2;
|
||||
int32 base_value;
|
||||
int32 limit_value;
|
||||
int32 slot;
|
||||
};
|
||||
|
||||
@@ -4349,8 +4351,8 @@ struct AARankEffect_Struct
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
};
|
||||
|
||||
|
||||
+14
-1
@@ -44,6 +44,12 @@ void EQEmuConfig::parse_config()
|
||||
if (_root["server"]["world"]["loginserver"].get("legacy", "0").asString() == "1") { LoginLegacy = true; }
|
||||
LoginAccount = _root["server"]["world"]["loginserver"].get("account", "").asString();
|
||||
LoginPassword = _root["server"]["world"]["loginserver"].get("password", "").asString();
|
||||
|
||||
// at least today, this is wrong a majority of the time
|
||||
// remove this if eqemulator ever upgrades its loginserver
|
||||
if (LoginHost.find("login.eqemulator.net") != std::string::npos) {
|
||||
LoginLegacy = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
char str[32];
|
||||
@@ -62,12 +68,19 @@ void EQEmuConfig::parse_config()
|
||||
|
||||
loginconfig->LoginLegacy = false;
|
||||
if (_root["server"]["world"][str].get("legacy", "0").asString() == "1") { loginconfig->LoginLegacy = true; }
|
||||
|
||||
// at least today, this is wrong a majority of the time
|
||||
// remove this if eqemulator ever upgrades its loginserver
|
||||
if (loginconfig->LoginHost.find("login.eqemulator.net") != std::string::npos) {
|
||||
loginconfig->LoginLegacy = true;
|
||||
}
|
||||
|
||||
loginlist.Insert(loginconfig);
|
||||
} while (LoginCount < 100);
|
||||
}
|
||||
|
||||
|
||||
//<locked> from xml converts to json as locked: "", so i default to "false".
|
||||
//<locked> from xml converts to json as locked: "", so i default to "false".
|
||||
//The only way to enable locked is by switching to true, meaning this value is always false until manually set true
|
||||
Locked = false;
|
||||
if (_root["server"]["world"].get("locked", "false").asString() == "true") { Locked = true; }
|
||||
|
||||
@@ -130,6 +130,8 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
|
||||
log_settings[Logs::Loot].log_to_gmsay = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Scheduler].log_to_console = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::Cheat].log_to_console = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::HTTP].log_to_console = static_cast<uint8>(Logs::General);
|
||||
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
|
||||
|
||||
/**
|
||||
* RFC 5424
|
||||
|
||||
@@ -125,6 +125,8 @@ namespace Logs {
|
||||
Cheat,
|
||||
ClientList,
|
||||
DiaWind,
|
||||
HTTP,
|
||||
Saylink,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@@ -208,6 +210,8 @@ namespace Logs {
|
||||
"Cheat",
|
||||
"ClientList",
|
||||
"DialogueWindow",
|
||||
"HTTP",
|
||||
"Saylink",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -676,6 +676,26 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::DiaWind, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogHTTP(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::General, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogHTTPDetail(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::HTTP].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::Detail, Logs::HTTP, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogSaylink(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::General, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogSaylinkDetail(message, ...) do {\
|
||||
if (LogSys.log_settings[Logs::Saylink].is_category_enabled == 1)\
|
||||
OutF(LogSys, Logs::Detail, Logs::Saylink, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
@@ -1066,6 +1086,12 @@
|
||||
#define LogDiaWindDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogHTTP(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define LogHTTPDetail(message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -0,0 +1,633 @@
|
||||
// Copyright (C) 2015 Ben Lewis <benjf5+github@gmail.com>
|
||||
// Licensed under the MIT license.
|
||||
// https://github.com/ben-zen/uri-library
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
class uri {
|
||||
/* URIs are broadly divided into two categories: hierarchical and
|
||||
* non-hierarchical. Both hierarchical URIs and non-hierarchical URIs have a
|
||||
* few elements in common; all URIs have a scheme of one or more alphanumeric
|
||||
* characters followed by a colon, and they all may optionally have a query
|
||||
* component preceded by a question mark, and a fragment component preceded by
|
||||
* an octothorpe (hash mark: '#'). The query consists of stanzas separated by
|
||||
* either ampersands ('&') or semicolons (';') (but only one or the other),
|
||||
* and each stanza consists of a key and an optional value; if the value
|
||||
* exists, the key and value must be divided by an equals sign.
|
||||
*
|
||||
* The following is an example from Wikipedia of a hierarchical URI:
|
||||
* scheme:[//[user:password@]domain[:port]][/]path[?query][#fragment]
|
||||
*/
|
||||
|
||||
public:
|
||||
|
||||
enum class scheme_category {
|
||||
Hierarchical,
|
||||
NonHierarchical
|
||||
};
|
||||
|
||||
enum class component {
|
||||
Scheme,
|
||||
Content,
|
||||
Username,
|
||||
Password,
|
||||
Host,
|
||||
Port,
|
||||
Path,
|
||||
Query,
|
||||
Fragment
|
||||
};
|
||||
|
||||
enum class query_argument_separator {
|
||||
ampersand,
|
||||
semicolon
|
||||
};
|
||||
|
||||
uri(
|
||||
char const *uri_text, scheme_category category = scheme_category::Hierarchical,
|
||||
query_argument_separator separator = query_argument_separator::ampersand
|
||||
) :
|
||||
m_category(category),
|
||||
m_path_is_rooted(false),
|
||||
m_port(0),
|
||||
m_separator(separator)
|
||||
{
|
||||
setup(std::string(uri_text), category);
|
||||
};
|
||||
|
||||
uri(
|
||||
std::string const &uri_text, scheme_category category = scheme_category::Hierarchical,
|
||||
query_argument_separator separator = query_argument_separator::ampersand
|
||||
) :
|
||||
m_category(category),
|
||||
m_path_is_rooted(false),
|
||||
m_port(0),
|
||||
m_separator(separator)
|
||||
{
|
||||
setup(uri_text, category);
|
||||
};
|
||||
|
||||
uri(
|
||||
std::map<component, std::string> const &components,
|
||||
scheme_category category,
|
||||
bool rooted_path,
|
||||
query_argument_separator separator = query_argument_separator::ampersand
|
||||
) :
|
||||
m_category(category),
|
||||
m_path_is_rooted(rooted_path),
|
||||
m_separator(separator)
|
||||
{
|
||||
if (components.count(component::Scheme)) {
|
||||
if (components.at(component::Scheme).length() == 0) {
|
||||
throw std::invalid_argument("Scheme cannot be empty.");
|
||||
}
|
||||
m_scheme = components.at(component::Scheme);
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("A URI must have a scheme.");
|
||||
}
|
||||
|
||||
if (category == scheme_category::Hierarchical) {
|
||||
if (components.count(component::Content)) {
|
||||
throw std::invalid_argument("The content component is only for use in non-hierarchical URIs.");
|
||||
}
|
||||
|
||||
bool has_username = components.count(component::Username);
|
||||
bool has_password = components.count(component::Password);
|
||||
if (has_username && has_password) {
|
||||
m_username = components.at(component::Username);
|
||||
m_password = components.at(component::Password);
|
||||
}
|
||||
else if ((has_username && !has_password) || (!has_username && has_password)) {
|
||||
throw std::invalid_argument("If a username or password is supplied, both must be provided.");
|
||||
}
|
||||
|
||||
if (components.count(component::Host)) {
|
||||
m_host = components.at(component::Host);
|
||||
}
|
||||
|
||||
if (components.count(component::Port)) {
|
||||
m_port = std::stoul(components.at(component::Port));
|
||||
}
|
||||
|
||||
if (components.count(component::Path)) {
|
||||
m_path = components.at(component::Path);
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument("A path is required on a hierarchical URI, even an empty path.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (components.count(component::Username)
|
||||
|| components.count(component::Password)
|
||||
|| components.count(component::Host)
|
||||
|| components.count(component::Port)
|
||||
|| components.count(component::Path)) {
|
||||
throw std::invalid_argument("None of the hierarchical components are allowed in a non-hierarchical URI.");
|
||||
}
|
||||
|
||||
if (components.count(component::Content)) {
|
||||
m_content = components.at(component::Content);
|
||||
}
|
||||
else {
|
||||
throw std::invalid_argument(
|
||||
"Content is a required component for a non-hierarchical URI, even an empty string."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (components.count(component::Query)) {
|
||||
m_query = components.at(component::Query);
|
||||
}
|
||||
|
||||
if (components.count(component::Fragment)) {
|
||||
m_fragment = components.at(component::Fragment);
|
||||
}
|
||||
}
|
||||
|
||||
uri(uri const &other, std::map<component, std::string> const &replacements) :
|
||||
m_category(other.m_category),
|
||||
m_path_is_rooted(other.m_path_is_rooted),
|
||||
m_separator(other.m_separator)
|
||||
{
|
||||
m_scheme = (replacements.count(component::Scheme))
|
||||
? replacements.at(component::Scheme) : other.m_scheme;
|
||||
|
||||
if (m_category == scheme_category::Hierarchical) {
|
||||
m_username = (replacements.count(component::Username))
|
||||
? replacements.at(component::Username) : other.m_username;
|
||||
|
||||
m_password = (replacements.count(component::Password))
|
||||
? replacements.at(component::Password) : other.m_password;
|
||||
|
||||
m_host = (replacements.count(component::Host))
|
||||
? replacements.at(component::Host) : other.m_host;
|
||||
|
||||
m_port = (replacements.count(component::Port))
|
||||
? std::stoul(replacements.at(component::Port)) : other.m_port;
|
||||
|
||||
m_path = (replacements.count(component::Path))
|
||||
? replacements.at(component::Path) : other.m_path;
|
||||
}
|
||||
else {
|
||||
m_content = (replacements.count(component::Content))
|
||||
? replacements.at(component::Content) : other.m_content;
|
||||
}
|
||||
|
||||
m_query = (replacements.count(component::Query))
|
||||
? replacements.at(component::Query) : other.m_query;
|
||||
|
||||
m_fragment = (replacements.count(component::Fragment))
|
||||
? replacements.at(component::Fragment) : other.m_fragment;
|
||||
}
|
||||
|
||||
// Copy constructor; just use the copy assignment operator internally.
|
||||
uri(uri const &other)
|
||||
{
|
||||
*this = other;
|
||||
};
|
||||
|
||||
// Copy assignment operator
|
||||
uri &operator=(uri const &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_scheme = other.m_scheme;
|
||||
m_content = other.m_content;
|
||||
m_username = other.m_username;
|
||||
m_password = other.m_password;
|
||||
m_host = other.m_host;
|
||||
m_path = other.m_path;
|
||||
m_query = other.m_query;
|
||||
m_fragment = other.m_fragment;
|
||||
m_query_dict = other.m_query_dict;
|
||||
m_category = other.m_category;
|
||||
m_port = other.m_port;
|
||||
m_path_is_rooted = other.m_path_is_rooted;
|
||||
m_separator = other.m_separator;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~uri() {};
|
||||
|
||||
std::string const &get_scheme() const
|
||||
{
|
||||
return m_scheme;
|
||||
};
|
||||
|
||||
scheme_category get_scheme_category() const
|
||||
{
|
||||
return m_category;
|
||||
};
|
||||
|
||||
std::string const &get_content() const
|
||||
{
|
||||
if (m_category != scheme_category::NonHierarchical) {
|
||||
throw std::domain_error("The content component is only valid for non-hierarchical URIs.");
|
||||
}
|
||||
return m_content;
|
||||
};
|
||||
|
||||
std::string const &get_username() const
|
||||
{
|
||||
if (m_category != scheme_category::Hierarchical) {
|
||||
throw std::domain_error("The username component is only valid for hierarchical URIs.");
|
||||
}
|
||||
return m_username;
|
||||
};
|
||||
|
||||
std::string const &get_password() const
|
||||
{
|
||||
if (m_category != scheme_category::Hierarchical) {
|
||||
throw std::domain_error("The password component is only valid for hierarchical URIs.");
|
||||
}
|
||||
return m_password;
|
||||
};
|
||||
|
||||
std::string const &get_host() const
|
||||
{
|
||||
if (m_category != scheme_category::Hierarchical) {
|
||||
throw std::domain_error("The host component is only valid for hierarchical URIs.");
|
||||
}
|
||||
return m_host;
|
||||
};
|
||||
|
||||
unsigned long get_port() const
|
||||
{
|
||||
if (m_category != scheme_category::Hierarchical) {
|
||||
throw std::domain_error("The port component is only valid for hierarchical URIs.");
|
||||
}
|
||||
return m_port;
|
||||
};
|
||||
|
||||
std::string const &get_path() const
|
||||
{
|
||||
if (m_category != scheme_category::Hierarchical) {
|
||||
throw std::domain_error("The path component is only valid for hierarchical URIs.");
|
||||
}
|
||||
return m_path;
|
||||
};
|
||||
|
||||
std::string const &get_query() const
|
||||
{
|
||||
return m_query;
|
||||
};
|
||||
|
||||
std::map<std::string, std::string> const &get_query_dictionary() const
|
||||
{
|
||||
return m_query_dict;
|
||||
};
|
||||
|
||||
std::string const &get_fragment() const
|
||||
{
|
||||
return m_fragment;
|
||||
};
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string full_uri;
|
||||
full_uri.append(m_scheme);
|
||||
full_uri.append(":");
|
||||
|
||||
if (m_content.length() > m_path.length()) {
|
||||
full_uri.append("//");
|
||||
if (!(m_username.empty() || m_password.empty())) {
|
||||
full_uri.append(m_username);
|
||||
full_uri.append(":");
|
||||
full_uri.append(m_password);
|
||||
full_uri.append("@");
|
||||
}
|
||||
|
||||
full_uri.append(m_host);
|
||||
|
||||
if (m_port != 0) {
|
||||
full_uri.append(":");
|
||||
full_uri.append(std::to_string(m_port));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_path_is_rooted) {
|
||||
full_uri.append("/");
|
||||
}
|
||||
full_uri.append(m_path);
|
||||
|
||||
if (!m_query.empty()) {
|
||||
full_uri.append("?");
|
||||
full_uri.append(m_query);
|
||||
}
|
||||
|
||||
if (!m_fragment.empty()) {
|
||||
full_uri.append("#");
|
||||
full_uri.append(m_fragment);
|
||||
}
|
||||
|
||||
return full_uri;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
void setup(std::string const &uri_text, scheme_category category)
|
||||
{
|
||||
size_t const uri_length = uri_text.length();
|
||||
|
||||
if (uri_length == 0) {
|
||||
throw std::invalid_argument("URIs cannot be of zero length.");
|
||||
}
|
||||
|
||||
std::string::const_iterator cursor = parse_scheme(
|
||||
uri_text,
|
||||
uri_text.begin());
|
||||
// After calling parse_scheme, *cursor == ':'; none of the following parsers
|
||||
// expect a separator character, so we advance the cursor upon calling them.
|
||||
cursor = parse_content(uri_text, (cursor + 1));
|
||||
|
||||
if ((cursor != uri_text.end()) && (*cursor == '?')) {
|
||||
cursor = parse_query(uri_text, (cursor + 1));
|
||||
}
|
||||
|
||||
if ((cursor != uri_text.end()) && (*cursor == '#')) {
|
||||
cursor = parse_fragment(uri_text, (cursor + 1));
|
||||
}
|
||||
|
||||
init_query_dictionary(); // If the query string is empty, this will be empty too.
|
||||
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_scheme(
|
||||
std::string const &uri_text,
|
||||
std::string::const_iterator scheme_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator scheme_end = scheme_start;
|
||||
while ((scheme_end != uri_text.end()) && (*scheme_end != ':')) {
|
||||
if (!(std::isalnum(*scheme_end) || (*scheme_end == '-')
|
||||
|| (*scheme_end == '+') || (*scheme_end == '.'))) {
|
||||
throw std::invalid_argument(
|
||||
"Invalid character found in the scheme component. Supplied URI was: \""
|
||||
+ uri_text + "\"."
|
||||
);
|
||||
}
|
||||
++scheme_end;
|
||||
}
|
||||
|
||||
if (scheme_end == uri_text.end()) {
|
||||
throw std::invalid_argument(
|
||||
"End of URI found while parsing the scheme. Supplied URI was: \""
|
||||
+ uri_text + "\"."
|
||||
);
|
||||
}
|
||||
|
||||
if (scheme_start == scheme_end) {
|
||||
throw std::invalid_argument(
|
||||
"Scheme component cannot be zero-length. Supplied URI was: \""
|
||||
+ uri_text + "\"."
|
||||
);
|
||||
}
|
||||
|
||||
m_scheme = std::move(std::string(scheme_start, scheme_end));
|
||||
return scheme_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_content(
|
||||
std::string const &uri_text,
|
||||
std::string::const_iterator content_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator content_end = content_start;
|
||||
while ((content_end != uri_text.end()) && (*content_end != '?') && (*content_end != '#')) {
|
||||
++content_end;
|
||||
}
|
||||
|
||||
m_content = std::move(std::string(content_start, content_end));
|
||||
|
||||
if ((m_category == scheme_category::Hierarchical) && (m_content.length() > 0)) {
|
||||
// If it's a hierarchical URI, the content should be parsed for the hierarchical components.
|
||||
std::string::const_iterator path_start = m_content.begin();
|
||||
std::string::const_iterator path_end = m_content.end();
|
||||
if (!m_content.compare(0, 2, "//")) {
|
||||
// In this case an authority component is present.
|
||||
std::string::const_iterator authority_cursor = (m_content.begin() + 2);
|
||||
if (m_content.find_first_of('@') != std::string::npos) {
|
||||
std::string::const_iterator userpass_divider = parse_username(
|
||||
uri_text,
|
||||
m_content,
|
||||
authority_cursor
|
||||
);
|
||||
authority_cursor = parse_password(uri_text, m_content, (userpass_divider + 1));
|
||||
// After this call, *authority_cursor == '@', so we skip over it.
|
||||
++authority_cursor;
|
||||
}
|
||||
|
||||
authority_cursor = parse_host(uri_text, m_content, authority_cursor);
|
||||
|
||||
if ((authority_cursor != m_content.end()) && (*authority_cursor == ':')) {
|
||||
authority_cursor = parse_port(uri_text, m_content, (authority_cursor + 1));
|
||||
}
|
||||
|
||||
if ((authority_cursor != m_content.end()) && (*authority_cursor == '/')) {
|
||||
// Then the path is rooted, and we should note this.
|
||||
m_path_is_rooted = true;
|
||||
path_start = authority_cursor + 1;
|
||||
}
|
||||
|
||||
// If we've reached the end and no path is present then set path_start
|
||||
// to the end.
|
||||
if (authority_cursor == m_content.end()) {
|
||||
path_start = m_content.end();
|
||||
}
|
||||
}
|
||||
else if (!m_content.compare(0, 1, "/")) {
|
||||
m_path_is_rooted = true;
|
||||
++path_start;
|
||||
}
|
||||
|
||||
// We can now build the path based on what remains in the content string,
|
||||
// since that's all that exists after the host and optional port component.
|
||||
m_path = std::move(std::string(path_start, path_end));
|
||||
}
|
||||
return content_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_username(
|
||||
std::string const &uri_text,
|
||||
std::string const &content,
|
||||
std::string::const_iterator username_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator username_end = username_start;
|
||||
// Since this is only reachable when '@' was in the content string, we can
|
||||
// ignore the end-of-string case.
|
||||
while (*username_end != ':') {
|
||||
if (*username_end == '@') {
|
||||
throw std::invalid_argument(
|
||||
"Username must be followed by a password. Supplied URI was: \""
|
||||
+ uri_text + "\"."
|
||||
);
|
||||
}
|
||||
++username_end;
|
||||
}
|
||||
m_username = std::move(std::string(username_start, username_end));
|
||||
return username_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_password(
|
||||
std::string const &uri_text,
|
||||
std::string const &content,
|
||||
std::string::const_iterator password_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator password_end = password_start;
|
||||
while (*password_end != '@') {
|
||||
++password_end;
|
||||
}
|
||||
|
||||
m_password = std::move(std::string(password_start, password_end));
|
||||
return password_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_host(
|
||||
std::string const &uri_text,
|
||||
std::string const &content,
|
||||
std::string::const_iterator host_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator host_end = host_start;
|
||||
// So, the host can contain a few things. It can be a domain, it can be an
|
||||
// IPv4 address, it can be an IPv6 address, or an IPvFuture literal. In the
|
||||
// case of those last two, it's of the form [...] where what's between the
|
||||
// brackets is a matter of which IPv?? version it is.
|
||||
while (host_end != content.end()) {
|
||||
if (*host_end == '[') {
|
||||
// We're parsing an IPv6 or IPvFuture address, so we should handle that
|
||||
// instead of the normal procedure.
|
||||
while ((host_end != content.end()) && (*host_end != ']')) {
|
||||
++host_end;
|
||||
}
|
||||
|
||||
if (host_end == content.end()) {
|
||||
throw std::invalid_argument(
|
||||
"End of content component encountered "
|
||||
"while parsing the host component. "
|
||||
"Supplied URI was: \""
|
||||
+ uri_text + "\"."
|
||||
);
|
||||
}
|
||||
|
||||
++host_end;
|
||||
break;
|
||||
// We can stop looping, we found the end of the IP literal, which is the
|
||||
// whole of the host component when one's in use.
|
||||
}
|
||||
else if ((*host_end == ':') || (*host_end == '/')) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
++host_end;
|
||||
}
|
||||
}
|
||||
|
||||
m_host = std::move(std::string(host_start, host_end));
|
||||
return host_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_port(
|
||||
std::string const &uri_text,
|
||||
std::string const &content,
|
||||
std::string::const_iterator port_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator port_end = port_start;
|
||||
while ((port_end != content.end()) && (*port_end != '/')) {
|
||||
if (!std::isdigit(*port_end)) {
|
||||
throw std::invalid_argument(
|
||||
"Invalid character while parsing the port. "
|
||||
"Supplied URI was: \"" + uri_text + "\"."
|
||||
);
|
||||
}
|
||||
|
||||
++port_end;
|
||||
}
|
||||
|
||||
m_port = std::stoul(std::string(port_start, port_end));
|
||||
return port_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_query(
|
||||
std::string const &uri_text,
|
||||
std::string::const_iterator query_start
|
||||
)
|
||||
{
|
||||
std::string::const_iterator query_end = query_start;
|
||||
while ((query_end != uri_text.end()) && (*query_end != '#')) {
|
||||
// Queries can contain almost any character except hash, which is reserved
|
||||
// for the start of the fragment.
|
||||
++query_end;
|
||||
}
|
||||
m_query = std::move(std::string(query_start, query_end));
|
||||
return query_end;
|
||||
};
|
||||
|
||||
std::string::const_iterator parse_fragment(
|
||||
std::string const &uri_text,
|
||||
std::string::const_iterator fragment_start
|
||||
)
|
||||
{
|
||||
m_fragment = std::move(std::string(fragment_start, uri_text.end()));
|
||||
return uri_text.end();
|
||||
};
|
||||
|
||||
void init_query_dictionary()
|
||||
{
|
||||
if (!m_query.empty()) {
|
||||
// Loop over the query string looking for '&'s, then check each one for
|
||||
// an '=' to find keys and values; if there's not an '=' then the key
|
||||
// will have an empty value in the map.
|
||||
char separator = (m_separator == query_argument_separator::ampersand) ? '&' : ';';
|
||||
size_t carat = 0;
|
||||
size_t stanza_end = m_query.find_first_of(separator);
|
||||
do {
|
||||
std::string stanza = m_query.substr(
|
||||
carat,
|
||||
((stanza_end != std::string::npos) ? (stanza_end - carat) : std::string::npos));
|
||||
size_t key_value_divider = stanza.find_first_of('=');
|
||||
std::string key = stanza.substr(0, key_value_divider);
|
||||
std::string value;
|
||||
if (key_value_divider != std::string::npos) {
|
||||
value = stanza.substr((key_value_divider + 1));
|
||||
}
|
||||
|
||||
if (m_query_dict.count(key) != 0) {
|
||||
throw std::invalid_argument("Bad key in the query string!");
|
||||
}
|
||||
|
||||
m_query_dict.emplace(key, value);
|
||||
carat = ((stanza_end != std::string::npos) ? (stanza_end + 1)
|
||||
: std::string::npos);
|
||||
stanza_end = m_query.find_first_of(separator, carat);
|
||||
} while ((stanza_end != std::string::npos)
|
||||
|| (carat != std::string::npos));
|
||||
}
|
||||
}
|
||||
|
||||
std::string m_scheme;
|
||||
std::string m_content;
|
||||
std::string m_username;
|
||||
std::string m_password;
|
||||
std::string m_host;
|
||||
std::string m_path;
|
||||
std::string m_query;
|
||||
std::string m_fragment;
|
||||
|
||||
std::map<std::string, std::string> m_query_dict;
|
||||
|
||||
scheme_category m_category;
|
||||
unsigned long m_port;
|
||||
bool m_path_is_rooted;
|
||||
query_argument_separator m_separator;
|
||||
};
|
||||
@@ -224,7 +224,7 @@ EQ::ItemInstance* EQ::InventoryProfile::GetItem(int16 slot_id, uint8 bagidx) con
|
||||
return GetItem(InventoryProfile::CalcSlotId(slot_id, bagidx));
|
||||
}
|
||||
|
||||
// Put an item snto specified slot
|
||||
// Put an item into specified slot
|
||||
int16 EQ::InventoryProfile::PutItem(int16 slot_id, const ItemInstance& inst)
|
||||
{
|
||||
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) {
|
||||
|
||||
+1
-1
@@ -482,7 +482,7 @@ namespace EQ
|
||||
uint32 Haste;
|
||||
uint32 DamageShield;
|
||||
uint32 RecastDelay;
|
||||
uint32 RecastType;
|
||||
int RecastType;
|
||||
uint32 AugDistiller;
|
||||
bool Attuneable;
|
||||
bool NoPet;
|
||||
|
||||
@@ -11,6 +11,7 @@ EQ::Net::ServertalkServerConnection::ServertalkServerConnection(std::shared_ptr<
|
||||
m_connection->OnRead(std::bind(&ServertalkServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
m_connection->OnDisconnect(std::bind(&ServertalkServerConnection::OnDisconnect, this, std::placeholders::_1));
|
||||
m_connection->Start();
|
||||
m_legacy_mode = false;
|
||||
}
|
||||
|
||||
EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
|
||||
@@ -19,17 +20,73 @@ EQ::Net::ServertalkServerConnection::~ServertalkServerConnection()
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet & p)
|
||||
{
|
||||
// pad zero size packets
|
||||
if (p.Length() == 0) {
|
||||
p.PutUInt8(0, 0);
|
||||
if (m_legacy_mode) {
|
||||
if (!m_connection)
|
||||
return;
|
||||
|
||||
if (opcode == ServerOP_UsertoWorldReq) {
|
||||
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
|
||||
|
||||
EQ::Net::DynamicPacket req;
|
||||
size_t i = 0;
|
||||
req.PutUInt32(i, req_in->lsaccountid); i += 4;
|
||||
req.PutUInt32(i, req_in->worldid); i += 4;
|
||||
req.PutUInt32(i, req_in->FromID); i += 4;
|
||||
req.PutUInt32(i, req_in->ToID); i += 4;
|
||||
req.PutData(i, req_in->IPAddr, 64); i += 64;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt16(0, ServerOP_UsertoWorldReqLeg);
|
||||
out.PutUInt16(2, req.Length() + 4);
|
||||
out.PutPacket(4, req);
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
return;
|
||||
}
|
||||
|
||||
if (opcode == ServerOP_LSClientAuth) {
|
||||
auto req_in = (ClientAuth_Struct*)p.Data();
|
||||
|
||||
EQ::Net::DynamicPacket req;
|
||||
size_t i = 0;
|
||||
req.PutUInt32(i, req_in->loginserver_account_id); i += 4;
|
||||
req.PutData(i, req_in->account_name, 30); i += 30;
|
||||
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); i += 4;
|
||||
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt16(0, ServerOP_LSClientAuthLeg);
|
||||
out.PutUInt16(2, req.Length() + 4);
|
||||
out.PutPacket(4, req);
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt16(0, opcode);
|
||||
out.PutUInt16(2, p.Length() + 4);
|
||||
out.PutPacket(4, p);
|
||||
|
||||
m_connection->Write((const char*)out.Data(), out.Length());
|
||||
} else {
|
||||
// pad zero size packets
|
||||
// pad packets that would cause a collision with legacy identification code
|
||||
// It's unlikely we'd send a 4MB msg for any reason but just incase.
|
||||
if (p.Length() == 0 || p.Length() == 43061256) {
|
||||
p.PutUInt8(0, 0);
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
out.PutUInt32(0, p.Length());
|
||||
out.PutUInt16(4, opcode);
|
||||
out.PutPacket(6, p);
|
||||
|
||||
InternalSend(ServertalkMessage, out);
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::SendPacket(ServerPacket *p)
|
||||
@@ -54,17 +111,41 @@ void EQ::Net::ServertalkServerConnection::OnMessage(std::function<void(uint16_t,
|
||||
void EQ::Net::ServertalkServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz)
|
||||
{
|
||||
m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + sz);
|
||||
ProcessReadBuffer();
|
||||
|
||||
if (m_legacy_mode) {
|
||||
ProcessOldReadBuffer();
|
||||
} else {
|
||||
ProcessReadBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
|
||||
{
|
||||
size_t current = 0;
|
||||
size_t total = m_buffer.size();
|
||||
constexpr size_t ls_info_size = sizeof(ServerNewLSInfo_Struct);
|
||||
|
||||
while (current < total) {
|
||||
auto left = total - current;
|
||||
|
||||
if (left < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto leg_opcode = *(uint16_t*)&m_buffer[current];
|
||||
auto leg_size = *(uint16_t*)&m_buffer[current + 2] - 4;
|
||||
|
||||
//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(ServerNewLSInfo_Struct)) {
|
||||
m_legacy_mode = true;
|
||||
m_identifier = "World";
|
||||
m_parent->ConnectionIdentified(this);
|
||||
ProcessOldReadBuffer();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
//header:
|
||||
//uint32 length;
|
||||
@@ -129,6 +210,57 @@ void EQ::Net::ServertalkServerConnection::ProcessReadBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessOldReadBuffer()
|
||||
{
|
||||
size_t current = 0;
|
||||
size_t total = m_buffer.size();
|
||||
|
||||
while (current < total) {
|
||||
auto left = total - current;
|
||||
|
||||
/*
|
||||
//header:
|
||||
//uint32 length;
|
||||
//uint8 type;
|
||||
*/
|
||||
uint16_t length = 0;
|
||||
uint16_t opcode = 0;
|
||||
if (left < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
opcode = *(uint16_t*)&m_buffer[current];
|
||||
length = *(uint16_t*)&m_buffer[current + 2];
|
||||
if (length < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
length -= 4;
|
||||
|
||||
if (current + 4 + length > total) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
EQ::Net::DynamicPacket p;
|
||||
ProcessMessageOld(opcode, p);
|
||||
}
|
||||
else {
|
||||
EQ::Net::StaticPacket p(&m_buffer[current + 4], length);
|
||||
ProcessMessageOld(opcode, p);
|
||||
}
|
||||
|
||||
current += length + 4;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
m_buffer.clear();
|
||||
}
|
||||
else {
|
||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + current);
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::OnDisconnect(TCPConnection *c)
|
||||
{
|
||||
m_parent->ConnectionDisconnected(this);
|
||||
@@ -144,7 +276,7 @@ void EQ::Net::ServertalkServerConnection::SendHello()
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::InternalSend(ServertalkPacketType type, EQ::Net::Packet &p)
|
||||
{
|
||||
if (!m_connection)
|
||||
if (!m_connection || m_legacy_mode)
|
||||
return;
|
||||
|
||||
EQ::Net::DynamicPacket out;
|
||||
@@ -201,3 +333,20 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p)
|
||||
LogError("Error parsing message from client: {0}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void EQ::Net::ServertalkServerConnection::ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p)
|
||||
{
|
||||
try {
|
||||
auto cb = m_message_callbacks.find(opcode);
|
||||
if (cb != m_message_callbacks.end()) {
|
||||
cb->second(opcode, p);
|
||||
}
|
||||
|
||||
if (m_message_callback) {
|
||||
m_message_callback(opcode, p);
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex) {
|
||||
LogError("Error parsing legacy message from client: {0}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,13 @@ namespace EQ
|
||||
private:
|
||||
void OnRead(TCPConnection* c, const unsigned char* data, size_t sz);
|
||||
void ProcessReadBuffer();
|
||||
void ProcessOldReadBuffer();
|
||||
void OnDisconnect(TCPConnection* c);
|
||||
void SendHello();
|
||||
void InternalSend(ServertalkPacketType type, EQ::Net::Packet &p);
|
||||
void ProcessHandshake(EQ::Net::Packet &p);
|
||||
void ProcessMessage(EQ::Net::Packet &p);
|
||||
void ProcessMessageOld(uint16_t opcode, EQ::Net::Packet &p);
|
||||
|
||||
std::shared_ptr<EQ::Net::TCPConnection> m_connection;
|
||||
ServertalkServer *m_parent;
|
||||
@@ -41,6 +43,7 @@ namespace EQ
|
||||
std::function<void(uint16_t, EQ::Net::Packet&)> m_message_callback;
|
||||
std::string m_identifier;
|
||||
std::string m_uuid;
|
||||
bool m_legacy_mode;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1863,8 +1863,8 @@ namespace RoF
|
||||
/*fill in some unknowns with observed values, hopefully it will help */
|
||||
eq->unknown800 = -1;
|
||||
eq->unknown844 = 600;
|
||||
eq->unknown880 = 50;
|
||||
eq->unknown884 = 10;
|
||||
OUT(LavaDamage);
|
||||
OUT(MinLavaDamage);
|
||||
eq->unknown888 = 1;
|
||||
eq->unknown889 = 0;
|
||||
eq->unknown890 = 1;
|
||||
|
||||
@@ -1919,8 +1919,8 @@ namespace RoF2
|
||||
eq->SkyRelated2 = -1;
|
||||
eq->NPCAggroMaxDist = 600;
|
||||
eq->FilterID = 2008; // Guild Lobby observed value
|
||||
eq->LavaDamage = 50;
|
||||
eq->MinLavaDamage = 10;
|
||||
OUT(LavaDamage);
|
||||
OUT(MinLavaDamage);
|
||||
eq->bDisallowManaStone = 1;
|
||||
eq->bNoBind = 0;
|
||||
eq->bNoAttack = 0;
|
||||
|
||||
@@ -4361,8 +4361,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
/*16*/
|
||||
};
|
||||
|
||||
@@ -581,8 +581,8 @@ struct NewZone_Struct {
|
||||
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
|
||||
/*0872*/ uint32 scriptIDSomething3;
|
||||
/*0876*/ uint32 SuspendBuffs;
|
||||
/*0880*/ uint32 unknown880; // Seen 50
|
||||
/*0884*/ uint32 unknown884; // Seen 10
|
||||
/*0880*/ uint32 LavaDamage; // Seen 50
|
||||
/*0884*/ uint32 MinLavaDamage; // Seen 10
|
||||
/*0888*/ uint8 unknown888; // Seen 1
|
||||
/*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj)
|
||||
/*0890*/ uint8 unknown890; // Seen 1
|
||||
@@ -4305,8 +4305,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
/*16*/
|
||||
};
|
||||
|
||||
@@ -1388,8 +1388,8 @@ namespace SoD
|
||||
/*fill in some unknowns with observed values, hopefully it will help */
|
||||
eq->unknown800 = -1;
|
||||
eq->unknown844 = 600;
|
||||
eq->unknown880 = 50;
|
||||
eq->unknown884 = 10;
|
||||
OUT(LavaDamage);
|
||||
OUT(MinLavaDamage);
|
||||
eq->unknown888 = 1;
|
||||
eq->unknown889 = 0;
|
||||
eq->unknown890 = 1;
|
||||
@@ -1874,8 +1874,8 @@ namespace SoD
|
||||
|
||||
for(auto i = 0; i < eq->total_abilities; ++i) {
|
||||
eq->abilities[i].skill_id = inapp->ReadUInt32();
|
||||
eq->abilities[i].base1 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base2 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].limit_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].slot = inapp->ReadUInt32();
|
||||
}
|
||||
|
||||
|
||||
@@ -450,8 +450,8 @@ struct NewZone_Struct {
|
||||
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
|
||||
/*0872*/ uint32 scriptIDSomething3;
|
||||
/*0876*/ uint32 SuspendBuffs;
|
||||
/*0880*/ uint32 unknown880; //seen 50
|
||||
/*0884*/ uint32 unknown884; //seen 10
|
||||
/*0880*/ uint32 LavaDamage; //seen 50
|
||||
/*0884*/ uint32 MinLavaDamage; //seen 10
|
||||
/*0888*/ uint8 unknown888; //seen 1
|
||||
/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj)
|
||||
/*0890*/ uint8 unknown890; //seen 1
|
||||
@@ -3748,8 +3748,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
/*16*/
|
||||
};
|
||||
|
||||
@@ -1066,8 +1066,8 @@ namespace SoF
|
||||
/*fill in some unknowns with observed values, hopefully it will help */
|
||||
eq->unknown796 = -1;
|
||||
eq->unknown840 = 600;
|
||||
eq->unknown876 = 50;
|
||||
eq->unknown880 = 10;
|
||||
OUT(LavaDamage);
|
||||
OUT(MinLavaDamage);
|
||||
eq->unknown884 = 1;
|
||||
eq->unknown885 = 0;
|
||||
eq->unknown886 = 1;
|
||||
@@ -1545,8 +1545,8 @@ namespace SoF
|
||||
|
||||
for(auto i = 0; i < eq->total_abilities; ++i) {
|
||||
eq->abilities[i].skill_id = inapp->ReadUInt32();
|
||||
eq->abilities[i].base1 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base2 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].limit_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].slot = inapp->ReadUInt32();
|
||||
}
|
||||
|
||||
|
||||
@@ -454,8 +454,8 @@ struct NewZone_Struct {
|
||||
/*0864*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
|
||||
/*0868*/ uint32 scriptIDSomething3;
|
||||
/*0872*/ uint32 SuspendBuffs;
|
||||
/*0876*/ uint32 unknown876; //seen 50
|
||||
/*0880*/ uint32 unknown880; //seen 10
|
||||
/*0876*/ uint32 LavaDamage; //seen 50
|
||||
/*0880*/ uint32 MinLavaDamage; //seen 10
|
||||
/*0884*/ uint8 unknown884; //seen 1
|
||||
/*0885*/ uint8 unknown885; //seen 0 (POK) or 1 (rujj)
|
||||
/*0886*/ uint8 unknown886; //seen 1
|
||||
@@ -3673,8 +3673,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
/*16*/
|
||||
};
|
||||
|
||||
@@ -1338,8 +1338,8 @@ namespace Titanium
|
||||
|
||||
for(auto i = 0; i < eq->total_abilities; ++i) {
|
||||
eq->abilities[i].skill_id = inapp->ReadUInt32();
|
||||
eq->abilities[i].base1 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base2 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].limit_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].slot = inapp->ReadUInt32();
|
||||
}
|
||||
|
||||
|
||||
@@ -3180,8 +3180,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
};
|
||||
|
||||
|
||||
@@ -1614,8 +1614,8 @@ namespace UF
|
||||
/*fill in some unknowns with observed values, hopefully it will help */
|
||||
eq->unknown800 = -1;
|
||||
eq->unknown844 = 600;
|
||||
eq->unknown880 = 50;
|
||||
eq->unknown884 = 10;
|
||||
OUT(LavaDamage);
|
||||
OUT(MinLavaDamage);
|
||||
eq->unknown888 = 1;
|
||||
eq->unknown889 = 0;
|
||||
eq->unknown890 = 1;
|
||||
@@ -2139,8 +2139,8 @@ namespace UF
|
||||
|
||||
for(auto i = 0; i < eq->total_abilities; ++i) {
|
||||
eq->abilities[i].skill_id = inapp->ReadUInt32();
|
||||
eq->abilities[i].base1 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base2 = inapp->ReadUInt32();
|
||||
eq->abilities[i].base_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].limit_value = inapp->ReadUInt32();
|
||||
eq->abilities[i].slot = inapp->ReadUInt32();
|
||||
}
|
||||
|
||||
|
||||
@@ -450,8 +450,8 @@ struct NewZone_Struct {
|
||||
/*0868*/ uint32 underworld_teleport_index; // > 0 teleports w/ zone point index, invalid succors, -1 affects some collisions
|
||||
/*0872*/ uint32 scriptIDSomething3;
|
||||
/*0876*/ uint32 SuspendBuffs;
|
||||
/*0880*/ uint32 unknown880; //seen 50
|
||||
/*0884*/ uint32 unknown884; //seen 10
|
||||
/*0880*/ uint32 LavaDamage; //seen 50
|
||||
/*0884*/ uint32 MinLavaDamage; //seen 10
|
||||
/*0888*/ uint8 unknown888; //seen 1
|
||||
/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj)
|
||||
/*0890*/ uint8 unknown890; //seen 1
|
||||
@@ -3803,8 +3803,8 @@ struct UseAA_Struct {
|
||||
|
||||
struct AA_Ability {
|
||||
/*00*/ uint32 skill_id;
|
||||
/*04*/ uint32 base1;
|
||||
/*08*/ uint32 base2;
|
||||
/*04*/ uint32 base_value;
|
||||
/*08*/ uint32 limit_value;
|
||||
/*12*/ uint32 slot;
|
||||
/*16*/
|
||||
};
|
||||
|
||||
@@ -2232,3 +2232,15 @@ bool PlayerAppearance::IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_v
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetGenderName(uint32 gender_id) {
|
||||
const char* gender_name = "Unknown";
|
||||
if (gender_id == MALE) {
|
||||
gender_name = "Male";
|
||||
} else if (gender_id == FEMALE) {
|
||||
gender_name = "Female";
|
||||
} else if (gender_id == NEUTER) {
|
||||
gender_name = "Neuter";
|
||||
}
|
||||
return gender_name;
|
||||
}
|
||||
@@ -851,6 +851,7 @@
|
||||
|
||||
const char* GetRaceIDName(uint16 race_id);
|
||||
const char* GetPlayerRaceName(uint32 player_race_value);
|
||||
const char* GetGenderName(uint32 gender_id);
|
||||
|
||||
uint32 GetPlayerRaceValue(uint16 race_id);
|
||||
uint32 GetPlayerRaceBit(uint16 race_id);
|
||||
@@ -1602,6 +1603,14 @@ namespace PlayerAppearance
|
||||
#define RACE_FALLEN_KNIGHT_722 722
|
||||
#define RACE_SERVANT_OF_SHADOW_723 723
|
||||
#define RACE_LUCLIN_724 724
|
||||
#define RACE_XARIC_725 725
|
||||
#define RACE_DERVISH_726 726
|
||||
#define RACE_DERVISH_727 727
|
||||
#define RACE_LUCLIN_728 728
|
||||
#define RACE_LUCLIN_729 729
|
||||
#define RACE_ORB_730 730
|
||||
#define RACE_LUCLIN_731 731
|
||||
#define RACE_PEGASUS_732 732
|
||||
#define RACE_INTERACTIVE_OBJECT_2250 2250
|
||||
|
||||
#endif
|
||||
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
entry.zone_in_time = 1800;
|
||||
entry.win_points = 0;
|
||||
entry.lose_points = 0;
|
||||
entry.theme = 1;
|
||||
entry.theme = LDoNThemes::GUK;
|
||||
entry.zone_in_zone_id = 0;
|
||||
entry.zone_in_x = 0;
|
||||
entry.zone_in_y = 0;
|
||||
|
||||
@@ -717,7 +717,7 @@ public:
|
||||
entry.itemclass = 0;
|
||||
entry.itemtype = 0;
|
||||
entry.ldonprice = 0;
|
||||
entry.ldontheme = 0;
|
||||
entry.ldontheme = LDoNThemes::Unused;
|
||||
entry.ldonsold = 0;
|
||||
entry.light = 0;
|
||||
entry.lore = "";
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* DO NOT MODIFY THIS FILE
|
||||
*
|
||||
* This repository was automatically generated and is NOT to be modified directly.
|
||||
* Any repository modifications are meant to be made to the repository extending the base.
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
#define EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../string_util.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseToolGameObjectsRepository {
|
||||
public:
|
||||
struct ToolGameObjects {
|
||||
int id;
|
||||
int zoneid;
|
||||
std::string zonesn;
|
||||
std::string object_name;
|
||||
std::string file_from;
|
||||
int is_global;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"zoneid",
|
||||
"zonesn",
|
||||
"object_name",
|
||||
"file_from",
|
||||
"is_global",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"zoneid",
|
||||
"zonesn",
|
||||
"object_name",
|
||||
"file_from",
|
||||
"is_global",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string SelectColumnsRaw()
|
||||
{
|
||||
return std::string(implode(", ", SelectColumns()));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("tool_game_objects");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static ToolGameObjects NewEntity()
|
||||
{
|
||||
ToolGameObjects entry{};
|
||||
|
||||
entry.id = 0;
|
||||
entry.zoneid = 0;
|
||||
entry.zonesn = "";
|
||||
entry.object_name = "";
|
||||
entry.file_from = "";
|
||||
entry.is_global = 0;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static ToolGameObjects GetToolGameObjectsEntry(
|
||||
const std::vector<ToolGameObjects> &tool_game_objectss,
|
||||
int tool_game_objects_id
|
||||
)
|
||||
{
|
||||
for (auto &tool_game_objects : tool_game_objectss) {
|
||||
if (tool_game_objects.id == tool_game_objects_id) {
|
||||
return tool_game_objects;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static ToolGameObjects FindOne(
|
||||
Database& db,
|
||||
int tool_game_objects_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
tool_game_objects_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
ToolGameObjects entry{};
|
||||
|
||||
entry.id = atoi(row[0]);
|
||||
entry.zoneid = atoi(row[1]);
|
||||
entry.zonesn = row[2] ? row[2] : "";
|
||||
entry.object_name = row[3] ? row[3] : "";
|
||||
entry.file_from = row[4] ? row[4] : "";
|
||||
entry.is_global = atoi(row[5]);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int tool_game_objects_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
tool_game_objects_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
ToolGameObjects tool_game_objects_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> update_values;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
update_values.push_back(columns[1] + " = " + std::to_string(tool_game_objects_entry.zoneid));
|
||||
update_values.push_back(columns[2] + " = '" + EscapeString(tool_game_objects_entry.zonesn) + "'");
|
||||
update_values.push_back(columns[3] + " = '" + EscapeString(tool_game_objects_entry.object_name) + "'");
|
||||
update_values.push_back(columns[4] + " = '" + EscapeString(tool_game_objects_entry.file_from) + "'");
|
||||
update_values.push_back(columns[5] + " = " + std::to_string(tool_game_objects_entry.is_global));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
implode(", ", update_values),
|
||||
PrimaryKey(),
|
||||
tool_game_objects_entry.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static ToolGameObjects InsertOne(
|
||||
Database& db,
|
||||
ToolGameObjects tool_game_objects_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.id));
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid));
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'");
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'");
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'");
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.is_global));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
implode(",", insert_values)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
tool_game_objects_entry.id = results.LastInsertedID();
|
||||
return tool_game_objects_entry;
|
||||
}
|
||||
|
||||
tool_game_objects_entry = NewEntity();
|
||||
|
||||
return tool_game_objects_entry;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
std::vector<ToolGameObjects> tool_game_objects_entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &tool_game_objects_entry: tool_game_objects_entries) {
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.id));
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.zoneid));
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.zonesn) + "'");
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.object_name) + "'");
|
||||
insert_values.push_back("'" + EscapeString(tool_game_objects_entry.file_from) + "'");
|
||||
insert_values.push_back(std::to_string(tool_game_objects_entry.is_global));
|
||||
|
||||
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<ToolGameObjects> All(Database& db)
|
||||
{
|
||||
std::vector<ToolGameObjects> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
ToolGameObjects entry{};
|
||||
|
||||
entry.id = atoi(row[0]);
|
||||
entry.zoneid = atoi(row[1]);
|
||||
entry.zonesn = row[2] ? row[2] : "";
|
||||
entry.object_name = row[3] ? row[3] : "";
|
||||
entry.file_from = row[4] ? row[4] : "";
|
||||
entry.is_global = atoi(row[5]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<ToolGameObjects> GetWhere(Database& db, std::string where_filter)
|
||||
{
|
||||
std::vector<ToolGameObjects> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {}",
|
||||
BaseSelect(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
ToolGameObjects entry{};
|
||||
|
||||
entry.id = atoi(row[0]);
|
||||
entry.zoneid = atoi(row[1]);
|
||||
entry.zonesn = row[2] ? row[2] : "";
|
||||
entry.object_name = row[3] ? row[3] : "";
|
||||
entry.file_from = row[4] ? row[4] : "";
|
||||
entry.is_global = atoi(row[5]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static int DeleteWhere(Database& db, std::string where_filter)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {}",
|
||||
TableName(),
|
||||
where_filter
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int Truncate(Database& db)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"TRUNCATE TABLE {}",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
@@ -110,6 +110,8 @@ public:
|
||||
std::string content_flags;
|
||||
std::string content_flags_disabled;
|
||||
int underworld_teleport_index;
|
||||
int lava_damage;
|
||||
int min_lava_damage;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -212,6 +214,8 @@ public:
|
||||
"content_flags",
|
||||
"content_flags_disabled",
|
||||
"underworld_teleport_index",
|
||||
"lava_damage",
|
||||
"min_lava_damage",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -339,6 +343,8 @@ public:
|
||||
entry.content_flags = "";
|
||||
entry.content_flags_disabled = "";
|
||||
entry.underworld_teleport_index = 0;
|
||||
entry.lava_damage = 50;
|
||||
entry.min_lava_damage = 10;
|
||||
|
||||
return entry;
|
||||
}
|
||||
@@ -466,6 +472,8 @@ public:
|
||||
entry.content_flags = row[89] ? row[89] : "";
|
||||
entry.content_flags_disabled = row[90] ? row[90] : "";
|
||||
entry.underworld_teleport_index = atoi(row[91]);
|
||||
entry.lava_damage = atoi(row[92]);
|
||||
entry.min_lava_damage = atoi(row[93]);
|
||||
|
||||
return entry;
|
||||
}
|
||||
@@ -590,6 +598,8 @@ public:
|
||||
update_values.push_back(columns[89] + " = '" + EscapeString(zone_entry.content_flags) + "'");
|
||||
update_values.push_back(columns[90] + " = '" + EscapeString(zone_entry.content_flags_disabled) + "'");
|
||||
update_values.push_back(columns[91] + " = " + std::to_string(zone_entry.underworld_teleport_index));
|
||||
update_values.push_back(columns[92] + " = " + std::to_string(zone_entry.lava_damage));
|
||||
update_values.push_back(columns[93] + " = " + std::to_string(zone_entry.min_lava_damage));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -703,6 +713,8 @@ public:
|
||||
insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'");
|
||||
insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'");
|
||||
insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index));
|
||||
insert_values.push_back(std::to_string(zone_entry.lava_damage));
|
||||
insert_values.push_back(std::to_string(zone_entry.min_lava_damage));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -824,6 +836,8 @@ public:
|
||||
insert_values.push_back("'" + EscapeString(zone_entry.content_flags) + "'");
|
||||
insert_values.push_back("'" + EscapeString(zone_entry.content_flags_disabled) + "'");
|
||||
insert_values.push_back(std::to_string(zone_entry.underworld_teleport_index));
|
||||
insert_values.push_back(std::to_string(zone_entry.lava_damage));
|
||||
insert_values.push_back(std::to_string(zone_entry.min_lava_damage));
|
||||
|
||||
insert_chunks.push_back("(" + implode(",", insert_values) + ")");
|
||||
}
|
||||
@@ -949,6 +963,8 @@ public:
|
||||
entry.content_flags = row[89] ? row[89] : "";
|
||||
entry.content_flags_disabled = row[90] ? row[90] : "";
|
||||
entry.underworld_teleport_index = atoi(row[91]);
|
||||
entry.lava_damage = atoi(row[92]);
|
||||
entry.min_lava_damage = atoi(row[93]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
@@ -1065,6 +1081,8 @@ public:
|
||||
entry.content_flags = row[89] ? row[89] : "";
|
||||
entry.content_flags_disabled = row[90] ? row[90] : "";
|
||||
entry.underworld_teleport_index = atoi(row[91]);
|
||||
entry.lava_damage = atoi(row[92]);
|
||||
entry.min_lava_damage = atoi(row[93]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
#define EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../string_util.h"
|
||||
#include "base/base_tool_game_objects_repository.h"
|
||||
|
||||
class ToolGameObjectsRepository: public BaseToolGameObjectsRepository {
|
||||
public:
|
||||
|
||||
/**
|
||||
* This file was auto generated and can be modified and extended upon
|
||||
*
|
||||
* Base repository methods are automatically
|
||||
* generated in the "base" version of this repository. The base repository
|
||||
* is immutable and to be left untouched, while methods in this class
|
||||
* are used as extension methods for more specific persistence-layer
|
||||
* accessors or mutators.
|
||||
*
|
||||
* Base Methods (Subject to be expanded upon in time)
|
||||
*
|
||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||
*
|
||||
* InsertOne
|
||||
* UpdateOne
|
||||
* DeleteOne
|
||||
* FindOne
|
||||
* GetWhere(std::string where_filter)
|
||||
* DeleteWhere(std::string where_filter)
|
||||
* InsertMany
|
||||
* All
|
||||
*
|
||||
* Example custom methods in a repository
|
||||
*
|
||||
* ToolGameObjectsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* ToolGameObjectsRepository::GetWhereNeverExpires()
|
||||
* ToolGameObjectsRepository::GetWhereXAndY()
|
||||
* ToolGameObjectsRepository::DeleteWhereXAndY()
|
||||
*
|
||||
* Most of the above could be covered by base methods, but if you as a developer
|
||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||
* method and encapsulate filters there
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_TOOL_GAME_OBJECTS_REPOSITORY_H
|
||||
+35
-2
@@ -139,6 +139,7 @@ RULE_INT(Character, TradeskillUpMakePoison, 2, "Make Poison skillup rate adjustm
|
||||
RULE_INT(Character, TradeskillUpPottery, 4, "Pottery skillup rate adjustment. Lower is faster")
|
||||
RULE_INT(Character, TradeskillUpResearch, 1, "Research skillup rate adjustment. Lower is faster")
|
||||
RULE_INT(Character, TradeskillUpTinkering, 2, "Tinkering skillup rate adjustment. Lower is faster")
|
||||
RULE_INT(Character, TradeskillUpTailoring, 2, "Tailoring skillup rate adjustment. Lower is faster")
|
||||
RULE_BOOL(Character, MarqueeHPUpdates, false, "Will show health percentage in center of screen if health lesser than 100%")
|
||||
RULE_INT(Character, IksarCommonTongue, 95, "Starting value for Common Tongue for Iksars")
|
||||
RULE_INT(Character, OgreCommonTongue, 95, "Starting value for Common Tongue for Ogres")
|
||||
@@ -167,6 +168,12 @@ RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-base
|
||||
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
|
||||
RULE_BOOL(Character, SkillUpFromItems, true, "Allow Skill ups from clickable items")
|
||||
RULE_BOOL(Character, EnableTestBuff, false, "Allow the use of /testbuff")
|
||||
RULE_BOOL(Character, UseResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")
|
||||
RULE_INT(Character, OldResurrectionSicknessSpellID, 757, "757 is Default Old Resurrection Sickness Spell ID")
|
||||
RULE_INT(Character, ResurrectionSicknessSpellID, 756, "756 is Default Resurrection Sickness Spell ID")
|
||||
RULE_BOOL(Character, EnableBardMelody, true, "Enable Bard /melody by default, to disable change to false for a classic experience.")
|
||||
RULE_BOOL(Character, EnableRangerAutoFire, true, "Enable Ranger /autofire by default, to disable change to false for a classic experience.")
|
||||
RULE_BOOL(Character, EnableTGB, true, "Enable /tgb (Target Group Buff) by default, to disable change to false for a classic experience.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@@ -181,6 +188,9 @@ RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a mer
|
||||
RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse")
|
||||
RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor")
|
||||
RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
|
||||
RULE_BOOL(Mercs, IsMercsElixirEnabled, false, "Override AI with elixir logic")
|
||||
RULE_INT(Mercs, MercsElixirHealPercent, 90, "Heal allies at this percent health")
|
||||
RULE_INT(Mercs, MercsElixirAEMinimum, 3, "AE Minimum to trigger AE spells (heals and nukes)")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Guild)
|
||||
@@ -319,7 +329,7 @@ RULE_INT(Spells, MaxDiscSlotsNPC, 0, "Maximum number of NPC disc slots. NPC don'
|
||||
RULE_INT(Spells, MaxTotalSlotsNPC, 60, "Maximum total of NPC slots. The default value is the limit of the Titanium client")
|
||||
RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default value is the limit of the Titanium client")
|
||||
RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells")
|
||||
RULE_INT(Spells, ReflectType, 3, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells")
|
||||
RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells")
|
||||
RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting")
|
||||
RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim")
|
||||
RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized")
|
||||
@@ -380,6 +390,10 @@ RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts
|
||||
RULE_BOOL(Spells, AllowDoubleInvis, false, "Allows you to cast invisibility spells on a player that is already invisible")
|
||||
RULE_BOOL(Spells, AllowSpellMemorizeFromItem, false, "Allows players to memorize spells by right-clicking spell scrolls")
|
||||
RULE_BOOL(Spells, InvisRequiresGroup, false, "Invis requires the the target to be in group.")
|
||||
RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate heal focus")
|
||||
RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells")
|
||||
RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells")
|
||||
RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -391,7 +405,8 @@ RULE_INT(Combat, ArcheryCritDifficulty, 3400, "Value against which is rolled to
|
||||
RULE_INT(Combat, ThrowingCritDifficulty, 1100, "Value against which is rolled to check if a throwing crit is triggered. Lower is easier")
|
||||
RULE_BOOL(Combat, NPCCanCrit, false, "Setting whether an NPC can land critical hits")
|
||||
RULE_BOOL(Combat, UseIntervalAC, true, "Switch whether bonuses, armour class, multipliers, classes and caps should be considered in the calculation of damage values")
|
||||
RULE_INT(Combat, PetAttackMagicLevel, 30, "Level at which pets can cause magic damage")
|
||||
RULE_INT(Combat, PetAttackMagicLevel, 10, "Level at which pets can cause magic damage, no longer used")
|
||||
RULE_INT(Combat, NPCAttackMagicLevel, 10, "Level at which NPC and pets can cause magic damage")
|
||||
RULE_BOOL(Combat, EnableFearPathing, true, "Setting whether to use pathing during fear")
|
||||
RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used")
|
||||
RULE_INT(Combat, FleeGrayHPRatio, 50, "HP percentage when a Gray NPC begins to flee")
|
||||
@@ -477,6 +492,8 @@ RULE_BOOL(Combat, UseExtendedPoisonProcs, false, "Allow old school poisons to la
|
||||
RULE_BOOL(Combat, EnableSneakPull, false, "Enable implementation of Sneak Pull")
|
||||
RULE_INT(Combat, SneakPullAssistRange, 400, "Modified range of assist for sneak pull")
|
||||
RULE_BOOL(Combat, Classic2HBAnimation, false, "2HB will use the 2 hand piercing animation instead of the overhead slashing animation")
|
||||
RULE_BOOL(Combat, ArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
|
||||
RULE_BOOL(Combat, ThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(NPC)
|
||||
@@ -508,6 +525,7 @@ RULE_BOOL(NPC, NPCHealOnGate, true, "Will the NPC Heal on Gate")
|
||||
RULE_BOOL(NPC, UseMeditateBasedManaRegen, false, "Based NPC ooc regen on Meditate skill")
|
||||
RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if enabled")
|
||||
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
|
||||
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Aggro)
|
||||
@@ -580,6 +598,13 @@ RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel f
|
||||
RULE_BOOL(Bots, AllowApplyPoisonCommand, true, "Allows the use of the bot command 'applypoison'")
|
||||
RULE_BOOL(Bots, AllowApplyPotionCommand, true, "Allows the use of the bot command 'applypotion'")
|
||||
RULE_BOOL(Bots, RestrictApplyPotionToRogue, true, "Restricts the bot command 'applypotion' to rogue-usable potions (i.e., poisons)")
|
||||
RULE_BOOL(Bots, OldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore")
|
||||
RULE_BOOL(Bots, ResurrectionSickness, true, "Use Resurrection Sickness based on Resurrection spell cast, set to false to disable Resurrection Sickness.")
|
||||
RULE_INT(Bots, OldResurrectionSicknessSpell, 757, "757 is Default Old Resurrection Sickness Spell")
|
||||
RULE_INT(Bots, ResurrectionSicknessSpell, 756, "756 is Default Resurrection Sickness Spell")
|
||||
RULE_BOOL(Bots, IsBotsElixirEnabled, false, "Override AI with elixir logic")
|
||||
RULE_INT(Bots, BotsElixirHealPercent, 90, "Heal allies at this percent health")
|
||||
RULE_INT(Bots, BotsElixirAEMinimum, 3, "AE Minimum to trigger AE spells (heals and nukes)")
|
||||
RULE_CATEGORY_END()
|
||||
#endif
|
||||
|
||||
@@ -598,6 +623,10 @@ RULE_INT(Chat, IntervalDurationMS, 60000, "Interval length in milliseconds")
|
||||
RULE_INT(Chat, KarmaUpdateIntervalMS, 1200000, "Karma update interval in milliseconds")
|
||||
RULE_INT(Chat, KarmaGlobalChatLimit, 72, "Amount of karma you need to be able to talk in ooc/auction/chat below the level limit")
|
||||
RULE_INT(Chat, GlobalChatLevelLimit, 8, "Level limit you need to of reached to talk in ooc/auction/chat if your karma is too low")
|
||||
RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
|
||||
RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]")
|
||||
RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window")
|
||||
RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Merchant)
|
||||
@@ -779,6 +808,10 @@ RULE_BOOL(Cheat, EnableMQFastMemDetector, true, "Enable the MQFastMem Detector.
|
||||
RULE_BOOL(Cheat, MarkMQWarpLT, false, "Mark clients makeing smaller warps")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Command)
|
||||
RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
#undef RULE_CATEGORY
|
||||
#undef RULE_INT
|
||||
#undef RULE_REAL
|
||||
|
||||
+205
-37
@@ -1,5 +1,5 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
|
||||
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -11,7 +11,7 @@
|
||||
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
|
||||
@@ -24,7 +24,10 @@
|
||||
#include "item_instance.h"
|
||||
#include "item_data.h"
|
||||
#include "../zone/zonedb.h"
|
||||
#include <algorithm>
|
||||
|
||||
// static bucket global
|
||||
std::vector<SaylinkRepository::Saylink> g_cached_saylinks = {};
|
||||
|
||||
bool EQ::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body)
|
||||
{
|
||||
@@ -102,13 +105,13 @@ const std::string &EQ::SayLinkEngine::GenerateLink()
|
||||
m_Link = "<LINKER ERROR>";
|
||||
LogError("SayLinkEngine::GenerateLink() failed to generate a useable say link");
|
||||
LogError(">> LinkType: {}, Lengths: [link: {}({}), body: {}({}), text: {}({})]",
|
||||
m_LinkType,
|
||||
m_Link.length(),
|
||||
EQ::constants::SAY_LINK_MAXIMUM_SIZE,
|
||||
m_LinkBody.length(),
|
||||
EQ::constants::SAY_LINK_BODY_SIZE,
|
||||
m_LinkText.length(),
|
||||
EQ::constants::SAY_LINK_TEXT_SIZE
|
||||
m_LinkType,
|
||||
m_Link.length(),
|
||||
EQ::constants::SAY_LINK_MAXIMUM_SIZE,
|
||||
m_LinkBody.length(),
|
||||
EQ::constants::SAY_LINK_BODY_SIZE,
|
||||
m_LinkText.length(),
|
||||
EQ::constants::SAY_LINK_TEXT_SIZE
|
||||
);
|
||||
LogError(">> LinkBody: {}", m_LinkBody.c_str());
|
||||
LogError(">> LinkText: {}", m_LinkText.c_str());
|
||||
@@ -295,33 +298,9 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo
|
||||
{
|
||||
uint32 saylink_id = 0;
|
||||
|
||||
/**
|
||||
* Query for an existing phrase and id in the saylink table
|
||||
*/
|
||||
std::string query = StringFormat(
|
||||
"SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1",
|
||||
EscapeString(saylink_text).c_str());
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
if (results.Success()) {
|
||||
if (results.RowCount() >= 1) {
|
||||
for (auto row = results.begin(); row != results.end(); ++row)
|
||||
saylink_id = static_cast<uint32>(atoi(row[0]));
|
||||
}
|
||||
else {
|
||||
std::string insert_query = StringFormat(
|
||||
"INSERT INTO `saylink` (`phrase`) VALUES ('%s')",
|
||||
EscapeString(saylink_text).c_str());
|
||||
|
||||
results = database.QueryDatabase(insert_query);
|
||||
if (!results.Success()) {
|
||||
LogError("Error in saylink phrase queries {}", results.ErrorMessage().c_str());
|
||||
}
|
||||
else {
|
||||
saylink_id = results.LastInsertedID();
|
||||
}
|
||||
}
|
||||
SaylinkRepository::Saylink saylink = GetOrSaveSaylink(saylink_text);
|
||||
if (saylink.id > 0) {
|
||||
saylink_id = saylink.id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,4 +318,193 @@ std::string EQ::SayLinkEngine::GenerateQuestSaylink(std::string saylink_text, bo
|
||||
linker.SetProxyText(link_name.c_str());
|
||||
|
||||
return linker.GenerateLink();
|
||||
}
|
||||
}
|
||||
|
||||
std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
|
||||
{
|
||||
std::string new_message = message;
|
||||
int link_index = 0;
|
||||
int saylink_index = 0;
|
||||
std::vector<std::string> links = {};
|
||||
std::vector<std::string> saylinks = {};
|
||||
int saylink_length = 50;
|
||||
std::string saylink_separator = "\u0012";
|
||||
std::string saylink_partial = "00000";
|
||||
|
||||
LogSaylinkDetail("new_message pre pass 1 [{}]", new_message);
|
||||
|
||||
// first pass - strip existing saylinks by putting placeholder anchors on them
|
||||
for (auto &saylink: split_string(new_message, saylink_separator)) {
|
||||
if (!saylink.empty() && saylink.length() > saylink_length &&
|
||||
saylink.find(saylink_partial) != std::string::npos) {
|
||||
saylinks.emplace_back(saylink);
|
||||
|
||||
LogSaylinkDetail("Found saylink [{}]", saylink);
|
||||
|
||||
// replace with anchor
|
||||
find_replace(
|
||||
new_message,
|
||||
fmt::format("{}", saylink),
|
||||
fmt::format("<saylink:{}>", saylink_index)
|
||||
);
|
||||
|
||||
saylink_index++;
|
||||
}
|
||||
}
|
||||
|
||||
LogSaylinkDetail("new_message post pass 1 [{}]", new_message);
|
||||
|
||||
LogSaylinkDetail("saylink separator count [{}]", std::count(new_message.begin(), new_message.end(), '\u0012'));
|
||||
|
||||
// loop through brackets until none exist
|
||||
if (new_message.find('[') != std::string::npos) {
|
||||
for (auto &b: split_string(new_message, "[")) {
|
||||
if (!b.empty() && b.find(']') != std::string::npos) {
|
||||
std::vector<std::string> right_split = split_string(b, "]");
|
||||
if (!right_split.empty()) {
|
||||
std::string bracket_message = trim(right_split[0]);
|
||||
|
||||
// we shouldn't see a saylink fragment here, ignore this bracket
|
||||
if (bracket_message.find(saylink_partial) != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip where multiple saylinks are within brackets
|
||||
if (bracket_message.find(saylink_separator) != std::string::npos &&
|
||||
std::count(bracket_message.begin(), bracket_message.end(), '\u0012') > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if non empty bracket contents
|
||||
if (!bracket_message.empty()) {
|
||||
LogSaylinkDetail("Found bracket_message [{}]", bracket_message);
|
||||
|
||||
// already a saylink
|
||||
// todo: improve this later
|
||||
if (!bracket_message.empty() &&
|
||||
(bracket_message.length() > saylink_length ||
|
||||
bracket_message.find(saylink_separator) != std::string::npos)) {
|
||||
links.emplace_back(bracket_message);
|
||||
}
|
||||
else {
|
||||
links.emplace_back(
|
||||
EQ::SayLinkEngine::GenerateQuestSaylink(
|
||||
bracket_message,
|
||||
false,
|
||||
bracket_message
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// replace with anchor
|
||||
find_replace(
|
||||
new_message,
|
||||
fmt::format("[{}]", bracket_message),
|
||||
fmt::format("<prelink:{}>", link_index)
|
||||
);
|
||||
|
||||
link_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogSaylinkDetail("new_message post pass 2 (post brackets) [{}]", new_message);
|
||||
|
||||
// strip any current delimiters of saylinks
|
||||
find_replace(new_message, saylink_separator, "");
|
||||
|
||||
// pop links onto anchors
|
||||
link_index = 0;
|
||||
for (auto &link: links) {
|
||||
|
||||
// strip any current delimiters of saylinks
|
||||
find_replace(link, saylink_separator, "");
|
||||
|
||||
find_replace(
|
||||
new_message,
|
||||
fmt::format("<prelink:{}>", link_index),
|
||||
fmt::format("[\u0012{}\u0012]", link)
|
||||
);
|
||||
link_index++;
|
||||
}
|
||||
|
||||
LogSaylinkDetail("new_message post pass 3 (post prelink anchor pop) [{}]", new_message);
|
||||
|
||||
// pop links onto anchors
|
||||
saylink_index = 0;
|
||||
for (auto &link: saylinks) {
|
||||
// strip any current delimiters of saylinks
|
||||
find_replace(link, saylink_separator, "");
|
||||
|
||||
// check to see if we did a double anchor pass (existing saylink that was also inside brackets)
|
||||
// this means we found a saylink and we're checking to see if we're already encoded before double encoding
|
||||
if (new_message.find(fmt::format("\u0012<saylink:{}>\u0012", saylink_index)) != std::string::npos) {
|
||||
LogSaylinkDetail("Found encoded saylink at index [{}]", saylink_index);
|
||||
|
||||
find_replace(
|
||||
new_message,
|
||||
fmt::format("\u0012<saylink:{}>\u0012", saylink_index),
|
||||
fmt::format("\u0012{}\u0012", link)
|
||||
);
|
||||
saylink_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
find_replace(
|
||||
new_message,
|
||||
fmt::format("<saylink:{}>", saylink_index),
|
||||
fmt::format("\u0012{}\u0012", link)
|
||||
);
|
||||
saylink_index++;
|
||||
}
|
||||
|
||||
LogSaylinkDetail("new_message post pass 4 (post saylink anchor pop) [{}]", new_message);
|
||||
|
||||
return new_message;
|
||||
}
|
||||
|
||||
void EQ::SayLinkEngine::LoadCachedSaylinks()
|
||||
{
|
||||
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not like '%#%'");
|
||||
LogSaylink("Loaded [{}] saylinks into cache", saylinks.size());
|
||||
g_cached_saylinks = saylinks;
|
||||
}
|
||||
|
||||
SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string saylink_text)
|
||||
{
|
||||
// return cached saylink if exist
|
||||
if (!g_cached_saylinks.empty()) {
|
||||
for (auto &s: g_cached_saylinks) {
|
||||
if (s.phrase == saylink_text) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto saylinks = SaylinkRepository::GetWhere(
|
||||
database,
|
||||
fmt::format("phrase = '{}'", EscapeString(saylink_text))
|
||||
);
|
||||
|
||||
// return if found from the database
|
||||
if (!saylinks.empty()) {
|
||||
return saylinks[0];
|
||||
}
|
||||
|
||||
// if not found in database - save
|
||||
if (saylinks.empty()) {
|
||||
auto new_saylink = SaylinkRepository::NewEntity();
|
||||
new_saylink.phrase = saylink_text;
|
||||
|
||||
// persist to database
|
||||
auto link = SaylinkRepository::InsertOne(database, new_saylink);
|
||||
if (link.id > 0) {
|
||||
g_cached_saylinks.emplace_back(link);
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,17 +1,17 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
|
||||
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
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
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "types.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "repositories/saylink_repository.h"
|
||||
|
||||
struct ServerLootItem_Struct;
|
||||
|
||||
@@ -105,6 +105,8 @@ namespace EQ
|
||||
|
||||
void Reset();
|
||||
|
||||
static std::string InjectSaylinksIfNotExist(const char *message);
|
||||
static void LoadCachedSaylinks();
|
||||
private:
|
||||
void generate_body();
|
||||
void generate_text();
|
||||
@@ -120,6 +122,7 @@ namespace EQ
|
||||
std::string m_LinkBody;
|
||||
std::string m_LinkText;
|
||||
bool m_Error;
|
||||
static SaylinkRepository::Saylink GetOrSaveSaylink(std::string saylink_text);
|
||||
};
|
||||
|
||||
} /*EQEmu*/
|
||||
|
||||
+188
-594
@@ -227,90 +227,26 @@
|
||||
#define ServerOP_HotReloadQuests 0x4011
|
||||
#define ServerOP_UpdateSchedulerEvents 0x4012
|
||||
|
||||
#define ServerOP_CZCastSpellPlayer 0x4500
|
||||
#define ServerOP_CZCastSpellGroup 0x4501
|
||||
#define ServerOP_CZCastSpellRaid 0x4502
|
||||
#define ServerOP_CZCastSpellGuild 0x4503
|
||||
#define ServerOP_CZMarqueePlayer 0x4504
|
||||
#define ServerOP_CZMarqueeGroup 0x4505
|
||||
#define ServerOP_CZMarqueeRaid 0x4506
|
||||
#define ServerOP_CZMarqueeGuild 0x4507
|
||||
#define ServerOP_CZMessagePlayer 0x4508
|
||||
#define ServerOP_CZMessageGroup 0x4509
|
||||
#define ServerOP_CZMessageRaid 0x4510
|
||||
#define ServerOP_CZMessageGuild 0x4511
|
||||
#define ServerOP_CZMovePlayer 0x4512
|
||||
#define ServerOP_CZMoveGroup 0x4513
|
||||
#define ServerOP_CZMoveRaid 0x4514
|
||||
#define ServerOP_CZMoveGuild 0x4515
|
||||
#define ServerOP_CZMoveInstancePlayer 0x4516
|
||||
#define ServerOP_CZMoveInstanceGroup 0x4517
|
||||
#define ServerOP_CZMoveInstanceRaid 0x4518
|
||||
#define ServerOP_CZMoveInstanceGuild 0x4519
|
||||
#define ServerOP_CZRemoveSpellPlayer 0x4520
|
||||
#define ServerOP_CZRemoveSpellGroup 0x4521
|
||||
#define ServerOP_CZRemoveSpellRaid 0x4522
|
||||
#define ServerOP_CZRemoveSpellGuild 0x4523
|
||||
#define ServerOP_CZSetEntityVariableByClientName 0x4524
|
||||
#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4525
|
||||
#define ServerOP_CZSetEntityVariableByGroupID 0x4526
|
||||
#define ServerOP_CZSetEntityVariableByRaidID 0x4527
|
||||
#define ServerOP_CZSetEntityVariableByGuildID 0x4528
|
||||
#define ServerOP_CZSignalClient 0x4529
|
||||
#define ServerOP_CZSignalClientByName 0x4530
|
||||
#define ServerOP_CZSignalNPC 0x4531
|
||||
#define ServerOP_CZSignalGroup 0x4532
|
||||
#define ServerOP_CZSignalRaid 0x4533
|
||||
#define ServerOP_CZSignalGuild 0x4534
|
||||
#define ServerOP_CZTaskActivityResetPlayer 0x4535
|
||||
#define ServerOP_CZTaskActivityResetGroup 0x4536
|
||||
#define ServerOP_CZTaskActivityResetRaid 0x4537
|
||||
#define ServerOP_CZTaskActivityResetGuild 0x4538
|
||||
#define ServerOP_CZTaskActivityUpdatePlayer 0x4539
|
||||
#define ServerOP_CZTaskActivityUpdateGroup 0x4540
|
||||
#define ServerOP_CZTaskActivityUpdateRaid 0x4541
|
||||
#define ServerOP_CZTaskActivityUpdateGuild 0x4542
|
||||
#define ServerOP_CZTaskAssignPlayer 0x4543
|
||||
#define ServerOP_CZTaskAssignGroup 0x4544
|
||||
#define ServerOP_CZTaskAssignRaid 0x4545
|
||||
#define ServerOP_CZTaskAssignGuild 0x4546
|
||||
#define ServerOP_CZTaskDisablePlayer 0x4547
|
||||
#define ServerOP_CZTaskDisableGroup 0x4548
|
||||
#define ServerOP_CZTaskDisableRaid 0x4549
|
||||
#define ServerOP_CZTaskDisableGuild 0x4550
|
||||
#define ServerOP_CZTaskEnablePlayer 0x4551
|
||||
#define ServerOP_CZTaskEnableGroup 0x4552
|
||||
#define ServerOP_CZTaskEnableRaid 0x4553
|
||||
#define ServerOP_CZTaskEnableGuild 0x4554
|
||||
#define ServerOP_CZTaskFailPlayer 0x4555
|
||||
#define ServerOP_CZTaskFailGroup 0x4556
|
||||
#define ServerOP_CZTaskFailRaid 0x4557
|
||||
#define ServerOP_CZTaskFailGuild 0x4558
|
||||
#define ServerOP_CZTaskRemovePlayer 0x4559
|
||||
#define ServerOP_CZTaskRemoveGroup 0x4560
|
||||
#define ServerOP_CZTaskRemoveRaid 0x4561
|
||||
#define ServerOP_CZTaskRemoveGuild 0x4562
|
||||
#define ServerOP_CZClientMessageString 0x4563
|
||||
#define ServerOP_CZLDoNUpdate 0x4564
|
||||
#define ServerOP_CZDialogueWindow 0x4500
|
||||
#define ServerOP_CZLDoNUpdate 0x4501
|
||||
#define ServerOP_CZMarquee 0x4502
|
||||
#define ServerOP_CZMessage 0x4503
|
||||
#define ServerOP_CZMove 0x4504
|
||||
#define ServerOP_CZSetEntityVariable 0x4505
|
||||
#define ServerOP_CZSignal 0x4506
|
||||
#define ServerOP_CZSpell 0x4507
|
||||
#define ServerOP_CZTaskUpdate 0x4508
|
||||
#define ServerOP_CZClientMessageString 0x4509
|
||||
|
||||
#define ServerOP_WWAssignTask 0x4750
|
||||
#define ServerOP_WWCastSpell 0x4751
|
||||
#define ServerOP_WWCompleteActivity 0x4752
|
||||
#define ServerOP_WWDisableTask 0x4753
|
||||
#define ServerOP_WWEnableTask 0x4754
|
||||
#define ServerOP_WWFailTask 0x4755
|
||||
#define ServerOP_WWMarquee 0x4756
|
||||
#define ServerOP_WWMessage 0x4757
|
||||
#define ServerOP_WWMove 0x4758
|
||||
#define ServerOP_WWMoveInstance 0x4759
|
||||
#define ServerOP_WWRemoveSpell 0x4760
|
||||
#define ServerOP_WWRemoveTask 0x4761
|
||||
#define ServerOP_WWResetActivity 0x4762
|
||||
#define ServerOP_WWSetEntityVariableClient 0x4763
|
||||
#define ServerOP_WWSetEntityVariableNPC 0x4764
|
||||
#define ServerOP_WWSignalClient 0x4765
|
||||
#define ServerOP_WWSignalNPC 0x4766
|
||||
#define ServerOP_WWUpdateActivity 0x4767
|
||||
#define ServerOP_WWDialogueWindow 0x4750
|
||||
#define ServerOP_WWLDoNUpdate 0x4751
|
||||
#define ServerOP_WWMarquee 0x4752
|
||||
#define ServerOP_WWMessage 0x4753
|
||||
#define ServerOP_WWMove 0x4754
|
||||
#define ServerOP_WWSetEntityVariable 0x4755
|
||||
#define ServerOP_WWSignal 0x4756
|
||||
#define ServerOP_WWSpell 0x4757
|
||||
#define ServerOP_WWTaskUpdate 0x4758
|
||||
|
||||
/**
|
||||
* QueryServer
|
||||
@@ -325,17 +261,79 @@
|
||||
#define ServerOP_QSPlayerDropItem 0x5007
|
||||
|
||||
enum {
|
||||
CZLDoNUpdateType_Character = 0,
|
||||
CZLDoNUpdateType_Group,
|
||||
CZLDoNUpdateType_Raid,
|
||||
CZLDoNUpdateType_Guild,
|
||||
CZLDoNUpdateType_Expedition
|
||||
CZUpdateType_Character,
|
||||
CZUpdateType_Group,
|
||||
CZUpdateType_Raid,
|
||||
CZUpdateType_Guild,
|
||||
CZUpdateType_Expedition,
|
||||
CZUpdateType_ClientName,
|
||||
CZUpdateType_NPC
|
||||
};
|
||||
|
||||
enum {
|
||||
CZLDoNUpdateSubtype_Win = 0,
|
||||
CZLDoNUpdateSubtype_Loss,
|
||||
CZLDoNUpdateSubtype_Points
|
||||
CZLDoNUpdateSubtype_AddLoss,
|
||||
CZLDoNUpdateSubtype_AddPoints,
|
||||
CZLDoNUpdateSubtype_AddWin,
|
||||
CZLDoNUpdateSubtype_RemoveLoss,
|
||||
CZLDoNUpdateSubtype_RemoveWin,
|
||||
};
|
||||
|
||||
enum {
|
||||
CZMoveUpdateSubtype_MoveZone,
|
||||
CZMoveUpdateSubtype_MoveZoneInstance
|
||||
};
|
||||
|
||||
enum {
|
||||
CZSpellUpdateSubtype_Cast,
|
||||
CZSpellUpdateSubtype_Remove
|
||||
};
|
||||
|
||||
enum {
|
||||
CZTaskUpdateSubtype_ActivityReset,
|
||||
CZTaskUpdateSubtype_ActivityUpdate,
|
||||
CZTaskUpdateSubtype_AssignTask,
|
||||
CZTaskUpdateSubtype_DisableTask,
|
||||
CZTaskUpdateSubtype_EnableTask,
|
||||
CZTaskUpdateSubtype_FailTask,
|
||||
CZTaskUpdateSubtype_RemoveTask
|
||||
};
|
||||
|
||||
enum {
|
||||
WWLDoNUpdateType_AddLoss,
|
||||
WWLDoNUpdateType_AddPoints,
|
||||
WWLDoNUpdateType_AddWin,
|
||||
WWLDoNUpdateType_RemoveLoss,
|
||||
WWLDoNUpdateType_RemoveWin
|
||||
};
|
||||
|
||||
enum {
|
||||
WWMoveUpdateType_MoveZone,
|
||||
WWMoveUpdateType_MoveZoneInstance
|
||||
};
|
||||
|
||||
enum {
|
||||
WWSetEntityVariableUpdateType_Character,
|
||||
WWSetEntityVariableUpdateType_NPC
|
||||
};
|
||||
|
||||
enum {
|
||||
WWSignalUpdateType_Character,
|
||||
WWSignalUpdateType_NPC
|
||||
};
|
||||
|
||||
enum {
|
||||
WWSpellUpdateType_Cast,
|
||||
WWSpellUpdateType_Remove
|
||||
};
|
||||
|
||||
enum {
|
||||
WWTaskUpdateType_ActivityReset,
|
||||
WWTaskUpdateType_ActivityUpdate,
|
||||
WWTaskUpdateType_AssignTask,
|
||||
WWTaskUpdateType_DisableTask,
|
||||
WWTaskUpdateType_EnableTask,
|
||||
WWTaskUpdateType_FailTask,
|
||||
WWTaskUpdateType_RemoveTask
|
||||
};
|
||||
|
||||
/* Query Serv Generic Packet Flag/Type Enumeration */
|
||||
@@ -1440,489 +1438,107 @@ struct QSGeneralQuery_Struct {
|
||||
char QueryString[0];
|
||||
};
|
||||
|
||||
struct CZCastSpellPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZCastSpellGroup_Struct {
|
||||
int group_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZCastSpellRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZCastSpellGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZClientSignal_Struct {
|
||||
int character_id;
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZGroupSignal_Struct {
|
||||
int group_id;
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZRaidSignal_Struct {
|
||||
int raid_id;
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZGuildSignal_Struct {
|
||||
int guild_id;
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZNPCSignal_Struct {
|
||||
uint32 npctype_id;
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZClientMessageString_Struct {
|
||||
uint32 string_id;
|
||||
uint16 chat_type;
|
||||
char character_name[64];
|
||||
char client_name[64];
|
||||
uint32 args_size;
|
||||
char args[1]; // null delimited
|
||||
};
|
||||
|
||||
struct CZClientSignalByName_Struct {
|
||||
char character_name[64];
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct CZCompleteActivityPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZCompleteActivityGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZCompleteActivityRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZCompleteActivityGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZMovePlayer_Struct {
|
||||
int character_id;
|
||||
char zone_short_name[32];
|
||||
};
|
||||
|
||||
struct CZMarqueePlayer_Struct {
|
||||
int character_id;
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
uint32 fade_in;
|
||||
uint32 fade_out;
|
||||
uint32 duration;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMarqueeGroup_Struct {
|
||||
int group_id;
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
uint32 fade_in;
|
||||
uint32 fade_out;
|
||||
uint32 duration;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMarqueeRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
uint32 fade_in;
|
||||
uint32 fade_out;
|
||||
uint32 duration;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMarqueeGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
uint32 fade_in;
|
||||
uint32 fade_out;
|
||||
uint32 duration;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMessagePlayer_Struct {
|
||||
uint32 type;
|
||||
char character_name[64];
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMessageGroup_Struct {
|
||||
uint32 type;
|
||||
int group_id;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMessageRaid_Struct {
|
||||
uint32 type;
|
||||
int raid_id;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMessageGuild_Struct {
|
||||
uint32 type;
|
||||
int guild_id;
|
||||
char message[512];
|
||||
};
|
||||
|
||||
struct CZMoveGroup_Struct {
|
||||
int group_id;
|
||||
char zone_short_name[32];
|
||||
};
|
||||
|
||||
struct CZMoveRaid_Struct {
|
||||
int raid_id;
|
||||
char zone_short_name[32];
|
||||
};
|
||||
|
||||
struct CZMoveGuild_Struct {
|
||||
int guild_id;
|
||||
char zone_short_name[32];
|
||||
};
|
||||
|
||||
struct CZMoveInstancePlayer_Struct {
|
||||
int character_id;
|
||||
uint16 instance_id;
|
||||
};
|
||||
|
||||
struct CZMoveInstanceGroup_Struct {
|
||||
int group_id;
|
||||
uint16 instance_id;
|
||||
};
|
||||
|
||||
struct CZMoveInstanceRaid_Struct {
|
||||
int raid_id;
|
||||
uint16 instance_id;
|
||||
};
|
||||
|
||||
struct CZMoveInstanceGuild_Struct {
|
||||
int guild_id;
|
||||
uint16 instance_id;
|
||||
};
|
||||
|
||||
struct CZRemoveSpellPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZRemoveSpellGroup_Struct {
|
||||
int group_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZRemoveSpellRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZRemoveSpellGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 spell_id;
|
||||
};
|
||||
|
||||
struct CZRemoveTaskPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZRemoveTaskGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZRemoveTaskRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZRemoveTaskGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZResetActivityPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZResetActivityGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZResetActivityRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZResetActivityGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZSetEntVarByNPCTypeID_Struct {
|
||||
uint32 npctype_id;
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct CZSetEntVarByClientName_Struct {
|
||||
char character_name[64];
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct CZSetEntVarByGroupID_Struct {
|
||||
int group_id;
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct CZSetEntVarByRaidID_Struct {
|
||||
int raid_id;
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct CZSetEntVarByGuildID_Struct {
|
||||
int guild_id;
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct CZTaskActivityResetPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZTaskActivityResetGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZTaskActivityResetRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZTaskActivityResetGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
};
|
||||
|
||||
struct CZTaskActivityUpdatePlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
int activity_count;
|
||||
};
|
||||
|
||||
struct CZTaskActivityUpdateGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
int activity_count;
|
||||
};
|
||||
|
||||
struct CZTaskActivityUpdateRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
int activity_count;
|
||||
};
|
||||
|
||||
struct CZTaskActivityUpdateGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
int activity_count;
|
||||
};
|
||||
|
||||
struct CZTaskAssignPlayer_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
bool enforce_level_requirement;
|
||||
};
|
||||
|
||||
struct CZTaskAssignGroup_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
bool enforce_level_requirement;
|
||||
};
|
||||
|
||||
struct CZTaskAssignRaid_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
bool enforce_level_requirement;
|
||||
};
|
||||
|
||||
struct CZTaskAssignGuild_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
bool enforce_level_requirement;
|
||||
};
|
||||
|
||||
struct CZTaskDisablePlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskDisableGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskDisableRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskDisableGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskEnablePlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskEnableGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskEnableRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskEnableGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskFailPlayer_Struct {
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskFailGroup_Struct {
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskFailRaid_Struct {
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskFailGuild_Struct {
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskRemovePlayer_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int character_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskRemoveGroup_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int group_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskRemoveRaid_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int raid_id;
|
||||
uint32 task_id;
|
||||
};
|
||||
|
||||
struct CZTaskRemoveGuild_Struct {
|
||||
uint16 npc_entity_id;
|
||||
int guild_id;
|
||||
uint32 task_id;
|
||||
struct CZDialogueWindow_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
char message[4096];
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct CZLDoNUpdate_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition
|
||||
uint8 update_subtype; // 0 - Win, 1 - Loss, 2 - Points
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
uint8 update_subtype; // 0 - Loss, 1 - Points, 2 - Win
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint32 theme_id;
|
||||
int points; // Always 1, except for when Points are used
|
||||
int points; // Only used in Points Subtype, else 1
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct WWAssignTask_Struct {
|
||||
uint16 npc_entity_id;
|
||||
uint32 task_id;
|
||||
bool enforce_level_requirement;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
struct CZMarquee_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
uint32 fade_in;
|
||||
uint32 fade_out;
|
||||
uint32 duration;
|
||||
char message[512];
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct WWCastSpell_Struct {
|
||||
struct CZMessage_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
int update_identifier; // Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint32 type;
|
||||
char message[512];
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct CZMove_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
uint8 update_subtype; // 0 - Move Zone, 1 - Move Zone Instance
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint16 instance_id; // Only used by Move Zone Instance, else 0
|
||||
char zone_short_name[32]; // Only by with Move Zone, else empty
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct CZSetEntityVariable_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC
|
||||
int update_identifier; // Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
char client_name[64]; // Only used by Character Type, else empty
|
||||
};
|
||||
|
||||
struct CZSignal_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name, 6 - NPC
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, Expedition ID, or NPC ID based on update type, 0 for Character Name
|
||||
uint32 signal;
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct CZSpell_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
uint8 update_subtype; // 0 - Cast Spell, 1 - Remove Spell
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint32 spell_id;
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct CZTaskUpdate_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - Group, 2 - Raid, 3 - Guild, 4 - Expedition, 5 - Character Name
|
||||
uint8 update_subtype; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task
|
||||
int update_identifier; // Character ID, Group ID, Raid ID, Guild ID, or Expedition ID based on update type, 0 for Character Name
|
||||
uint32 task_identifier;
|
||||
int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1
|
||||
int update_count; // Only used by Activity Update, else 1
|
||||
bool enforce_level_requirement; // Only used by Assign Task
|
||||
char client_name[64]; // Only used by Character Name Type, else empty
|
||||
};
|
||||
|
||||
struct WWDialogueWindow_Struct {
|
||||
char message[4096];
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWDisableTask_Struct {
|
||||
uint32 task_id;
|
||||
struct WWLDoNUpdate_Struct {
|
||||
uint8 update_type; // 0 - Loss, 1 - Points, 2 - Win
|
||||
uint32 theme_id;
|
||||
int points; // Only used in Points Subtype, else 1
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWEnableTask_Struct {
|
||||
uint32 task_id;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWFailTask_Struct {
|
||||
uint32 task_id;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
struct WWMarquee_Struct {
|
||||
uint32 type;
|
||||
uint32 priority;
|
||||
@@ -1942,63 +1558,41 @@ struct WWMessage_Struct {
|
||||
};
|
||||
|
||||
struct WWMove_Struct {
|
||||
char zone_short_name[32];
|
||||
uint8 update_type; // 0 - Move Zone, 1 - Move Zone Instance
|
||||
char zone_short_name[32]; // Used with Move Zone
|
||||
uint16 instance_id; // Used with Move Zone Instance
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWMoveInstance_Struct {
|
||||
uint16 instance_id;
|
||||
struct WWSetEntityVariable_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - NPC
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWRemoveSpell_Struct {
|
||||
struct WWSignal_Struct {
|
||||
uint8 update_type; // 0 - Character, 1 - NPC
|
||||
uint32 signal;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWSpell_Struct {
|
||||
uint8 update_type; // 0 - Cast Spell, 1 - Remove Spell
|
||||
uint32 spell_id;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWRemoveTask_Struct {
|
||||
uint32 task_id;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
|
||||
};
|
||||
|
||||
struct WWResetActivity_Struct {
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWSetEntVarClient_Struct {
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWSetEntVarNPC_Struct {
|
||||
char variable_name[256];
|
||||
char variable_value[256];
|
||||
};
|
||||
|
||||
struct WWSignalClient_Struct {
|
||||
uint32 signal;
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
struct WWSignalNPC_Struct {
|
||||
uint32 signal;
|
||||
};
|
||||
|
||||
struct WWUpdateActivity_Struct {
|
||||
uint32 task_id;
|
||||
int activity_id;
|
||||
int activity_count;
|
||||
struct WWTaskUpdate_Struct {
|
||||
uint8 update_type; // 0 - Activity Reset, 1 - Activity Update, 2 - Assign Task, 3 - Disable Task, 4 - Enable Task, 5 - Fail Task, 6 - Remove Task
|
||||
uint32 task_identifier;
|
||||
int task_subidentifier; // Activity ID for Activity Reset and Activity Update, NPC Entity ID for Assign Task, else -1
|
||||
int update_count; // Update Count for Activity Update, else 1
|
||||
bool enforce_level_requirement; // Only used by Assign Task, else false
|
||||
uint8 min_status;
|
||||
uint8 max_status;
|
||||
};
|
||||
|
||||
+59
-58
@@ -1118,7 +1118,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
|
||||
item.Haste = (uint32)atoul(row[ItemField::haste]);
|
||||
item.DamageShield = (uint32)atoul(row[ItemField::damageshield]);
|
||||
item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]);
|
||||
item.RecastType = (uint32)atoul(row[ItemField::recasttype]);
|
||||
item.RecastType = (int)atoi(row[ItemField::recasttype]);
|
||||
item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]);
|
||||
item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]);
|
||||
item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true;
|
||||
@@ -1682,7 +1682,7 @@ void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpe
|
||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||
int spellID = atoi(row[0]);
|
||||
if((spellID > 0) && (spellID <= iMaxSpellID))
|
||||
sp[spellID].DamageShieldType = atoi(row[1]);
|
||||
sp[spellID].damage_shield_type = atoi(row[1]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1762,48 +1762,48 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades));
|
||||
|
||||
sp[tempid].range=static_cast<float>(atof(row[9]));
|
||||
sp[tempid].aoerange=static_cast<float>(atof(row[10]));
|
||||
sp[tempid].pushback=static_cast<float>(atof(row[11]));
|
||||
sp[tempid].pushup=static_cast<float>(atof(row[12]));
|
||||
sp[tempid].aoe_range=static_cast<float>(atof(row[10]));
|
||||
sp[tempid].push_back=static_cast<float>(atof(row[11]));
|
||||
sp[tempid].push_up=static_cast<float>(atof(row[12]));
|
||||
sp[tempid].cast_time=atoi(row[13]);
|
||||
sp[tempid].recovery_time=atoi(row[14]);
|
||||
sp[tempid].recast_time=atoi(row[15]);
|
||||
sp[tempid].buffdurationformula=atoi(row[16]);
|
||||
sp[tempid].buffduration=atoi(row[17]);
|
||||
sp[tempid].AEDuration=atoi(row[18]);
|
||||
sp[tempid].buff_duration_formula=atoi(row[16]);
|
||||
sp[tempid].buff_duration=atoi(row[17]);
|
||||
sp[tempid].aoe_duration=atoi(row[18]);
|
||||
sp[tempid].mana=atoi(row[19]);
|
||||
|
||||
int y=0;
|
||||
for(y=0; y< EFFECT_COUNT;y++)
|
||||
sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value
|
||||
sp[tempid].base_value[y]=atoi(row[20+y]); // effect_base_value
|
||||
|
||||
for(y=0; y < EFFECT_COUNT; y++)
|
||||
sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value
|
||||
sp[tempid].limit_value[y]=atoi(row[32+y]); // effect_limit_value
|
||||
|
||||
for(y=0; y< EFFECT_COUNT;y++)
|
||||
sp[tempid].max[y]=atoi(row[44+y]);
|
||||
sp[tempid].max_value[y]=atoi(row[44+y]);
|
||||
|
||||
for(y=0; y< 4;y++)
|
||||
sp[tempid].components[y]=atoi(row[58+y]);
|
||||
sp[tempid].component[y]=atoi(row[58+y]);
|
||||
|
||||
for(y=0; y< 4;y++)
|
||||
sp[tempid].component_counts[y]=atoi(row[62+y]);
|
||||
sp[tempid].component_count[y]=atoi(row[62+y]);
|
||||
|
||||
for(y=0; y< 4;y++)
|
||||
sp[tempid].NoexpendReagent[y]=atoi(row[66+y]);
|
||||
sp[tempid].no_expend_reagent[y]=atoi(row[66+y]);
|
||||
|
||||
for(y=0; y< EFFECT_COUNT;y++)
|
||||
sp[tempid].formula[y]=atoi(row[70+y]);
|
||||
|
||||
sp[tempid].goodEffect=atoi(row[83]);
|
||||
sp[tempid].Activated=atoi(row[84]);
|
||||
sp[tempid].resisttype=atoi(row[85]);
|
||||
sp[tempid].good_effect=atoi(row[83]);
|
||||
sp[tempid].activated=atoi(row[84]);
|
||||
sp[tempid].resist_type=atoi(row[85]);
|
||||
|
||||
for(y=0; y< EFFECT_COUNT;y++)
|
||||
sp[tempid].effectid[y]=atoi(row[86+y]);
|
||||
sp[tempid].effect_id[y]=atoi(row[86+y]);
|
||||
|
||||
sp[tempid].targettype = (SpellTargetType) atoi(row[98]);
|
||||
sp[tempid].basediff=atoi(row[99]);
|
||||
sp[tempid].target_type = (SpellTargetType) atoi(row[98]);
|
||||
sp[tempid].base_difficulty=atoi(row[99]);
|
||||
|
||||
int tmp_skill = atoi(row[100]);;
|
||||
|
||||
@@ -1812,15 +1812,15 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
else
|
||||
sp[tempid].skill = (EQ::skills::SkillType) tmp_skill;
|
||||
|
||||
sp[tempid].zonetype=atoi(row[101]);
|
||||
sp[tempid].EnvironmentType=atoi(row[102]);
|
||||
sp[tempid].TimeOfDay=atoi(row[103]);
|
||||
sp[tempid].zone_type=atoi(row[101]);
|
||||
sp[tempid].environment_type=atoi(row[102]);
|
||||
sp[tempid].time_of_day=atoi(row[103]);
|
||||
|
||||
for(y=0; y < PLAYER_CLASS_COUNT;y++)
|
||||
sp[tempid].classes[y]=atoi(row[104+y]);
|
||||
|
||||
sp[tempid].CastingAnim=atoi(row[120]);
|
||||
sp[tempid].SpellAffectIndex=atoi(row[123]);
|
||||
sp[tempid].casting_animation=atoi(row[120]);
|
||||
sp[tempid].spell_affect_index=atoi(row[123]);
|
||||
sp[tempid].disallow_sit=atoi(row[124]);
|
||||
sp[tempid].deity_agnostic=atoi(row[125]);
|
||||
|
||||
@@ -1829,31 +1829,32 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
|
||||
sp[tempid].new_icon=atoi(row[144]);
|
||||
sp[tempid].uninterruptable=atoi(row[146]) != 0;
|
||||
sp[tempid].ResistDiff=atoi(row[147]);
|
||||
sp[tempid].dot_stacking_exempt = atoi(row[148]) != 0;
|
||||
sp[tempid].RecourseLink = atoi(row[150]);
|
||||
sp[tempid].resist_difficulty=atoi(row[147]);
|
||||
sp[tempid].unstackable_dot = atoi(row[148]) != 0;
|
||||
sp[tempid].recourse_link = atoi(row[150]);
|
||||
sp[tempid].no_partial_resist = atoi(row[151]) != 0;
|
||||
|
||||
sp[tempid].short_buff_box = atoi(row[154]);
|
||||
sp[tempid].descnum = atoi(row[155]);
|
||||
sp[tempid].typedescnum = atoi(row[156]);
|
||||
sp[tempid].effectdescnum = atoi(row[157]);
|
||||
sp[tempid].description_id = atoi(row[155]);
|
||||
sp[tempid].type_description_id = atoi(row[156]);
|
||||
sp[tempid].effect_description_id = atoi(row[157]);
|
||||
|
||||
sp[tempid].npc_no_los = atoi(row[159]) != 0;
|
||||
sp[tempid].feedbackable = atoi(row[160]) != 0;
|
||||
sp[tempid].reflectable = atoi(row[161]) != 0;
|
||||
sp[tempid].bonushate=atoi(row[162]);
|
||||
sp[tempid].bonus_hate=atoi(row[162]);
|
||||
|
||||
sp[tempid].ldon_trap = atoi(row[165]) != 0;
|
||||
sp[tempid].EndurCost=atoi(row[166]);
|
||||
sp[tempid].EndurTimerIndex=atoi(row[167]);
|
||||
sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0;
|
||||
sp[tempid].HateAdded=atoi(row[173]);
|
||||
sp[tempid].EndurUpkeep=atoi(row[174]);
|
||||
sp[tempid].numhitstype = atoi(row[175]);
|
||||
sp[tempid].numhits = atoi(row[176]);
|
||||
sp[tempid].pvpresistbase=atoi(row[177]);
|
||||
sp[tempid].pvpresistcalc=atoi(row[178]);
|
||||
sp[tempid].pvpresistcap=atoi(row[179]);
|
||||
sp[tempid].endurance_cost=atoi(row[166]);
|
||||
sp[tempid].timer_id=atoi(row[167]);
|
||||
sp[tempid].is_discipline = atoi(row[168]) != 0;
|
||||
sp[tempid].hate_added=atoi(row[173]);
|
||||
sp[tempid].endurance_upkeep=atoi(row[174]);
|
||||
sp[tempid].hit_number_type = atoi(row[175]);
|
||||
sp[tempid].hit_number = atoi(row[176]);
|
||||
sp[tempid].pvp_resist_base=atoi(row[177]);
|
||||
sp[tempid].pvp_resist_per_level=atoi(row[178]);
|
||||
sp[tempid].pvp_resist_cap=atoi(row[179]);
|
||||
sp[tempid].spell_category=atoi(row[180]);
|
||||
sp[tempid].pvp_duration = atoi(row[181]);
|
||||
sp[tempid].pvp_duration_cap = atoi(row[182]);
|
||||
@@ -1861,11 +1862,11 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
sp[tempid].cast_not_standing = atoi(row[184]) != 0;
|
||||
sp[tempid].can_mgb=atoi(row[185]);
|
||||
sp[tempid].dispel_flag = atoi(row[186]);
|
||||
sp[tempid].MinResist = atoi(row[189]);
|
||||
sp[tempid].MaxResist = atoi(row[190]);
|
||||
sp[tempid].min_resist = atoi(row[189]);
|
||||
sp[tempid].max_resist = atoi(row[190]);
|
||||
sp[tempid].viral_targets = atoi(row[191]);
|
||||
sp[tempid].viral_timer = atoi(row[192]);
|
||||
sp[tempid].NimbusEffect = atoi(row[193]);
|
||||
sp[tempid].nimbus_effect = atoi(row[193]);
|
||||
sp[tempid].directional_start = static_cast<float>(atoi(row[194]));
|
||||
sp[tempid].directional_end = static_cast<float>(atoi(row[195]));
|
||||
sp[tempid].sneak = atoi(row[196]) != 0;
|
||||
@@ -1873,29 +1874,29 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
sp[tempid].no_detrimental_spell_aggro = atoi(row[198]) != 0;
|
||||
sp[tempid].suspendable = atoi(row[200]) != 0;
|
||||
sp[tempid].viral_range = atoi(row[201]);
|
||||
sp[tempid].songcap = atoi(row[202]);
|
||||
sp[tempid].song_cap = atoi(row[202]);
|
||||
sp[tempid].no_block = atoi(row[205]);
|
||||
sp[tempid].spellgroup=atoi(row[207]);
|
||||
sp[tempid].spell_group=atoi(row[207]);
|
||||
sp[tempid].rank = atoi(row[208]);
|
||||
sp[tempid].no_resist=atoi(row[209]);
|
||||
sp[tempid].CastRestriction = atoi(row[211]);
|
||||
sp[tempid].AllowRest = atoi(row[212]) != 0;
|
||||
sp[tempid].InCombat = atoi(row[213]) != 0;
|
||||
sp[tempid].OutofCombat = atoi(row[214]) != 0;
|
||||
sp[tempid].cast_restriction = atoi(row[211]);
|
||||
sp[tempid].allow_rest = atoi(row[212]) != 0;
|
||||
sp[tempid].can_cast_in_combat = atoi(row[213]) != 0;
|
||||
sp[tempid].can_cast_out_of_combat = atoi(row[214]) != 0;
|
||||
sp[tempid].override_crit_chance = atoi(row[217]);
|
||||
sp[tempid].aemaxtargets = atoi(row[218]);
|
||||
sp[tempid].aoe_max_targets = atoi(row[218]);
|
||||
sp[tempid].no_heal_damage_item_mod = atoi(row[219]);
|
||||
sp[tempid].caster_requirement_id = atoi(row[220]);
|
||||
sp[tempid].spell_class = atoi(row[221]);
|
||||
sp[tempid].spell_subclass = atoi(row[222]);
|
||||
sp[tempid].persistdeath = atoi(row[224]) != 0;
|
||||
sp[tempid].min_dist = atof(row[227]);
|
||||
sp[tempid].min_dist_mod = atof(row[228]);
|
||||
sp[tempid].max_dist = atof(row[229]);
|
||||
sp[tempid].max_dist_mod = atof(row[230]);
|
||||
sp[tempid].persist_death = atoi(row[224]) != 0;
|
||||
sp[tempid].min_distance = atof(row[227]);
|
||||
sp[tempid].min_distance_mod = atof(row[228]);
|
||||
sp[tempid].max_distance = atof(row[229]);
|
||||
sp[tempid].max_distance_mod = atof(row[230]);
|
||||
sp[tempid].min_range = static_cast<float>(atoi(row[231]));
|
||||
sp[tempid].no_remove = atoi(row[232]) != 0;
|
||||
sp[tempid].DamageShieldType = 0;
|
||||
sp[tempid].damage_shield_type = 0;
|
||||
}
|
||||
|
||||
LoadDamageShieldTypes(sp, max_spells);
|
||||
|
||||
+10
-96
@@ -177,8 +177,7 @@ bool EQ::skills::IsMeleeDmg(SkillType skill)
|
||||
|
||||
const std::map<EQ::skills::SkillType, std::string>& EQ::skills::GetSkillTypeMap()
|
||||
{
|
||||
/* VS2013 code
|
||||
static const std::map<SkillUseTypes, std::string> skill_use_types_map = {
|
||||
static const std::map<SkillType, std::string> skill_type_map = {
|
||||
{ Skill1HBlunt, "1H Blunt" },
|
||||
{ Skill1HSlashing, "1H Slashing" },
|
||||
{ Skill2HBlunt, "2H Blunt" },
|
||||
@@ -258,103 +257,18 @@ const std::map<EQ::skills::SkillType, std::string>& EQ::skills::GetSkillTypeMap(
|
||||
{ SkillTripleAttack, "Triple Attack" },
|
||||
{ Skill2HPiercing, "2H Piercing" }
|
||||
};
|
||||
*/
|
||||
|
||||
/* VS2012 code - begin */
|
||||
|
||||
static const char* skill_use_names[SkillCount] = {
|
||||
"1H Blunt",
|
||||
"1H Slashing",
|
||||
"2H Blunt",
|
||||
"2H Slashing",
|
||||
"Abjuration",
|
||||
"Alteration",
|
||||
"Apply Poison",
|
||||
"Archery",
|
||||
"Backstab",
|
||||
"Bind Wound",
|
||||
"Bash",
|
||||
"Block",
|
||||
"Brass Instruments",
|
||||
"Channeling",
|
||||
"Conjuration",
|
||||
"Defense",
|
||||
"Disarm",
|
||||
"Disarm Traps",
|
||||
"Divination",
|
||||
"Dodge",
|
||||
"Double Attack",
|
||||
"Dragon Punch",
|
||||
"Dual Wield",
|
||||
"Eagle Strike",
|
||||
"Evocation",
|
||||
"Feign Death",
|
||||
"Flying Kick",
|
||||
"Forage",
|
||||
"Hand to Hand",
|
||||
"Hide",
|
||||
"Kick",
|
||||
"Meditate",
|
||||
"Mend",
|
||||
"Offense",
|
||||
"Parry",
|
||||
"Pick Lock",
|
||||
"1H Piercing",
|
||||
"Riposte",
|
||||
"Round Kick",
|
||||
"Safe Fall",
|
||||
"Sense Heading",
|
||||
"Singing",
|
||||
"Sneak",
|
||||
"Specialize Abjuration",
|
||||
"Specialize Alteration",
|
||||
"Specialize Conjuration",
|
||||
"Specialize Divination",
|
||||
"Specialize Evocation",
|
||||
"Pick Pockets",
|
||||
"Stringed Instruments",
|
||||
"Swimming",
|
||||
"Throwing",
|
||||
"Tiger Claw",
|
||||
"Tracking",
|
||||
"Wind Instruments",
|
||||
"Fishing",
|
||||
"Make Poison",
|
||||
"Tinkering",
|
||||
"Research",
|
||||
"Alchemy",
|
||||
"Baking",
|
||||
"Tailoring",
|
||||
"Sense Traps",
|
||||
"Blacksmithing",
|
||||
"Fletching",
|
||||
"Brewing",
|
||||
"Alcohol Tolerance",
|
||||
"Begging",
|
||||
"Jewelry Making",
|
||||
"Pottery",
|
||||
"Percussion Instruments",
|
||||
"Intimidation",
|
||||
"Berserking",
|
||||
"Taunt",
|
||||
"Frenzy",
|
||||
"Remove Traps",
|
||||
"Triple Attack",
|
||||
"2H Piercing"
|
||||
};
|
||||
|
||||
static std::map<SkillType, std::string> skill_type_map;
|
||||
|
||||
skill_type_map.clear();
|
||||
|
||||
for (int i = Skill1HBlunt; i < SkillCount; ++i)
|
||||
skill_type_map[(SkillType)i] = skill_use_names[i];
|
||||
|
||||
/* VS2012 code - end */
|
||||
|
||||
return skill_type_map;
|
||||
}
|
||||
|
||||
std::string EQ::skills::GetSkillName(SkillType skill)
|
||||
{
|
||||
if (skill >= Skill1HBlunt && skill <= Skill2HPiercing) {
|
||||
auto skills = GetSkillTypeMap();
|
||||
return skills[skill];
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
EQ::SkillProfile::SkillProfile()
|
||||
{
|
||||
memset(&Skill, 0, (sizeof(uint32) * PACKET_SKILL_ARRAY_SIZE));
|
||||
|
||||
@@ -171,6 +171,7 @@ namespace EQ
|
||||
|
||||
extern const std::map<SkillType, std::string>& GetSkillTypeMap();
|
||||
|
||||
std::string GetSkillName(SkillType skill);
|
||||
} /*skills*/
|
||||
|
||||
struct SkillProfile { // prototype - not implemented
|
||||
|
||||
+326
-163
@@ -87,7 +87,7 @@
|
||||
|
||||
bool IsTargetableAESpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) {
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].target_type == ST_AETarget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -103,8 +103,8 @@ bool IsLifetapSpell(uint16 spell_id)
|
||||
{
|
||||
// Ancient Lifebane: 2115
|
||||
if (IsValidSpell(spell_id) &&
|
||||
(spells[spell_id].targettype == ST_Tap ||
|
||||
spells[spell_id].targettype == ST_TargetAETap ||
|
||||
(spells[spell_id].target_type == ST_Tap ||
|
||||
spells[spell_id].target_type == ST_TargetAETap ||
|
||||
spell_id == 2115))
|
||||
return true;
|
||||
|
||||
@@ -124,7 +124,7 @@ bool IsStunSpell(uint16 spell_id)
|
||||
bool IsSummonSpell(uint16 spellid)
|
||||
{
|
||||
for (int o = 0; o < EFFECT_COUNT; o++) {
|
||||
uint32 tid = spells[spellid].effectid[o];
|
||||
uint32 tid = spells[spellid].effect_id[o];
|
||||
if (tid == SE_SummonPet || tid == SE_SummonItem || tid == SE_SummonPC)
|
||||
return true;
|
||||
}
|
||||
@@ -140,10 +140,10 @@ bool IsEvacSpell(uint16 spellid)
|
||||
bool IsDamageSpell(uint16 spellid)
|
||||
{
|
||||
for (int o = 0; o < EFFECT_COUNT; o++) {
|
||||
uint32 tid = spells[spellid].effectid[o];
|
||||
uint32 tid = spells[spellid].effect_id[o];
|
||||
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) &&
|
||||
spells[spellid].targettype != ST_Tap && spells[spellid].buffduration < 1 &&
|
||||
spells[spellid].base[o] < 0)
|
||||
spells[spellid].target_type != ST_Tap && spells[spellid].buff_duration < 1 &&
|
||||
spells[spellid].base_value[o] < 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -163,8 +163,8 @@ bool IsCureSpell(uint16 spell_id)
|
||||
bool CureEffect = false;
|
||||
|
||||
for(int i = 0; i < EFFECT_COUNT; i++){
|
||||
if (sp.effectid[i] == SE_DiseaseCounter || sp.effectid[i] == SE_PoisonCounter
|
||||
|| sp.effectid[i] == SE_CurseCounter || sp.effectid[i] == SE_CorruptionCounter)
|
||||
if (sp.effect_id[i] == SE_DiseaseCounter || sp.effect_id[i] == SE_PoisonCounter
|
||||
|| sp.effect_id[i] == SE_CurseCounter || sp.effect_id[i] == SE_CorruptionCounter)
|
||||
CureEffect = true;
|
||||
}
|
||||
|
||||
@@ -179,8 +179,8 @@ bool IsSlowSpell(uint16 spell_id)
|
||||
const SPDat_Spell_Struct &sp = spells[spell_id];
|
||||
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
if ((sp.effectid[i] == SE_AttackSpeed && sp.base[i] < 100) ||
|
||||
(sp.effectid[i] == SE_AttackSpeed4))
|
||||
if ((sp.effect_id[i] == SE_AttackSpeed && sp.base_value[i] < 100) ||
|
||||
(sp.effect_id[i] == SE_AttackSpeed4))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -191,8 +191,8 @@ bool IsHasteSpell(uint16 spell_id)
|
||||
const SPDat_Spell_Struct &sp = spells[spell_id];
|
||||
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
if(sp.effectid[i] == SE_AttackSpeed)
|
||||
return (sp.base[i] < 100);
|
||||
if(sp.effect_id[i] == SE_AttackSpeed)
|
||||
return (sp.base_value[i] < 100);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ bool IsPercentalHealSpell(uint16 spell_id)
|
||||
|
||||
bool IsGroupOnlySpell(uint16 spell_id)
|
||||
{
|
||||
return IsValidSpell(spell_id) && spells[spell_id].goodEffect == 2;
|
||||
return IsValidSpell(spell_id) && spells[spell_id].good_effect == 2;
|
||||
}
|
||||
|
||||
bool IsBeneficialSpell(uint16 spell_id)
|
||||
@@ -219,10 +219,10 @@ bool IsBeneficialSpell(uint16 spell_id)
|
||||
return false;
|
||||
|
||||
// You'd think just checking goodEffect flag would be enough?
|
||||
if (spells[spell_id].goodEffect == 1) {
|
||||
if (spells[spell_id].good_effect == 1) {
|
||||
// If the target type is ST_Self or ST_Pet and is a SE_CancleMagic spell
|
||||
// it is not Beneficial
|
||||
SpellTargetType tt = spells[spell_id].targettype;
|
||||
SpellTargetType tt = spells[spell_id].target_type;
|
||||
if (tt != ST_Self && tt != ST_Pet &&
|
||||
IsEffectInSpell(spell_id, SE_CancelMagic))
|
||||
return false;
|
||||
@@ -231,14 +231,14 @@ bool IsBeneficialSpell(uint16 spell_id)
|
||||
// We need to check more things!
|
||||
if (tt == ST_Target || tt == ST_AETarget || tt == ST_Animal ||
|
||||
tt == ST_Undead || tt == ST_Pet) {
|
||||
uint16 sai = spells[spell_id].SpellAffectIndex;
|
||||
uint16 sai = spells[spell_id].spell_affect_index;
|
||||
|
||||
// If the resisttype is magic and SpellAffectIndex is Calm/memblur/dispell sight
|
||||
// it's not beneficial
|
||||
if (spells[spell_id].resisttype == RESIST_MAGIC) {
|
||||
if (spells[spell_id].resist_type == RESIST_MAGIC) {
|
||||
// checking these SAI cause issues with the rng defensive proc line
|
||||
// So I guess instead of fixing it for real, just a quick hack :P
|
||||
if (spells[spell_id].effectid[0] != SE_DefensiveProc &&
|
||||
if (spells[spell_id].effect_id[0] != SE_DefensiveProc &&
|
||||
(sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur ||
|
||||
sai == SAI_Calm_Song))
|
||||
return false;
|
||||
@@ -252,7 +252,7 @@ bool IsBeneficialSpell(uint16 spell_id)
|
||||
}
|
||||
|
||||
// And finally, if goodEffect is not 0 or if it's a group spell it's beneficial
|
||||
return spells[spell_id].goodEffect != 0 || IsGroupSpell(spell_id);
|
||||
return spells[spell_id].good_effect != 0 || IsGroupSpell(spell_id);
|
||||
}
|
||||
|
||||
bool IsDetrimentalSpell(uint16 spell_id)
|
||||
@@ -364,8 +364,8 @@ bool IsImprovedDamageSpell(uint16 spell_id)
|
||||
bool IsAEDurationSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) &&
|
||||
(spells[spell_id].targettype == ST_AETarget || spells[spell_id].targettype == ST_UndeadAE) &&
|
||||
spells[spell_id].AEDuration != 0)
|
||||
(spells[spell_id].target_type == ST_AETarget || spells[spell_id].target_type == ST_UndeadAE) &&
|
||||
spells[spell_id].aoe_duration != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -383,7 +383,7 @@ bool IsPureNukeSpell(uint16 spell_id)
|
||||
effect_count++;
|
||||
|
||||
if (effect_count == 1 && IsEffectInSpell(spell_id, SE_CurrentHP) &&
|
||||
spells[spell_id].buffduration == 0 && IsDamageSpell(spell_id))
|
||||
spells[spell_id].buff_duration == 0 && IsDamageSpell(spell_id))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -392,7 +392,7 @@ bool IsPureNukeSpell(uint16 spell_id)
|
||||
bool IsAENukeSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
|
||||
spells[spell_id].aoerange > 0)
|
||||
spells[spell_id].aoe_range > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -401,7 +401,7 @@ bool IsAENukeSpell(uint16 spell_id)
|
||||
bool IsPBAENukeSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
|
||||
spells[spell_id].aoerange > 0 && spells[spell_id].targettype == ST_AECaster)
|
||||
spells[spell_id].aoe_range > 0 && spells[spell_id].target_type == ST_AECaster)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -410,7 +410,7 @@ bool IsPBAENukeSpell(uint16 spell_id)
|
||||
bool IsAERainNukeSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && IsPureNukeSpell(spell_id) &&
|
||||
spells[spell_id].aoerange > 0 && spells[spell_id].AEDuration > 1000)
|
||||
spells[spell_id].aoe_range > 0 && spells[spell_id].aoe_duration > 1000)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -424,12 +424,12 @@ bool IsPartialCapableSpell(uint16 spell_id)
|
||||
// spell uses 600 (partial) scale if first effect is damage, else it uses 200 scale.
|
||||
// this includes DoTs. no_partial_resist excludes spells like necro snares
|
||||
for (int o = 0; o < EFFECT_COUNT; o++) {
|
||||
auto tid = spells[spell_id].effectid[o];
|
||||
auto tid = spells[spell_id].effect_id[o];
|
||||
|
||||
if (IsBlankSpellEffect(spell_id, o))
|
||||
continue;
|
||||
|
||||
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base[o] < 0)
|
||||
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base_value[o] < 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -452,9 +452,9 @@ bool IsResistableSpell(uint16 spell_id)
|
||||
bool IsGroupSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) &&
|
||||
(spells[spell_id].targettype == ST_AEBard ||
|
||||
spells[spell_id].targettype == ST_Group ||
|
||||
spells[spell_id].targettype == ST_GroupTeleport))
|
||||
(spells[spell_id].target_type == ST_AEBard ||
|
||||
spells[spell_id].target_type == ST_Group ||
|
||||
spells[spell_id].target_type == ST_GroupTeleport))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -464,7 +464,7 @@ bool IsGroupSpell(uint16 spell_id)
|
||||
bool IsTGBCompatibleSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) &&
|
||||
(!IsDetrimentalSpell(spell_id) && spells[spell_id].buffduration != 0 &&
|
||||
(!IsDetrimentalSpell(spell_id) && spells[spell_id].buff_duration != 0 &&
|
||||
!IsBardSong(spell_id) && !IsEffectInSpell(spell_id, SE_Illusion)))
|
||||
return true;
|
||||
|
||||
@@ -473,7 +473,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id)
|
||||
|
||||
bool IsBardSong(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].IsDisciplineBuff)
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].is_discipline)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -487,7 +487,7 @@ bool IsEffectInSpell(uint16 spellid, int effect)
|
||||
return false;
|
||||
|
||||
for (j = 0; j < EFFECT_COUNT; j++)
|
||||
if (spells[spellid].effectid[j] == effect)
|
||||
if (spells[spellid].effect_id[j] == effect)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -498,15 +498,15 @@ bool IsEffectInSpell(uint16 spellid, int effect)
|
||||
// the blanks
|
||||
bool IsBlankSpellEffect(uint16 spellid, int effect_index)
|
||||
{
|
||||
int effect, base, formula;
|
||||
int effect, base_value, formula;
|
||||
|
||||
effect = spells[spellid].effectid[effect_index];
|
||||
base = spells[spellid].base[effect_index];
|
||||
effect = spells[spellid].effect_id[effect_index];
|
||||
base_value = spells[spellid].base_value[effect_index];
|
||||
formula = spells[spellid].formula[effect_index];
|
||||
|
||||
// SE_CHA is "spacer"
|
||||
// SE_Stacking* are also considered blank where this is used
|
||||
if (effect == SE_Blank || (effect == SE_CHA && base == 0 && formula == 100) ||
|
||||
if (effect == SE_Blank || (effect == SE_CHA && base_value == 0 && formula == 100) ||
|
||||
effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite)
|
||||
return true;
|
||||
|
||||
@@ -561,7 +561,7 @@ int GetSpellEffectIndex(uint16 spell_id, int effect)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == effect)
|
||||
if (spells[spell_id].effect_id[i] == effect)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
@@ -591,7 +591,7 @@ bool BeneficialSpell(uint16 spell_id)
|
||||
/*|| spells[spell_id].stacking == 27*/ )
|
||||
return true;
|
||||
|
||||
switch (spells[spell_id].goodEffect) {
|
||||
switch (spells[spell_id].good_effect) {
|
||||
case 1:
|
||||
case 3:
|
||||
return true;
|
||||
@@ -602,7 +602,7 @@ bool BeneficialSpell(uint16 spell_id)
|
||||
|
||||
bool GroupOnlySpell(uint16 spell_id)
|
||||
{
|
||||
switch (spells[spell_id].goodEffect) {
|
||||
switch (spells[spell_id].good_effect) {
|
||||
case 2:
|
||||
case 3:
|
||||
return true;
|
||||
@@ -623,9 +623,9 @@ int32 CalculatePoisonCounters(uint16 spell_id)
|
||||
|
||||
int32 Counters = 0;
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_PoisonCounter &&
|
||||
spells[spell_id].base[i] > 0)
|
||||
Counters += spells[spell_id].base[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_PoisonCounter &&
|
||||
spells[spell_id].base_value[i] > 0)
|
||||
Counters += spells[spell_id].base_value[i];
|
||||
|
||||
return Counters;
|
||||
}
|
||||
@@ -637,9 +637,9 @@ int32 CalculateDiseaseCounters(uint16 spell_id)
|
||||
|
||||
int32 Counters = 0;
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if(spells[spell_id].effectid[i] == SE_DiseaseCounter &&
|
||||
spells[spell_id].base[i] > 0)
|
||||
Counters += spells[spell_id].base[i];
|
||||
if(spells[spell_id].effect_id[i] == SE_DiseaseCounter &&
|
||||
spells[spell_id].base_value[i] > 0)
|
||||
Counters += spells[spell_id].base_value[i];
|
||||
|
||||
return Counters;
|
||||
}
|
||||
@@ -651,9 +651,9 @@ int32 CalculateCurseCounters(uint16 spell_id)
|
||||
|
||||
int32 Counters = 0;
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if(spells[spell_id].effectid[i] == SE_CurseCounter &&
|
||||
spells[spell_id].base[i] > 0)
|
||||
Counters += spells[spell_id].base[i];
|
||||
if(spells[spell_id].effect_id[i] == SE_CurseCounter &&
|
||||
spells[spell_id].base_value[i] > 0)
|
||||
Counters += spells[spell_id].base_value[i];
|
||||
|
||||
return Counters;
|
||||
}
|
||||
@@ -665,9 +665,9 @@ int32 CalculateCorruptionCounters(uint16 spell_id)
|
||||
|
||||
int32 Counters = 0;
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_CorruptionCounter &&
|
||||
spells[spell_id].base[i] > 0)
|
||||
Counters += spells[spell_id].base[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_CorruptionCounter &&
|
||||
spells[spell_id].base_value[i] > 0)
|
||||
Counters += spells[spell_id].base_value[i];
|
||||
|
||||
return Counters;
|
||||
}
|
||||
@@ -695,7 +695,7 @@ bool IsDisciplineBuff(uint16 spell_id)
|
||||
if (!IsValidSpell(spell_id))
|
||||
return false;
|
||||
|
||||
if (spells[spell_id].IsDisciplineBuff && spells[spell_id].targettype == ST_Self)
|
||||
if (spells[spell_id].is_discipline && spells[spell_id].target_type == ST_Self)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -707,7 +707,7 @@ bool IsDiscipline(uint16 spell_id)
|
||||
return false;
|
||||
|
||||
if (spells[spell_id].mana == 0 &&
|
||||
(spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep))
|
||||
(spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -719,7 +719,7 @@ bool IsCombatSkill(uint16 spell_id)
|
||||
return false;
|
||||
|
||||
//Check if Discipline
|
||||
if ((spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)))
|
||||
if ((spells[spell_id].mana == 0 && (spells[spell_id].endurance_cost || spells[spell_id].endurance_upkeep)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -738,7 +738,7 @@ bool IsRuneSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id))
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_Rune)
|
||||
if (spells[spell_id].effect_id[i] == SE_Rune)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -748,7 +748,7 @@ bool IsMagicRuneSpell(uint16 spell_id)
|
||||
{
|
||||
if(IsValidSpell(spell_id))
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
if(spells[spell_id].effectid[i] == SE_AbsorbMagicAtt)
|
||||
if(spells[spell_id].effect_id[i] == SE_AbsorbMagicAtt)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -758,8 +758,8 @@ bool IsManaTapSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id))
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_CurrentMana &&
|
||||
spells[spell_id].targettype == ST_Tap)
|
||||
if (spells[spell_id].effect_id[i] == SE_CurrentMana &&
|
||||
spells[spell_id].target_type == ST_Tap)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -788,8 +788,8 @@ bool IsPartialDeathSaveSpell(uint16 spell_id)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_DeathSave &&
|
||||
spells[spell_id].base[i] == 1)
|
||||
if (spells[spell_id].effect_id[i] == SE_DeathSave &&
|
||||
spells[spell_id].base_value[i] == 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -802,8 +802,8 @@ bool IsFullDeathSaveSpell(uint16 spell_id)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++)
|
||||
if (spells[spell_id].effectid[i] == SE_DeathSave &&
|
||||
spells[spell_id].base[i] == 2)
|
||||
if (spells[spell_id].effect_id[i] == SE_DeathSave &&
|
||||
spells[spell_id].base_value[i] == 2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -833,6 +833,14 @@ bool IsTeleportSpell(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsTranslocateSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsEffectInSpell(spell_id, SE_Translocate))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsGateSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsEffectInSpell(spell_id, SE_Gate))
|
||||
@@ -844,7 +852,7 @@ bool IsGateSpell(uint16 spell_id)
|
||||
bool IsPlayerIllusionSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsEffectInSpell(spell_id, SE_Illusion) &&
|
||||
spells[spell_id].targettype == ST_Self)
|
||||
spells[spell_id].target_type == ST_Self)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -853,7 +861,7 @@ bool IsPlayerIllusionSpell(uint16 spell_id)
|
||||
int GetSpellEffectDescNum(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id))
|
||||
return spells[spell_id].effectdescnum;
|
||||
return spells[spell_id].effect_description_id;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -864,12 +872,12 @@ DmgShieldType GetDamageShieldType(uint16 spell_id, int32 DSType)
|
||||
// else, make a guess, based on the resist type. Default return value is DS_THORNS
|
||||
if (IsValidSpell(spell_id)) {
|
||||
LogSpells("DamageShieldType for spell [{}] ([{}]) is [{}]", spell_id,
|
||||
spells[spell_id].name, spells[spell_id].DamageShieldType);
|
||||
spells[spell_id].name, spells[spell_id].damage_shield_type);
|
||||
|
||||
if (spells[spell_id].DamageShieldType)
|
||||
return (DmgShieldType) spells[spell_id].DamageShieldType;
|
||||
if (spells[spell_id].damage_shield_type)
|
||||
return (DmgShieldType) spells[spell_id].damage_shield_type;
|
||||
|
||||
switch (spells[spell_id].resisttype) {
|
||||
switch (spells[spell_id].resist_type) {
|
||||
case RESIST_COLD:
|
||||
return DS_TORMENT;
|
||||
case RESIST_FIRE:
|
||||
@@ -899,12 +907,12 @@ bool IsLDoNObjectSpell(uint16 spell_id)
|
||||
|
||||
int32 GetSpellResistType(uint16 spell_id)
|
||||
{
|
||||
return spells[spell_id].resisttype;
|
||||
return spells[spell_id].resist_type;
|
||||
}
|
||||
|
||||
int32 GetSpellTargetType(uint16 spell_id)
|
||||
{
|
||||
return (int32)spells[spell_id].targettype;
|
||||
return (int32)spells[spell_id].target_type;
|
||||
}
|
||||
|
||||
bool IsHealOverTimeSpell(uint16 spell_id)
|
||||
@@ -929,7 +937,7 @@ bool IsFastHealSpell(uint16 spell_id)
|
||||
const int MaxFastHealCastingTime = 2000;
|
||||
|
||||
if (spells[spell_id].cast_time <= MaxFastHealCastingTime &&
|
||||
spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
|
||||
spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
|
||||
!IsGroupSpell(spell_id))
|
||||
return true;
|
||||
|
||||
@@ -941,7 +949,7 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
||||
const int MaxFastHealCastingTime = 1000;
|
||||
|
||||
if (spells[spell_id].cast_time <= MaxFastHealCastingTime &&
|
||||
spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
|
||||
spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
|
||||
!IsGroupSpell(spell_id))
|
||||
return true;
|
||||
|
||||
@@ -950,8 +958,8 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
||||
|
||||
bool IsRegularSingleTargetHealSpell(uint16 spell_id)
|
||||
{
|
||||
if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 &&
|
||||
spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 &&
|
||||
if(spells[spell_id].effect_id[0] == 0 && spells[spell_id].base_value[0] > 0 &&
|
||||
spells[spell_id].target_type == ST_Target && spells[spell_id].buff_duration == 0 &&
|
||||
!IsCompleteHealSpell(spell_id) &&
|
||||
!IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id))
|
||||
return true;
|
||||
@@ -977,7 +985,7 @@ bool IsGroupCompleteHealSpell(uint16 spell_id)
|
||||
|
||||
bool IsGroupHealOverTimeSpell(uint16 spell_id)
|
||||
{
|
||||
if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buffduration < 10)
|
||||
if( IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buff_duration < 10)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1008,8 +1016,8 @@ bool IsResistDebuffSpell(uint16 spell_id)
|
||||
bool IsSelfConversionSpell(uint16 spell_id)
|
||||
{
|
||||
if (GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) &&
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
|
||||
spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0)
|
||||
IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 &&
|
||||
spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
@@ -1018,7 +1026,7 @@ bool IsSelfConversionSpell(uint16 spell_id)
|
||||
// returns true for both detrimental and beneficial buffs
|
||||
bool IsBuffSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && (spells[spell_id].buffduration || spells[spell_id].buffdurationformula))
|
||||
if (IsValidSpell(spell_id) && (spells[spell_id].buff_duration || spells[spell_id].buff_duration_formula))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1026,7 +1034,7 @@ bool IsBuffSpell(uint16 spell_id)
|
||||
|
||||
bool IsPersistDeathSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].persistdeath)
|
||||
if (IsValidSpell(spell_id) && spells[spell_id].persist_death)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1043,8 +1051,8 @@ bool IsSuspendableSpell(uint16 spell_id)
|
||||
uint32 GetMorphTrigger(uint32 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect)
|
||||
return spells[spell_id].base[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect)
|
||||
return spells[spell_id].base_value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1052,9 +1060,9 @@ uint32 GetMorphTrigger(uint32 spell_id)
|
||||
bool IsCastonFadeDurationSpell(uint16 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
if (spells[spell_id].effectid[i] == SE_CastOnFadeEffect
|
||||
|| spells[spell_id].effectid[i] == SE_CastOnFadeEffectNPC
|
||||
|| spells[spell_id].effectid[i] == SE_CastOnFadeEffectAlways){
|
||||
if (spells[spell_id].effect_id[i] == SE_CastOnFadeEffect
|
||||
|| spells[spell_id].effect_id[i] == SE_CastOnFadeEffectNPC
|
||||
|| spells[spell_id].effect_id[i] == SE_CastOnFadeEffectAlways){
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1065,7 +1073,7 @@ bool IsCastonFadeDurationSpell(uint16 spell_id)
|
||||
bool IsPowerDistModSpell(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id) &&
|
||||
(spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist)
|
||||
(spells[spell_id].max_distance_mod || spells[spell_id].min_distance_mod) && spells[spell_id].max_distance > spells[spell_id].min_distance)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1074,8 +1082,8 @@ bool IsPowerDistModSpell(uint16 spell_id)
|
||||
uint32 GetPartialMeleeRuneReduction(uint32 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage)
|
||||
return spells[spell_id].base[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage)
|
||||
return spells[spell_id].base_value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1083,8 +1091,8 @@ uint32 GetPartialMeleeRuneReduction(uint32 spell_id)
|
||||
uint32 GetPartialMagicRuneReduction(uint32 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage)
|
||||
return spells[spell_id].base[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage)
|
||||
return spells[spell_id].base_value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1092,8 +1100,8 @@ uint32 GetPartialMagicRuneReduction(uint32 spell_id)
|
||||
uint32 GetPartialMeleeRuneAmount(uint32 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_MitigateMeleeDamage)
|
||||
return spells[spell_id].max[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_MitigateMeleeDamage)
|
||||
return spells[spell_id].max_value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1101,8 +1109,8 @@ uint32 GetPartialMeleeRuneAmount(uint32 spell_id)
|
||||
uint32 GetPartialMagicRuneAmount(uint32 spell_id)
|
||||
{
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_MitigateSpellDamage)
|
||||
return spells[spell_id].max[i];
|
||||
if (spells[spell_id].effect_id[i] == SE_MitigateSpellDamage)
|
||||
return spells[spell_id].max_value[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1111,7 +1119,7 @@ uint32 GetPartialMagicRuneAmount(uint32 spell_id)
|
||||
bool DetrimentalSpellAllowsRest(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id))
|
||||
return spells[spell_id].AllowRest;
|
||||
return spells[spell_id].allow_rest;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -1130,7 +1138,7 @@ bool IsStackableDot(uint16 spell_id)
|
||||
if (!IsValidSpell(spell_id))
|
||||
return false;
|
||||
const auto &spell = spells[spell_id];
|
||||
if (spell.dot_stacking_exempt || spell.goodEffect || !spell.buffdurationformula)
|
||||
if (spell.unstackable_dot || spell.good_effect || !spell.buff_duration_formula)
|
||||
return false;
|
||||
return IsEffectInSpell(spell_id, SE_CurrentHP) || IsEffectInSpell(spell_id, SE_GravityEffect);
|
||||
}
|
||||
@@ -1222,7 +1230,7 @@ bool IsEffectIgnoredInStacking(int spa)
|
||||
case SE_LimitClass:
|
||||
case SE_LimitRace:
|
||||
case SE_FcBaseEffects:
|
||||
case 415:
|
||||
case SE_FFItemClass:
|
||||
case SE_SkillDamageAmount2:
|
||||
case SE_FcLimitUse:
|
||||
case SE_FcIncreaseNumHits:
|
||||
@@ -1247,6 +1255,7 @@ bool IsEffectIgnoredInStacking(int spa)
|
||||
case SE_Ff_ReuseTimeMax:
|
||||
case SE_Ff_Value_Min:
|
||||
case SE_Ff_Value_Max:
|
||||
case SE_Ff_FocusTimerMin:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -1288,6 +1297,8 @@ bool IsFocusLimit(int spa)
|
||||
case SE_Ff_ReuseTimeMax:
|
||||
case SE_Ff_Value_Min:
|
||||
case SE_Ff_Value_Max:
|
||||
case SE_Ff_FocusTimerMin:
|
||||
case SE_FFItemClass:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -1297,7 +1308,7 @@ bool IsFocusLimit(int spa)
|
||||
uint32 GetNimbusEffect(uint16 spell_id)
|
||||
{
|
||||
if (IsValidSpell(spell_id))
|
||||
return (int32)spells[spell_id].NimbusEffect;
|
||||
return (int32)spells[spell_id].nimbus_effect;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1311,9 +1322,9 @@ int32 GetFuriousBash(uint16 spell_id)
|
||||
int32 mod = 0;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i)
|
||||
if (spells[spell_id].effectid[i] == SE_SpellHateMod)
|
||||
mod = spells[spell_id].base[i];
|
||||
else if (spells[spell_id].effectid[i] == SE_LimitEffect && spells[spell_id].base[i] == 999)
|
||||
if (spells[spell_id].effect_id[i] == SE_SpellHateMod)
|
||||
mod = spells[spell_id].base_value[i];
|
||||
else if (spells[spell_id].effect_id[i] == SE_LimitEffect && spells[spell_id].base_value[i] == 999)
|
||||
found_effect_limit = true;
|
||||
|
||||
if (found_effect_limit)
|
||||
@@ -1334,8 +1345,8 @@ bool IsSpellUsableThisZoneType(uint16 spell_id, uint8 zone_type)
|
||||
{
|
||||
//check if spell can be cast in any zone (-1 or 255), then if spell zonetype matches zone's zonetype
|
||||
// || spells[spell_id].zonetype == 255 comparing signed 8 bit int to 255 is always false
|
||||
if (IsValidSpell(spell_id) && (spells[spell_id].zonetype == -1 ||
|
||||
spells[spell_id].zonetype == zone_type))
|
||||
if (IsValidSpell(spell_id) && (spells[spell_id].zone_type == -1 ||
|
||||
spells[spell_id].zone_type == zone_type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -1348,16 +1359,109 @@ const char* GetSpellName(uint16 spell_id)
|
||||
|
||||
bool SpellRequiresTarget(int spell_id)
|
||||
{
|
||||
if (spells[spell_id].targettype == ST_AEClientV1 ||
|
||||
spells[spell_id].targettype == ST_Self ||
|
||||
spells[spell_id].targettype == ST_AECaster ||
|
||||
spells[spell_id].targettype == ST_Ring ||
|
||||
spells[spell_id].targettype == ST_Beam) {
|
||||
if (spells[spell_id].target_type == ST_AEClientV1 ||
|
||||
spells[spell_id].target_type == ST_Self ||
|
||||
spells[spell_id].target_type == ST_AECaster ||
|
||||
spells[spell_id].target_type == ST_Ring ||
|
||||
spells[spell_id].target_type == ST_Beam) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect)
|
||||
{
|
||||
|
||||
//Effects that are verified modifiable by bard instrument/singing mods, or highly likely due to similiar type of effect.
|
||||
switch (effect) {
|
||||
|
||||
//Only modify instant endurance or mana effects (Ie. Mana drain, Crescendo line)
|
||||
case SE_CurrentEndurance:
|
||||
case SE_CurrentMana: {
|
||||
if (spells[spell_id].buff_duration == 0) {
|
||||
return true;
|
||||
}
|
||||
//Mana regen is not modified.
|
||||
return false;
|
||||
}
|
||||
|
||||
case SE_CurrentHP:
|
||||
case SE_ArmorClass:
|
||||
case SE_ACv2:
|
||||
case SE_MovementSpeed:
|
||||
case SE_ATK:
|
||||
case SE_STR:
|
||||
case SE_DEX:
|
||||
case SE_AGI:
|
||||
case SE_STA:
|
||||
case SE_INT:
|
||||
case SE_WIS:
|
||||
case SE_CHA:
|
||||
case SE_AllStats:
|
||||
case SE_ResistFire:
|
||||
case SE_ResistCold:
|
||||
case SE_ResistPoison:
|
||||
case SE_ResistDisease:
|
||||
case SE_ResistMagic:
|
||||
case SE_ResistAll:
|
||||
case SE_ResistCorruption:
|
||||
case SE_Rune:
|
||||
case SE_AbsorbMagicAtt:
|
||||
case SE_DamageShield:
|
||||
case SE_MitigateDamageShield:
|
||||
case SE_Amplification: //On live Amplification is modified by singing mods, including itself.
|
||||
case SE_TripleAttackChance:
|
||||
case SE_Flurry:
|
||||
case SE_DamageModifier:
|
||||
case SE_DamageModifier2:
|
||||
case SE_MinDamageModifier:
|
||||
case SE_ProcChance:
|
||||
case SE_PetFlurry: // ? Need verified
|
||||
case SE_DiseaseCounter:
|
||||
case SE_PoisonCounter:
|
||||
case SE_CurseCounter:
|
||||
case SE_CorruptionCounter:
|
||||
return true;
|
||||
|
||||
/*
|
||||
Following are confirmed NOT modifiable by instrument/singing mods.
|
||||
Focus Effects, Proc Effects, Spell Triggers are not modified but handled elsewhere, not neccessary to checked here.
|
||||
*/
|
||||
|
||||
case SE_AttackSpeed: //(Haste AND Slow not modifiable)
|
||||
case SE_AttackSpeed2:
|
||||
case SE_AttackSpeed3:
|
||||
case SE_Lull:
|
||||
case SE_ChangeFrenzyRad:
|
||||
case SE_Harmony:
|
||||
case SE_AddFaction:
|
||||
//case SE_CurrentMana: // duration only
|
||||
case SE_ManaRegen_v2:
|
||||
//case SE_CurrentEndurance: // duration only
|
||||
case SE_PersistentEffect:
|
||||
case SE_ReduceReuseTimer:
|
||||
case SE_Stun:
|
||||
case SE_Mez:
|
||||
case SE_WipeHateList: //?
|
||||
case SE_CancelMagic:
|
||||
case SE_ManaAbsorbPercentDamage:
|
||||
case SE_ResistSpellChance:
|
||||
case SE_Reflect:
|
||||
case SE_MitigateSpellDamage:
|
||||
case SE_MitigateMeleeDamage:
|
||||
case SE_AllInstrumentMod:
|
||||
case SE_AddSingingMod:
|
||||
case SE_SongModCap:
|
||||
case SE_BardSongRange:
|
||||
case SE_TemporaryPets:
|
||||
case SE_SpellOnDeath:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
//Allowing anything not confirmed to be restricted / allowed to receive modifiers, as to not inhbit anyone making custom bard songs.
|
||||
}
|
||||
|
||||
int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot)
|
||||
{
|
||||
if (!IsValidSpell(spell_id))
|
||||
@@ -1380,93 +1484,152 @@ int GetSpellStatValue(uint32 spell_id, const char* stat_identifier, uint8 slot)
|
||||
}
|
||||
|
||||
if (slot < 12) {
|
||||
if (id == "base") { return spells[spell_id].base[slot]; }
|
||||
else if (id == "base2") { return spells[spell_id].base2[slot]; }
|
||||
else if (id == "max") { return spells[spell_id].max[slot]; }
|
||||
if (id == "base") { return spells[spell_id].base_value[slot]; }
|
||||
else if (id == "base2") { return spells[spell_id].limit_value[slot]; }
|
||||
else if (id == "max") { return spells[spell_id].max_value[slot]; }
|
||||
else if (id == "formula") { return spells[spell_id].formula[slot]; }
|
||||
else if (id == "effectid") { return spells[spell_id].effectid[slot]; }
|
||||
else if (id == "effectid") { return spells[spell_id].effect_id[slot]; }
|
||||
}
|
||||
|
||||
if (slot < 4) {
|
||||
if (id == "components") { return spells[spell_id].components[slot]; }
|
||||
else if (id == "component_counts") { return spells[spell_id].component_counts[slot]; }
|
||||
else if (id == "NoexpendReagent") { return spells[spell_id].NoexpendReagent[slot]; }
|
||||
if (id == "components") { return spells[spell_id].component[slot]; }
|
||||
else if (id == "component_counts") { return spells[spell_id].component_count[slot]; }
|
||||
else if (id == "noexpendreagent") { return spells[spell_id].no_expend_reagent[slot]; }
|
||||
}
|
||||
|
||||
if (id == "range") { return static_cast<int32>(spells[spell_id].range); }
|
||||
else if (id == "aoerange") { return static_cast<int32>(spells[spell_id].aoerange); }
|
||||
else if (id == "pushback") { return static_cast<int32>(spells[spell_id].pushback); }
|
||||
else if (id == "pushup") { return static_cast<int32>(spells[spell_id].pushup); }
|
||||
else if (id == "aoe_range") { return static_cast<int32>(spells[spell_id].aoe_range); }
|
||||
else if (id == "push_back") { return static_cast<int32>(spells[spell_id].push_back); }
|
||||
else if (id == "push_up") { return static_cast<int32>(spells[spell_id].push_up); }
|
||||
else if (id == "cast_time") { return spells[spell_id].cast_time; }
|
||||
else if (id == "recovery_time") { return spells[spell_id].recovery_time; }
|
||||
else if (id == "recast_time") { return spells[spell_id].recast_time; }
|
||||
else if (id == "buffdurationformula") { return spells[spell_id].buffdurationformula; }
|
||||
else if (id == "buffduration") { return spells[spell_id].buffduration; }
|
||||
else if (id == "AEDuration") { return spells[spell_id].AEDuration; }
|
||||
else if (id == "buff_duration_formula") { return spells[spell_id].buff_duration_formula; }
|
||||
else if (id == "buff_duration") { return spells[spell_id].buff_duration; }
|
||||
else if (id == "aeduration") { return spells[spell_id].aoe_duration; }
|
||||
else if (id == "mana") { return spells[spell_id].mana; }
|
||||
//else if (id == "LightType") {stat = spells[spell_id].LightType; } - Not implemented
|
||||
else if (id == "goodEffect") { return spells[spell_id].goodEffect; }
|
||||
else if (id == "Activated") { return spells[spell_id].Activated; }
|
||||
else if (id == "resisttype") { return spells[spell_id].resisttype; }
|
||||
else if (id == "targettype") { return spells[spell_id].targettype; }
|
||||
else if (id == "basediff") { return spells[spell_id].basediff; }
|
||||
else if (id == "goodeffect") { return spells[spell_id].good_effect; }
|
||||
else if (id == "activated") { return spells[spell_id].activated; }
|
||||
else if (id == "resisttype") { return spells[spell_id].resist_type; }
|
||||
else if (id == "targettype") { return spells[spell_id].target_type; }
|
||||
else if (id == "basediff") { return spells[spell_id].base_difficulty; }
|
||||
else if (id == "skill") { return spells[spell_id].skill; }
|
||||
else if (id == "zonetype") { return spells[spell_id].zonetype; }
|
||||
else if (id == "EnvironmentType") { return spells[spell_id].EnvironmentType; }
|
||||
else if (id == "TimeOfDay") { return spells[spell_id].TimeOfDay; }
|
||||
else if (id == "CastingAnim") { return spells[spell_id].CastingAnim; }
|
||||
else if (id == "SpellAffectIndex") { return spells[spell_id].SpellAffectIndex; }
|
||||
else if (id == "zonetype") { return spells[spell_id].zone_type; }
|
||||
else if (id == "environmenttype") { return spells[spell_id].environment_type; }
|
||||
else if (id == "timeofday") { return spells[spell_id].time_of_day; }
|
||||
else if (id == "castinganim") { return spells[spell_id].casting_animation; }
|
||||
else if (id == "spellaffectindex") { return spells[spell_id].spell_affect_index; }
|
||||
else if (id == "disallow_sit") { return spells[spell_id].disallow_sit; }
|
||||
//else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented
|
||||
else if (id == "uninterruptable") { return spells[spell_id].uninterruptable; }
|
||||
else if (id == "ResistDiff") { return spells[spell_id].ResistDiff; }
|
||||
else if (id == "dot_stacking_exempt") { return spells[spell_id].dot_stacking_exempt; }
|
||||
else if (id == "RecourseLink") { return spells[spell_id].RecourseLink; }
|
||||
else if (id == "resistdiff") { return spells[spell_id].resist_difficulty; }
|
||||
else if (id == "dot_stacking_exempt") { return spells[spell_id].unstackable_dot; }
|
||||
else if (id == "recourselink") { return spells[spell_id].recourse_link; }
|
||||
else if (id == "no_partial_resist") { return spells[spell_id].no_partial_resist; }
|
||||
else if (id == "short_buff_box") { return spells[spell_id].short_buff_box; }
|
||||
else if (id == "descnum") { return spells[spell_id].descnum; }
|
||||
else if (id == "effectdescnum") { return spells[spell_id].effectdescnum; }
|
||||
else if (id == "descnum") { return spells[spell_id].description_id; }
|
||||
else if (id == "effectdescnum") { return spells[spell_id].effect_description_id; }
|
||||
else if (id == "npc_no_los") { return spells[spell_id].npc_no_los; }
|
||||
else if (id == "feedbackable") { return spells[spell_id].feedbackable; }
|
||||
else if (id == "reflectable") { return spells[spell_id].reflectable; }
|
||||
else if (id == "bonushate") { return spells[spell_id].bonushate; }
|
||||
else if (id == "EndurCost") { return spells[spell_id].EndurCost; }
|
||||
else if (id == "EndurTimerIndex") { return spells[spell_id].EndurTimerIndex; }
|
||||
else if (id == "IsDisciplineBuff") { return spells[spell_id].IsDisciplineBuff; }
|
||||
else if (id == "HateAdded") { return spells[spell_id].HateAdded; }
|
||||
else if (id == "EndurUpkeep") { return spells[spell_id].EndurUpkeep; }
|
||||
else if (id == "numhitstype") { return spells[spell_id].numhitstype; }
|
||||
else if (id == "numhits") { return spells[spell_id].numhits; }
|
||||
else if (id == "pvpresistbase") { return spells[spell_id].pvpresistbase; }
|
||||
else if (id == "pvpresistcalc") { return spells[spell_id].pvpresistcalc; }
|
||||
else if (id == "pvpresistcap") { return spells[spell_id].pvpresistcap; }
|
||||
else if (id == "bonushate") { return spells[spell_id].bonus_hate; }
|
||||
else if (id == "endurcost") { return spells[spell_id].endurance_cost; }
|
||||
else if (id == "endurtimerindex") { return spells[spell_id].timer_id; }
|
||||
else if (id == "isdisciplinebuff") { return spells[spell_id].is_discipline; }
|
||||
else if (id == "hateadded") { return spells[spell_id].hate_added; }
|
||||
else if (id == "endurupkeep") { return spells[spell_id].endurance_upkeep; }
|
||||
else if (id == "numhitstype") { return spells[spell_id].hit_number_type; }
|
||||
else if (id == "numhits") { return spells[spell_id].hit_number; }
|
||||
else if (id == "pvpresistbase") { return spells[spell_id].pvp_resist_base; }
|
||||
else if (id == "pvpresistcalc") { return spells[spell_id].pvp_resist_per_level; }
|
||||
else if (id == "pvpresistcap") { return spells[spell_id].pvp_resist_cap; }
|
||||
else if (id == "spell_category") { return spells[spell_id].spell_category; }
|
||||
else if (id == "can_mgb") { return spells[spell_id].can_mgb; }
|
||||
else if (id == "dispel_flag") { return spells[spell_id].dispel_flag; }
|
||||
else if (id == "MinResist") { return spells[spell_id].MinResist; }
|
||||
else if (id == "MaxResist") { return spells[spell_id].MaxResist; }
|
||||
else if (id == "minresist") { return spells[spell_id].min_resist; }
|
||||
else if (id == "maxresist") { return spells[spell_id].max_resist; }
|
||||
else if (id == "viral_targets") { return spells[spell_id].viral_targets; }
|
||||
else if (id == "viral_timer") { return spells[spell_id].viral_timer; }
|
||||
else if (id == "NimbusEffect") { return spells[spell_id].NimbusEffect; }
|
||||
else if (id == "nimbuseffect") { return spells[spell_id].nimbus_effect; }
|
||||
else if (id == "directional_start") { return static_cast<int32>(spells[spell_id].directional_start); }
|
||||
else if (id == "directional_end") { return static_cast<int32>(spells[spell_id].directional_end); }
|
||||
else if (id == "not_focusable") { return spells[spell_id].not_focusable; }
|
||||
else if (id == "suspendable") { return spells[spell_id].suspendable; }
|
||||
else if (id == "viral_range") { return spells[spell_id].viral_range; }
|
||||
else if (id == "spellgroup") { return spells[spell_id].spellgroup; }
|
||||
else if (id == "spellgroup") { return spells[spell_id].spell_group; }
|
||||
else if (id == "rank") { return spells[spell_id].rank; }
|
||||
else if (id == "no_resist") { return spells[spell_id].no_resist; }
|
||||
else if (id == "CastRestriction") { return spells[spell_id].CastRestriction; }
|
||||
else if (id == "AllowRest") { return spells[spell_id].AllowRest; }
|
||||
else if (id == "InCombat") { return spells[spell_id].InCombat; }
|
||||
else if (id == "OutofCombat") { return spells[spell_id].OutofCombat; }
|
||||
else if (id == "aemaxtargets") { return spells[spell_id].aemaxtargets; }
|
||||
else if (id == "castrestriction") { return spells[spell_id].cast_restriction; }
|
||||
else if (id == "allowrest") { return spells[spell_id].allow_rest; }
|
||||
else if (id == "incombat") { return spells[spell_id].can_cast_in_combat; }
|
||||
else if (id == "outofcombat") { return spells[spell_id].can_cast_out_of_combat; }
|
||||
else if (id == "aemaxtargets") { return spells[spell_id].aoe_max_targets; }
|
||||
else if (id == "no_heal_damage_item_mod") { return spells[spell_id].no_heal_damage_item_mod; }
|
||||
else if (id == "persistdeath") { return spells[spell_id].persistdeath; }
|
||||
else if (id == "min_dist") { return static_cast<int32>(spells[spell_id].min_dist); }
|
||||
else if (id == "min_dist_mod") { return static_cast<int32>(spells[spell_id].min_dist_mod); }
|
||||
else if (id == "max_dist") { return static_cast<int32>(spells[spell_id].max_dist); }
|
||||
else if (id == "persistdeath") { return spells[spell_id].persist_death; }
|
||||
else if (id == "min_dist") { return static_cast<int32>(spells[spell_id].min_distance); }
|
||||
else if (id == "min_dist_mod") { return static_cast<int32>(spells[spell_id].min_distance_mod); }
|
||||
else if (id == "max_dist") { return static_cast<int32>(spells[spell_id].max_distance); }
|
||||
else if (id == "min_range") { return static_cast<int32>(spells[spell_id].min_range); }
|
||||
else if (id == "DamageShieldType") { return spells[spell_id].DamageShieldType; }
|
||||
else if (id == "damageshieldtype") { return spells[spell_id].damage_shield_type; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsVirusSpell(int32 spell_id)
|
||||
{
|
||||
if (GetViralMinSpreadTime(spell_id) && GetViralMaxSpreadTime(spell_id) && GetViralSpreadRange(spell_id)){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 GetViralMinSpreadTime(int32 spell_id)
|
||||
{
|
||||
return spells[spell_id].viral_targets;
|
||||
}
|
||||
|
||||
int32 GetViralMaxSpreadTime(int32 spell_id)
|
||||
{
|
||||
return spells[spell_id].viral_timer;
|
||||
}
|
||||
|
||||
int32 GetViralSpreadRange(int32 spell_id)
|
||||
{
|
||||
return spells[spell_id].viral_range;
|
||||
}
|
||||
|
||||
uint32 GetProcLimitTimer(int32 spell_id, int proc_type) {
|
||||
|
||||
//This allows for support for effects that may have multiple different proc types and timers.
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool use_next_timer = false;
|
||||
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||
|
||||
if (proc_type == SE_WeaponProc) {
|
||||
if (spells[spell_id].effect_id[i] == SE_WeaponProc || spells[spell_id].effect_id[i] == SE_AddMeleeProc) {
|
||||
use_next_timer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_type == SE_RangedProc) {
|
||||
if (spells[spell_id].effect_id[i] == SE_RangedProc) {
|
||||
use_next_timer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_type == SE_DefensiveProc) {
|
||||
if (spells[spell_id].effect_id[i] == SE_DefensiveProc) {
|
||||
use_next_timer = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_next_timer && spells[spell_id].effect_id[i] == SE_Proc_Timer_Modifier) {
|
||||
return spells[spell_id].limit_value[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
+107
-81
@@ -129,6 +129,8 @@
|
||||
#define SPELL_SPIRITUAL_ECHO 1248
|
||||
#define SPELL_BRISTLING_ARMAMENT 1249
|
||||
#define SPELL_WATON_DESTRUCTION 1250
|
||||
#define SPELL_TRANSLOCATE_GROUP 1334
|
||||
#define SPELL_TRANSLOCATE 1422
|
||||
#define SPELL_ACTING_MAGIC_RESIST_I 1900
|
||||
#define SPELL_ACTING_FIRE_RESIST_I 1901
|
||||
#define SPELL_ACTING_COLD_RESIST_I 1902
|
||||
@@ -153,8 +155,11 @@
|
||||
#define SPELL_ACTING_SPIRIT_I 1921
|
||||
#define SPELL_ACTING_SPIRIT_II 1922
|
||||
#define SPELL_RESURRECTION_SICKNESS 756
|
||||
#define SPELL_RESURRECTION_SICKNESS4 757
|
||||
#define SPELL_TELEPORT 3243
|
||||
#define SPELL_RESURRECTION_SICKNESS2 5249
|
||||
#define SPELL_REVIVAL_SICKNESS 13087
|
||||
#define SPELL_RESURRECTION_SICKNESS3 37624
|
||||
#define SPELL_PACT_OF_HATE_RECOURSE 40375
|
||||
#define SPELL_INCENDIARY_OOZE_BUFF 32513
|
||||
|
||||
@@ -172,9 +177,12 @@
|
||||
#define EFFECT_COUNT 12
|
||||
#define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2)
|
||||
#define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists.
|
||||
#define MaxLimitInclude 16 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects
|
||||
#define MaxLimitInclude 18 //Number(x 0.5) of focus Limiters that have inclusive checks used when calcing focus effects
|
||||
#define MAX_SKILL_PROCS 4 //Number of spells to check skill procs from. (This is arbitrary) [Single spell can have multiple proc checks]
|
||||
#define MAX_AA_PROCS 16 //(Actual Proc Amount is MAX_AA_PROCS/4) Number of spells to check AA procs from. (This is arbitrary)
|
||||
#define MAX_SYMPATHETIC_PROCS 10 // Number of sympathetic procs a client can have (This is arbitrary)
|
||||
#define MAX_FOCUS_PROC_LIMIT_TIMERS 20 //Number of focus recast timers that can be going at same time (This is arbitrary)
|
||||
#define MAX_PROC_LIMIT_TIMERS 8 //Number of proc delay timers that can be going at same time, different proc types get their own timer array. (This is arbitrary)
|
||||
|
||||
|
||||
|
||||
@@ -198,7 +206,9 @@ enum FocusLimitIncludes {
|
||||
IncludeExistsSELimitSpellClass = 12,
|
||||
IncludeFoundSELimitSpellClass = 13,
|
||||
IncludeExistsSELimitSpellSubclass = 14,
|
||||
IncludeFoundSELimitSpellSubclass = 15
|
||||
IncludeFoundSELimitSpellSubclass = 15,
|
||||
IncludeExistsSEFFItemClass = 16,
|
||||
IncludeFoundSEFFItemClass = 17
|
||||
};
|
||||
/*
|
||||
The id's correspond to 'type' 39 in live(2021) dbstr_us gives the message for target and caster restricted effects. These are not present in the ROF2 dbstr_us.
|
||||
@@ -509,6 +519,15 @@ enum NegateSpellEffectType
|
||||
NEGATE_SPA_SPELLBONUS_AND_AABONUS = 5,
|
||||
NEGATE_SPA_ITEMBONUS_AND_AABONUS = 6,
|
||||
};
|
||||
//Used for rule RuleI(Spells, ReflectType))
|
||||
enum ReflectSpellType
|
||||
{
|
||||
REFLECT_DISABLED = 0,
|
||||
REFLECT_SINGLE_TARGET_SPELLS_ONLY = 1,
|
||||
REFLECT_ALL_PLAYER_SPELLS = 2,
|
||||
RELFECT_ALL_SINGLE_TARGET_SPELLS = 3,
|
||||
REFLECT_ALL_SPELLS = 4,
|
||||
};
|
||||
|
||||
enum SpellTypes : uint32
|
||||
{
|
||||
@@ -547,7 +566,7 @@ const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellTyp
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
// TODO: import sai list
|
||||
enum SpellAffectIndex {
|
||||
enum spell_affect_index {
|
||||
SAI_Summon_Mount_Unclass = -1,
|
||||
SAI_Direct_Damage = 0,
|
||||
SAI_Heal_Cure = 1,
|
||||
@@ -746,7 +765,7 @@ typedef enum {
|
||||
//#define SE_TransferItem 60 // not used
|
||||
#define SE_Identify 61 // implemented
|
||||
//#define SE_ItemID 62 // not used
|
||||
#define SE_WipeHateList 63 // implemented
|
||||
#define SE_WipeHateList 63 // implemented, @Memblur, chance to wipe hate list of target, base: pct chance, limit: none, max: ? (not implemented), Note: caster level and CHA add to pct chance
|
||||
#define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly
|
||||
#define SE_InfraVision 65 // implemented
|
||||
#define SE_UltraVision 66 // implemented
|
||||
@@ -801,7 +820,7 @@ typedef enum {
|
||||
#define SE_Hunger 115 // implemented - Song of Sustenance
|
||||
#define SE_CurseCounter 116 // implemented
|
||||
#define SE_MagicWeapon 117 // implemented - makes weapon magical
|
||||
#define SE_Amplification 118 // implemented - Harmonize/Amplification (stacks with other singing mods)
|
||||
#define SE_Amplification 118 // implemented, @Song, stackable singing mod, base: mod%, limit: none, max: none, Note: Can focus itself.
|
||||
#define SE_AttackSpeed3 119 // implemented
|
||||
#define SE_HealRate 120 // implemented - reduces healing by a %
|
||||
#define SE_ReverseDS 121 // implemented
|
||||
@@ -837,11 +856,11 @@ typedef enum {
|
||||
#define SE_SuspendPet 151 // implemented, @Pet, allow caster to have an extra suspended pet, base: 0=no buffs/items 1=buffs+items, limit: none, max: none
|
||||
#define SE_TemporaryPets 152 // implemented
|
||||
#define SE_BalanceHP 153 // implemented
|
||||
#define SE_DispelDetrimental 154 // implemented
|
||||
#define SE_DispelDetrimental 154 // implemented, @Dispel, removes only detrimental effects on a target, base: pct chance (950=95%), limit: none, max: none
|
||||
#define SE_SpellCritDmgIncrease 155 // implemented - no known live spells use this currently
|
||||
#define SE_IllusionCopy 156 // implemented - Deception
|
||||
#define SE_SpellDamageShield 157 // implemented - Petrad's Protection
|
||||
#define SE_Reflect 158 // implemented
|
||||
#define SE_SpellDamageShield 157 // implemented, @DS, causes non-melee damage on caster of a spell, base: Amt DS (negative), limit: none, max: unknown (same as base but +)
|
||||
#define SE_Reflect 158 // implemented, @SpellMisc, reflect casted detrimental spell back at caster, base: chance pct, limit: resist modifier (positive value reduces resists), max: pct of base dmg mod (50=50pct of base)
|
||||
#define SE_AllStats 159 // implemented
|
||||
//#define SE_MakeDrunk 160 // *not implemented - Effect works entirely client side (Should check against tolerance)
|
||||
#define SE_MitigateSpellDamage 161 // implemented - rune with max value
|
||||
@@ -862,7 +881,7 @@ typedef enum {
|
||||
#define SE_DualWieldChance 176 // implemented
|
||||
#define SE_DoubleAttackChance 177 // implemented
|
||||
#define SE_MeleeLifetap 178 // implemented
|
||||
#define SE_AllInstrumentMod 179 // implemented
|
||||
#define SE_AllInstrumentMod 179 // implemented, @Song, set mod for ALL instrument/singing skills that will be used if higher then item mods, base: mod%, limit: none, max: none
|
||||
#define SE_ResistSpellChance 180 // implemented
|
||||
#define SE_ResistFearChance 181 // implemented
|
||||
#define SE_HundredHands 182 // implemented
|
||||
@@ -892,7 +911,7 @@ typedef enum {
|
||||
#define SE_AETaunt 206 // implemented
|
||||
#define SE_FleshToBone 207 // implemented
|
||||
//#define SE_PurgePoison 208 // not used
|
||||
#define SE_DispelBeneficial 209 // implemented
|
||||
#define SE_DispelBeneficial 209 // implemented, @Dispel, removes only beneficial effects on a target, base: pct chance (950=95%), limit: none, max: none
|
||||
#define SE_PetShield 210 // implmented, @ShieldAbility, allows pet to 'shield' owner for 50 pct of damage taken for a duration, base: Time multiplier 1=12 seconds, 2=24 ect, limit: mitigation on pet owner override (not on live), max: mitigation on pet overide (not on live)
|
||||
#define SE_AEMelee 211 // implemented TO DO: Implement to allow NPC use (client only atm).
|
||||
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
|
||||
@@ -925,7 +944,7 @@ typedef enum {
|
||||
#define SE_FeignedCastOnChance 239 // implemented - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
|
||||
//#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.]
|
||||
#define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet.
|
||||
#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr
|
||||
#define SE_IncreaseChanceMemwipe 242 // implemented - @Memblur, increases the chance to wipe hate with memory blurr, base: chance pct, limit: none, max: none, Note: Mods final blur chance after other bonuses added.
|
||||
#define SE_CharmBreakChance 243 // implemented - Total Domination
|
||||
#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break.
|
||||
#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
|
||||
@@ -943,8 +962,8 @@ typedef enum {
|
||||
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold - official name is GivePetHold
|
||||
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
|
||||
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
|
||||
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
|
||||
#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live)
|
||||
#define SE_AddSingingMod 260 // implemented, @Song, set mod for specific instrument/singing skills that will be used if higher then item mods, base: mod%, limit: ItemType ID, max: none
|
||||
#define SE_SongModCap 261 // implemented, @Song, raise max song modifier cap, base: amt, limit: none, max: none, Note: No longer used on live
|
||||
#define SE_RaiseStatCap 262 // implemented
|
||||
#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master.
|
||||
#define SE_HastenedAASkill 264 // implemented
|
||||
@@ -953,7 +972,7 @@ typedef enum {
|
||||
#define SE_AddPetCommand 267 // implemented - sets command base2 to base1
|
||||
#define SE_ReduceTradeskillFail 268 // implemented - reduces chance to fail with given tradeskill by a percent chance
|
||||
#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound.
|
||||
#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo)
|
||||
#define SE_BardSongRange 270 // implemented, @Song, increase range of beneficial bard songs, base: mod%, limit: none, max: none , Note: example Sionachie's Crescendo
|
||||
#define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods
|
||||
#define SE_CastingLevel2 272 // implemented
|
||||
#define SE_CriticalDoTChance 273 // implemented
|
||||
@@ -974,7 +993,7 @@ typedef enum {
|
||||
#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch)
|
||||
#define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration.
|
||||
#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap
|
||||
#define SE_Purify 291 // implemented - Removes determental effects
|
||||
#define SE_Purify 291 // implemented, @Dispel, remove up specified amount of detiremental spells, base: amt removed, limit: none, max: none, Note: excluding charm, fear, resurrection, and revival sickness
|
||||
#define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte.
|
||||
#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. -- live descriptions sounds like this isn't limited to frontal anymore
|
||||
#define SE_CriticalSpellChance 294 // implemented - increase chance to critical hit and critical damage modifier.
|
||||
@@ -1065,7 +1084,7 @@ typedef enum {
|
||||
#define SE_ShadowStepDirectional 379 // implemented - handled by client
|
||||
#define SE_Knockdown 380 // implemented - small knock back(handled by client)
|
||||
//#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront
|
||||
#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none
|
||||
#define SE_NegateSpellEffect 382 // implemented, @Debuff, negates specific spell effect benefits for the duration of the debuff and prevent non-duration spell effect from working, base: see NegateSpellEffecttype Enum, limit: SPA id, max: none
|
||||
#define SE_SympatheticProc 383 // implemented, @Fc, On Caster, cast on spell use, base: variable proc chance on cast time, limit: spellid
|
||||
#define SE_Leap 384 // implemented - Leap effect, ie stomping leap
|
||||
#define SE_LimitSpellGroup 385 // implemented, @Ff, Spell group(s) that a spell focus can require or exclude, base1: spellgroup id, Include: Positive Exclude: Negative
|
||||
@@ -1076,9 +1095,9 @@ typedef enum {
|
||||
#define SE_FcTimerLockout 390 // implemented, @Fc, On Caster, set a spell to be on recast timer, base: recast duration milliseconds, Note: Applied from casted spells only
|
||||
#define SE_LimitManaMax 391 // implemented, @Ff, Mininum mana of spell that can be focused, base1: mana amt
|
||||
#define SE_FcHealAmt 392 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt
|
||||
#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received critical chance mod, base: chance pct
|
||||
#define SE_FcHealPctIncoming 393 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct
|
||||
#define SE_FcHealAmtIncoming 394 // implemented, @Fc, On Target, heal received mod flat amt, base: amt
|
||||
#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct
|
||||
#define SE_FcHealPctCritIncoming 395 // implemented, @Fc, On Target, heal received mod pct, base: pct, limit: random max pct
|
||||
#define SE_FcHealAmtCrit 396 // implemented, @Fc, On Caster, spell healing mod flat amt, base: amt
|
||||
#define SE_PetMeleeMitigation 397 // implemented[AA] - additional mitigation to your pets. Adds AC
|
||||
#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets
|
||||
@@ -1098,7 +1117,7 @@ typedef enum {
|
||||
#define SE_LimitRace 412 // implemented, @Ff, Race that can use the spell focus, base1: race, Note: not used in any known live spells. Use only single race at a time.
|
||||
#define SE_FcBaseEffects 413 // implemented, @Fc, On Caster, base spell effectiveness mod pct, base: pct
|
||||
#define SE_LimitCastingSkill 414 // implemented, @Ff, Spell and singing skills(s) that a spell focus can require or exclude, base1: skill id, Include: Positive Exclude: Negative
|
||||
//#define SE_FFItemClass 415 // not used - base1 matches ItemType, base2 matches SubType, -1 ignored, max is bitmask of valid slots
|
||||
#define SE_FFItemClass 415 // implemented, @Ff, Limits focuses to be applied only from item click. base1: item ItemType (-1 to include for all ItemTypes,-1000 to exclude clicks from getting the focus, or exclude specific SubTypes or Slots if set), limit: item SubType (-1 for all SubTypes), max: item Slots (bitmask of valid slots, -1 ALL slots), Note: not used on live. See comments in Mob::CalcFocusEffect for more details.
|
||||
#define SE_ACv2 416 // implemented - New AC spell effect
|
||||
#define SE_ManaRegen_v2 417 // implemented - New mana regen effect
|
||||
#define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks
|
||||
@@ -1125,8 +1144,8 @@ typedef enum {
|
||||
#define SE_Assassinate 439 // implemented[AA] - Assassinate damage
|
||||
#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC
|
||||
#define SE_DistanceRemoval 441 // implemented - Buff is removed from target when target moves X amount of distance away from where initially hit.
|
||||
#define SE_TriggerOnReqTarget 442 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
#define SE_TriggerOnReqCaster 443 // implemented - triggers a spell which a certain criteria are met (below X amount of hp,mana,end, number of pets on hatelist)
|
||||
#define SE_TriggerOnReqTarget 442 // implemented, @SpellTrigger, triggers a spell when Target Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually cast on a target
|
||||
#define SE_TriggerOnReqCaster 443 // implemented, @SpellTrigger, triggers a spell when Caster Requirement conditions are met (see enum SpellRestriction for IDs), base: spellid, limit: SpellRestriction ID, max: none, Note: Usually self only
|
||||
#define SE_ImprovedTaunt 444 // implemented - Locks Aggro On Caster and Decrease other Players Aggro by X% on NPC targets below level Y
|
||||
//#define SE_AddMercSlot 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs.
|
||||
#define SE_AStacker 446 // implementet - bufff stacking blocker (26219 | Qirik's Watch)
|
||||
@@ -1194,8 +1213,8 @@ typedef enum {
|
||||
#define SE_Fc_Amplify_Amt 508 // implemented, @Fc, On Caster, damage-heal-dot mod flat amt, base: amt
|
||||
#define SE_Health_Transfer 509 // implemented - exchange health for damage or healing on a target. ie Lifeburn/Act of Valor
|
||||
#define SE_Fc_ResistIncoming 510 // implemented, @Fc, On Target, resist modifier, base: amt
|
||||
//#define SE_Ff_FocusTimerMin 511 //
|
||||
#define SE_Proc_Timer_Modifier 512 // implemented - spell trigger limiter used currently with SPA 481, ie. limit to 1 proc every 1.5 seconds (base=1 base2=1500).
|
||||
#define SE_Ff_FocusTimerMin 511 // implemented, @Ff, sets a recast time until focus can be used again, base: 1, limit: time ms, Note: ie. limit to 1 trigger every 1.5 seconds
|
||||
#define SE_Proc_Timer_Modifier 512 // implemented - limits procs per amount of a time based on timer value, base: 1, limit: time ms, Note:, ie limit to 1 proc every 55 seconds)
|
||||
//#define SE_Mana_Max_Percent 513 //
|
||||
//#define SE_Endurance_Max_Percent 514 //
|
||||
#define SE_AC_Avoidance_Max_Percent 515 // implemented - stackable avoidance modifier
|
||||
@@ -1238,44 +1257,44 @@ struct SPDat_Spell_Struct
|
||||
/* 007 */ char cast_on_other[64]; // Message when spell is cast on someone else -- CASTEDOTHERTXT
|
||||
/* 008 */ char spell_fades[64]; // Spell fades -- SPELLGONE
|
||||
/* 009 */ float range; // -- RANGE
|
||||
/* 010 */ float aoerange; // -- IMPACTRANGE
|
||||
/* 011 */ float pushback; // -- OUTFORCE
|
||||
/* 012 */ float pushup; // -- UPFORCE
|
||||
/* 010 */ float aoe_range; // -- IMPACTRANGE
|
||||
/* 011 */ float push_back; // -- OUTFORCE
|
||||
/* 012 */ float push_up; // -- UPFORCE
|
||||
/* 013 */ uint32 cast_time; // Cast time -- CASTINGTIME
|
||||
/* 014 */ uint32 recovery_time; // Recovery time -- RECOVERYDELAY
|
||||
/* 015 */ uint32 recast_time; // Recast same spell time -- SPELLDELAY
|
||||
/* 016 */ uint32 buffdurationformula; // -- DURATIONBASE
|
||||
/* 017 */ uint32 buffduration; // -- DURATIONCAP
|
||||
/* 018 */ uint32 AEDuration; // sentinel, rain of something -- IMPACTDURATION
|
||||
/* 016 */ uint32 buff_duration_formula; // -- DURATIONBASE
|
||||
/* 017 */ uint32 buff_duration; // -- DURATIONCAP
|
||||
/* 018 */ uint32 aoe_duration; // sentinel, rain of something -- IMPACTDURATION
|
||||
/* 019 */ uint16 mana; // Mana Used -- MANACOST
|
||||
/* 020 */ int base[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12
|
||||
/* 032 */ int base2[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12
|
||||
/* 044 */ int32 max[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP
|
||||
/* 020 */ int base_value[EFFECT_COUNT]; //various purposes -- BASEAFFECT1 .. BASEAFFECT12
|
||||
/* 032 */ int limit_value[EFFECT_COUNT]; //various purposes -- BASE_EFFECT2_1 ... BASE_EFFECT2_12
|
||||
/* 044 */ int32 max_value[EFFECT_COUNT]; // -- AFFECT1CAP ... AFFECT12CAP
|
||||
/* 056 */ //uint16 icon; // Spell icon -- IMAGENUMBER
|
||||
/* 057 */ //uint16 memicon; // Icon on membarthing -- MEMIMAGENUMBER
|
||||
/* 058 */ int32 components[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4
|
||||
/* 062 */ int component_counts[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4
|
||||
/* 066 */ int NoexpendReagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.)
|
||||
/* 058 */ int32 component[4]; // reagents -- EXPENDREAGENT1 ... EXPENDREAGENT4
|
||||
/* 062 */ int component_count[4]; // amount of regents used -- EXPENDQTY1 ... EXPENDQTY4
|
||||
/* 066 */ int no_expend_reagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.)
|
||||
// If it is a number between 1-4 it means components[number] is a focus and not to expend it
|
||||
// If it is a valid itemid it means this item is a focus as well
|
||||
// -- NOEXPENDREAGENT1 ... NOEXPENDREAGENT4
|
||||
/* 070 */ uint16 formula[EFFECT_COUNT]; // Spell's value formula -- LEVELAFFECT1MOD ... LEVELAFFECT12MOD
|
||||
/* 082 */ //int LightType; // probaly another effecttype flag -- LIGHTTYPE
|
||||
/* 083 */ int8 goodEffect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL
|
||||
/* 084 */ int Activated; // probably another effecttype flag -- ACTIVATED
|
||||
/* 085 */ int resisttype; // -- RESISTTYPE
|
||||
/* 086 */ int effectid[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12
|
||||
/* 098 */ SpellTargetType targettype; // Spell's Target -- TYPENUMBER
|
||||
/* 099 */ int basediff; // base difficulty fizzle adjustment -- BASEDIFFICULTY
|
||||
/* 083 */ int8 good_effect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only -- BENEFICIAL
|
||||
/* 084 */ int activated; // probably another effecttype flag -- ACTIVATED
|
||||
/* 085 */ int resist_type; // -- RESISTTYPE
|
||||
/* 086 */ int effect_id[EFFECT_COUNT]; // Spell's effects -- SPELLAFFECT1 ... SPELLAFFECT12
|
||||
/* 098 */ SpellTargetType target_type; // Spell's Target -- TYPENUMBER
|
||||
/* 099 */ int base_difficulty; // base difficulty fizzle adjustment -- BASEDIFFICULTY
|
||||
/* 100 */ EQ::skills::SkillType skill; // -- CASTINGSKILL
|
||||
/* 101 */ int8 zonetype; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE
|
||||
/* 102 */ int8 EnvironmentType; // -- ENVIRONMENTTYPE
|
||||
/* 103 */ int8 TimeOfDay; // -- TIMEOFDAY
|
||||
/* 101 */ int8 zone_type; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE
|
||||
/* 102 */ int8 environment_type; // -- ENVIRONMENTTYPE
|
||||
/* 103 */ int8 time_of_day; // -- TIMEOFDAY
|
||||
/* 104 */ uint8 classes[PLAYER_CLASS_COUNT]; // Classes, and their min levels -- WARRIORMIN ... BERSERKERMIN
|
||||
/* 120 */ uint8 CastingAnim; // -- CASTINGANIM
|
||||
/* 120 */ uint8 casting_animation; // -- CASTINGANIM
|
||||
/* 121 */ //uint8 TargetAnim; // -- TARGETANIM
|
||||
/* 122 */ //uint32 TravelType; // -- TRAVELTYPE
|
||||
/* 123 */ uint16 SpellAffectIndex; // -- SPELLAFFECTINDEX
|
||||
/* 123 */ uint16 spell_affect_index; // -- SPELLAFFECTINDEX
|
||||
/* 124 */ int8 disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury -- CANCELONSIT
|
||||
/* 125 */ int8 deity_agnostic;// 125: Words of the Skeptic -- DEITY_AGNOSTIC
|
||||
/* 126 */ int8 deities[16]; // Deity check. 201 - 216 per http://www.eqemulator.net/wiki/wikka.php?wakka=DeityList
|
||||
@@ -1287,36 +1306,36 @@ struct SPDat_Spell_Struct
|
||||
/* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon -- NEW_ICON
|
||||
/* 145 */ //int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is, particles I think -- SPELL_EFFECT_INDEX
|
||||
/* 146 */ bool uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) -- NO_INTERRUPT
|
||||
/* 147 */ int16 ResistDiff; // -- RESIST_MOD
|
||||
/* 148 */ bool dot_stacking_exempt; // -- NOT_STACKABLE_DOT
|
||||
/* 147 */ int16 resist_difficulty; // -- RESIST_MOD
|
||||
/* 148 */ bool unstackable_dot; // -- NOT_STACKABLE_DOT
|
||||
/* 149 */ //int deletable; // -- DELETE_OK
|
||||
/* 150 */ uint16 RecourseLink; // -- REFLECT_SPELLINDEX
|
||||
/* 150 */ uint16 recourse_link; // -- REFLECT_SPELLINDEX
|
||||
/* 151 */ bool no_partial_resist; // 151: -1, 0, or 1 -- NO_PARTIAL_SAVE
|
||||
/* 152 */ //bool small_targets_only; // -- SMALL_TARGETS_ONLY
|
||||
/* 153 */ //bool uses_persistent_particles; // -- USES_PERSISTENT_PARTICLES
|
||||
/* 154 */ int8 short_buff_box; // != 0, goes to short buff box. -- BARD_BUFF_BOX
|
||||
/* 155 */ int descnum; // eqstr of description of spell -- DESCRIPTION_INDEX
|
||||
/* 156 */ int typedescnum; // eqstr of type description -- PRIMARY_CATEGORY
|
||||
/* 157 */ int effectdescnum; // eqstr of effect description -- SECONDARY_CATEGORY_1
|
||||
/* 155 */ int description_id; // eqstr of description of spell -- DESCRIPTION_INDEX
|
||||
/* 156 */ int type_description_id; // eqstr of type description -- PRIMARY_CATEGORY
|
||||
/* 157 */ int effect_description_id; // eqstr of effect description -- SECONDARY_CATEGORY_1
|
||||
/* 158 */ //int secondary_category_2; //Category Desc ID 3 -- SECONDARY_CATEGORY_2
|
||||
/* 159 */ bool npc_no_los; // -- NO_NPC_LOS
|
||||
/* 160 */ //bool feedbackable; // -- FEEDBACKABLE
|
||||
/* 160 */ bool feedbackable; // -- FEEDBACKABLE
|
||||
/* 161 */ bool reflectable; // -- REFLECTABLE
|
||||
/* 162 */ int bonushate; // -- HATE_MOD
|
||||
/* 162 */ int bonus_hate; // -- HATE_MOD
|
||||
/* 163 */ //int resist_per_level; // -- RESIST_PER_LEVEL
|
||||
/* 164 */ //int resist_cap; // for most spells this appears to mimic ResistDiff -- RESIST_CAP
|
||||
/* 165 */ bool ldon_trap; //Flag found on all LDON trap / chest related spells. -- AFFECT_INANIMATE
|
||||
/* 166 */ int EndurCost; // -- STAMINA_COST
|
||||
/* 167 */ int8 EndurTimerIndex; // bad name, used for all spells -- TIMER_INDEX
|
||||
/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast -- IS_SKILL
|
||||
/* 166 */ int endurance_cost; // -- STAMINA_COST
|
||||
/* 167 */ int8 timer_id; // bad name, used for all spells -- TIMER_INDEX
|
||||
/* 168 */ bool is_discipline; //Will goto the combat window when cast -- IS_SKILL
|
||||
/* 169 - 172*/ //These are zero for ALL spells, also removed from live -- ATTACK_OPENING, DEFENSE_OPENING, SKILL_OPENING, NPC_ERROR_OPENING
|
||||
/* 173 */ int HateAdded; // -- SPELL_HATE_GIVEN
|
||||
/* 174 */ int EndurUpkeep; // -- ENDUR_UPKEEP
|
||||
/* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE
|
||||
/* 176 */ int numhits; // -- LIMITED_USE_COUNT
|
||||
/* 177 */ int pvpresistbase; // -- PVP_RESIST_MOD
|
||||
/* 178 */ int pvpresistcalc; // -- PVP_RESIST_PER_LEVEL
|
||||
/* 179 */ int pvpresistcap; // -- PVP_RESIST_CAP
|
||||
/* 173 */ int hate_added; // -- SPELL_HATE_GIVEN
|
||||
/* 174 */ int endurance_upkeep; // -- ENDUR_UPKEEP
|
||||
/* 175 */ int hit_number_type; // defines which type of behavior will tick down the numhit counter. -- LIMITED_USE_TYPE
|
||||
/* 176 */ int hit_number; // -- LIMITED_USE_COUNT
|
||||
/* 177 */ int pvp_resist_base; // -- PVP_RESIST_MOD
|
||||
/* 178 */ int pvp_resist_per_level; // -- PVP_RESIST_PER_LEVEL
|
||||
/* 179 */ int pvp_resist_cap; // -- PVP_RESIST_CAP
|
||||
/* 180 */ int spell_category; // -- GLOBAL_GROUP
|
||||
/* 181 */ int pvp_duration; // buffdurationformula for PvP -- PVP_DURATION
|
||||
/* 182 */ int pvp_duration_cap; // buffduration for PvP -- PVP_DURATION_CAP
|
||||
@@ -1326,11 +1345,11 @@ struct SPDat_Spell_Struct
|
||||
/* 186 */ int dispel_flag; // -- NO_DISPELL
|
||||
/* 187 */ //int npc_category; // -- NPC_MEM_CATEGORY
|
||||
/* 188 */ //int npc_usefulness; // -- NPC_USEFULNESS
|
||||
/* 189 */ int MinResist; // -- MIN_RESIST
|
||||
/* 190 */ int MaxResist; // -- MAX_RESIST
|
||||
/* 189 */ int min_resist; // -- MIN_RESIST
|
||||
/* 190 */ int max_resist; // -- MAX_RESIST
|
||||
/* 191 */ uint8 viral_targets; // -- MIN_SPREAD_TIME
|
||||
/* 192 */ uint8 viral_timer; // -- MAX_SPREAD_TIME
|
||||
/* 193 */ int NimbusEffect; // -- DURATION_PARTICLE_EFFECT
|
||||
/* 193 */ int nimbus_effect; // -- DURATION_PARTICLE_EFFECT
|
||||
/* 194 */ float directional_start; //Cone Start Angle: -- CONE_START_ANGLE
|
||||
/* 195 */ float directional_end; // Cone End Angle: -- CONE_END_ANGLE
|
||||
/* 196 */ bool sneak; // effect can only be used if sneaking (rogue 'Daggerfall' ect) -- SNEAK_ATTACK
|
||||
@@ -1339,35 +1358,35 @@ struct SPDat_Spell_Struct
|
||||
/* 199 */ //bool show_wear_off_message; // -- SHOW_WEAR_OFF_MESSAGE
|
||||
/* 200 */ bool suspendable; // buff is suspended in suspended buff zones -- IS_COUNTDOWN_HELD
|
||||
/* 201 */ int viral_range; // -- SPREAD_RADIUS
|
||||
/* 202 */ int songcap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP
|
||||
/* 202 */ int song_cap; // individual song cap -- BASE_EFFECTS_FOCUS_CAP
|
||||
/* 203 */ //bool stacks_with_self; // -- STACKS_WITH_SELF
|
||||
/* 204 */ //int not_shown_to_player; // client skips this -- NOT_SHOWN_TO_PLAYER
|
||||
/* 205 */ bool no_block; // -- NO_BUFF_BLOCK
|
||||
/* 206 */ //int8 anim_variation; // -- ANIM_VARIATION
|
||||
/* 207 */ int spellgroup; // -- SPELL_GROUP
|
||||
/* 207 */ int spell_group; // -- SPELL_GROUP
|
||||
/* 208 */ int rank; //increments AA effects with same name -- SPELL_GROUP_RANK
|
||||
/* 209 */ int no_resist; //makes spells unresistable, which makes charms unbreakable as well. -- NO_RESIST
|
||||
/* 210 */ // bool allow_spellscribe; // -- ALLOW_SPELLSCRIBE
|
||||
/* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID
|
||||
/* 212 */ bool AllowRest; // -- BYPASS_REGEN_CHECK
|
||||
/* 213 */ bool InCombat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT
|
||||
/* 214 */ bool OutofCombat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT
|
||||
/* 211 */ int cast_restriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat -- SPELL_REQ_ASSOCIATION_ID
|
||||
/* 212 */ bool allow_rest; // -- BYPASS_REGEN_CHECK
|
||||
/* 213 */ bool can_cast_in_combat; //Allow spell if target is in combat -- CAN_CAST_IN_COMBAT
|
||||
/* 214 */ bool can_cast_out_of_combat; //Allow spell if target is out of combat -- CAN_CAST_OUT_OF_COMBAT
|
||||
/* 215 */ //bool show_dot_message; // -- SHOW_DOT_MESSAGE
|
||||
/* 216 */ //bool invalid; // -- INVALID
|
||||
/* 217 */ int override_crit_chance; //Places a cap on the max chance to critical -- OVERRIDE_CRIT_CHANCE
|
||||
/* 218 */ int aemaxtargets; //Is used for various AE effects -- MAX_TARGETS
|
||||
/* 218 */ int aoe_max_targets; //Is used for various AE effects -- MAX_TARGETS
|
||||
/* 219 */ int no_heal_damage_item_mod; // -- NO_HEAL_DAMAGE_ITEM_MOD
|
||||
/* 220 */ int caster_requirement_id; // -- CASTER_REQUIREMENT_ID
|
||||
/* 221 */ int spell_class; // -- SPELL_CLASS
|
||||
/* 222 */ int spell_subclass; // -- SPELL_SUBCLASS
|
||||
/* 223 */ //int ai_valid_targets; // -- AI_VALID_TARGETS
|
||||
/* 224 */ bool persistdeath; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH
|
||||
/* 224 */ bool persist_death; // buff doesn't get stripped on death -- NO_STRIP_ON_DEATH
|
||||
/* 225 */ //float base_effects_focus_slope; // -- BASE_EFFECTS_FOCUS_SLOPE
|
||||
/* 226 */ //float base_effects_focus_offset; // -- BASE_EFFECTS_FOCUS_OFFSET
|
||||
/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST
|
||||
/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT
|
||||
/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST
|
||||
/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT
|
||||
/* 227 */ float min_distance; //spell power modified by distance from caster (Min Distance) -- DISTANCE_MOD_CLOSE_DIST
|
||||
/* 228 */ float min_distance_mod; //spell power modified by distance from caster (Modifier at Min Distance) -- DISTANCE_MOD_CLOSE_MULT
|
||||
/* 229 */ float max_distance; //spell power modified by distance from caster (Max Distance) -- DISTANCE_MOD_FAR_DIST
|
||||
/* 230 */ float max_distance_mod; //spell power modified by distance from caster (Modifier at Max Distance) -- DISTANCE_MOD_FAR_MULT
|
||||
/* The client also does this
|
||||
* v26 = *(float *)&v4->DistanceModFarDist - *(float *)&v4->DistanceModCloseDist;
|
||||
* if ( v26 > -0.00000011920929 && v26 < 0.00000011920929 )
|
||||
@@ -1381,7 +1400,7 @@ struct SPDat_Spell_Struct
|
||||
/* 234 */ //bool only_during_fast_regen; // -- ONLY_DURING_FAST_REGEN
|
||||
/* 235 */ //bool is_beta_only; // -- IS_BETA_ONLY
|
||||
/* 236 */ //int spell_subgroup; // -- SPELL_SUBGROUP
|
||||
uint8 DamageShieldType; // This field does not exist in spells_us.txt
|
||||
uint8 damage_shield_type; // This field does not exist in spells_us.txt
|
||||
};
|
||||
|
||||
extern const SPDat_Spell_Struct* spells;
|
||||
@@ -1461,6 +1480,7 @@ bool IsPartialDeathSaveSpell(uint16 spell_id);
|
||||
bool IsShadowStepSpell(uint16 spell_id);
|
||||
bool IsSuccorSpell(uint16 spell_id);
|
||||
bool IsTeleportSpell(uint16 spell_id);
|
||||
bool IsTranslocateSpell(uint16 spell_id);
|
||||
bool IsGateSpell(uint16 spell_id);
|
||||
bool IsPlayerIllusionSpell(uint16 spell_id); // seveian 2008-09-23
|
||||
bool IsLDoNObjectSpell(uint16 spell_id);
|
||||
@@ -1493,7 +1513,13 @@ bool IsBardOnlyStackEffect(int effect);
|
||||
bool IsCastWhileInvis(uint16 spell_id);
|
||||
bool IsEffectIgnoredInStacking(int spa);
|
||||
bool IsFocusLimit(int spa);
|
||||
bool SpellRequiresTarget(int targettype);
|
||||
bool SpellRequiresTarget(int target_type);
|
||||
bool IsVirusSpell(int32 spell_id);
|
||||
int GetViralMinSpreadTime(int32 spell_id);
|
||||
int GetViralMaxSpreadTime(int32 spell_id);
|
||||
int GetViralSpreadRange(int32 spell_id);
|
||||
bool IsInstrumentModAppliedToSpellEffect(int32 spell_id, int effect);
|
||||
uint32 GetProcLimitTimer(int32 spell_id, int proc_type);
|
||||
|
||||
int CalcPetHp(int levelb, int classb, int STA = 75);
|
||||
int GetSpellEffectDescNum(uint16 spell_id);
|
||||
|
||||
+560
-146
@@ -16,34 +16,36 @@
|
||||
|
||||
#include "string_util.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
#define snprintf _snprintf
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
#define snprintf _snprintf
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#endif
|
||||
|
||||
// original source:
|
||||
// https://github.com/facebook/folly/blob/master/folly/String.cpp
|
||||
//
|
||||
const std::string vStringFormat(const char* format, va_list args)
|
||||
const std::string vStringFormat(const char *format, va_list args)
|
||||
{
|
||||
std::string output;
|
||||
va_list tmpargs;
|
||||
va_list tmpargs;
|
||||
|
||||
va_copy(tmpargs,args);
|
||||
va_copy(tmpargs, args);
|
||||
int characters_used = vsnprintf(nullptr, 0, format, tmpargs);
|
||||
va_end(tmpargs);
|
||||
|
||||
@@ -51,7 +53,7 @@ const std::string vStringFormat(const char* format, va_list args)
|
||||
if (characters_used > 0) {
|
||||
output.resize(characters_used + 1);
|
||||
|
||||
va_copy(tmpargs,args);
|
||||
va_copy(tmpargs, args);
|
||||
characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs);
|
||||
va_end(tmpargs);
|
||||
|
||||
@@ -59,8 +61,9 @@ const std::string vStringFormat(const char* format, va_list args)
|
||||
|
||||
// We shouldn't have a format error by this point, but I can't imagine what error we
|
||||
// could have by this point. Still, return empty string;
|
||||
if (characters_used < 0)
|
||||
if (characters_used < 0) {
|
||||
output.clear();
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@@ -86,8 +89,9 @@ const std::string str_toupper(std::string s)
|
||||
const std::string ucfirst(std::string s)
|
||||
{
|
||||
std::string output = s;
|
||||
if (!s.empty())
|
||||
if (!s.empty()) {
|
||||
output[0] = static_cast<char>(::toupper(s[0]));
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -101,18 +105,20 @@ const std::string StringFormat(const char *format, ...)
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitString(const std::string &str, const char delim) {
|
||||
std::vector<std::string> SplitString(const std::string &str, const char delim)
|
||||
{
|
||||
std::vector<std::string> ret;
|
||||
std::string::size_type start = 0;
|
||||
auto end = str.find(delim);
|
||||
std::string::size_type start = 0;
|
||||
auto end = str.find(delim);
|
||||
while (end != std::string::npos) {
|
||||
ret.emplace_back(str, start, end - start);
|
||||
start = end + 1;
|
||||
end = str.find(delim, start);
|
||||
end = str.find(delim, start);
|
||||
}
|
||||
// this will catch the last word since the string is unlikely to end with a delimiter
|
||||
if (str.length() > start)
|
||||
if (str.length() > start) {
|
||||
ret.emplace_back(str, start, str.length() - start);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -152,14 +158,16 @@ std::string get_between(const std::string &s, std::string start_delim, std::stri
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string::size_type search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator)
|
||||
std::string::size_type
|
||||
search_deliminated_string(const std::string &haystack, const std::string &needle, const char deliminator)
|
||||
{
|
||||
// this shouldn't go out of bounds, even without obvious bounds checks
|
||||
auto pos = haystack.find(needle);
|
||||
while (pos != std::string::npos) {
|
||||
auto c = haystack[pos + needle.length()];
|
||||
if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator))
|
||||
if ((c == '\0' || c == deliminator) && (pos == 0 || haystack[pos - 1] == deliminator)) {
|
||||
return pos;
|
||||
}
|
||||
pos = haystack.find(needle, pos + needle.length());
|
||||
}
|
||||
return std::string::npos;
|
||||
@@ -179,7 +187,7 @@ std::string implode(std::string glue, std::vector<std::string> src)
|
||||
}
|
||||
|
||||
std::string final_output = output.str();
|
||||
final_output.resize (output.str().size () - glue.size());
|
||||
final_output.resize(output.str().size() - glue.size());
|
||||
|
||||
return final_output;
|
||||
}
|
||||
@@ -201,80 +209,83 @@ std::vector<std::string> wrap(std::vector<std::string> &src, std::string charact
|
||||
return new_vector;
|
||||
}
|
||||
|
||||
std::string EscapeString(const std::string &s) {
|
||||
std::string EscapeString(const std::string &s)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
size_t sz = s.length();
|
||||
for(size_t i = 0; i < sz; ++i) {
|
||||
size_t sz = s.length();
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
char c = s[i];
|
||||
switch(c) {
|
||||
case '\x00':
|
||||
ret += "\\x00";
|
||||
break;
|
||||
case '\n':
|
||||
ret += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
ret += "\\r";
|
||||
break;
|
||||
case '\\':
|
||||
ret += "\\\\";
|
||||
break;
|
||||
case '\'':
|
||||
ret += "\\'";
|
||||
break;
|
||||
case '\"':
|
||||
ret += "\\\"";
|
||||
break;
|
||||
case '\x1a':
|
||||
ret += "\\x1a";
|
||||
break;
|
||||
default:
|
||||
ret.push_back(c);
|
||||
break;
|
||||
switch (c) {
|
||||
case '\x00':
|
||||
ret += "\\x00";
|
||||
break;
|
||||
case '\n':
|
||||
ret += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
ret += "\\r";
|
||||
break;
|
||||
case '\\':
|
||||
ret += "\\\\";
|
||||
break;
|
||||
case '\'':
|
||||
ret += "\\'";
|
||||
break;
|
||||
case '\"':
|
||||
ret += "\\\"";
|
||||
break;
|
||||
case '\x1a':
|
||||
ret += "\\x1a";
|
||||
break;
|
||||
default:
|
||||
ret.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string EscapeString(const char *src, size_t sz) {
|
||||
std::string EscapeString(const char *src, size_t sz)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
for(size_t i = 0; i < sz; ++i) {
|
||||
for (size_t i = 0; i < sz; ++i) {
|
||||
char c = src[i];
|
||||
switch(c) {
|
||||
case '\x00':
|
||||
ret += "\\x00";
|
||||
break;
|
||||
case '\n':
|
||||
ret += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
ret += "\\r";
|
||||
break;
|
||||
case '\\':
|
||||
ret += "\\\\";
|
||||
break;
|
||||
case '\'':
|
||||
ret += "\\'";
|
||||
break;
|
||||
case '\"':
|
||||
ret += "\\\"";
|
||||
break;
|
||||
case '\x1a':
|
||||
ret += "\\x1a";
|
||||
break;
|
||||
default:
|
||||
ret.push_back(c);
|
||||
break;
|
||||
switch (c) {
|
||||
case '\x00':
|
||||
ret += "\\x00";
|
||||
break;
|
||||
case '\n':
|
||||
ret += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
ret += "\\r";
|
||||
break;
|
||||
case '\\':
|
||||
ret += "\\\\";
|
||||
break;
|
||||
case '\'':
|
||||
ret += "\\'";
|
||||
break;
|
||||
case '\"':
|
||||
ret += "\\\"";
|
||||
break;
|
||||
case '\x1a':
|
||||
ret += "\\x1a";
|
||||
break;
|
||||
default:
|
||||
ret.push_back(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool StringIsNumber(const std::string &s) {
|
||||
bool StringIsNumber(const std::string &s)
|
||||
{
|
||||
try {
|
||||
auto r = stod(s);
|
||||
return true;
|
||||
@@ -284,15 +295,18 @@ bool StringIsNumber(const std::string &s) {
|
||||
}
|
||||
}
|
||||
|
||||
void ToLowerString(std::string &s) {
|
||||
void ToLowerString(std::string &s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
|
||||
}
|
||||
|
||||
void ToUpperString(std::string &s) {
|
||||
void ToUpperString(std::string &s)
|
||||
{
|
||||
std::transform(s.begin(), s.end(), s.begin(), ::toupper);
|
||||
}
|
||||
|
||||
std::string JoinString(const std::vector<std::string>& ar, const std::string &delim) {
|
||||
std::string JoinString(const std::vector<std::string> &ar, const std::string &delim)
|
||||
{
|
||||
std::string ret;
|
||||
for (size_t i = 0; i < ar.size(); ++i) {
|
||||
if (i != 0) {
|
||||
@@ -312,7 +326,7 @@ void find_replace(std::string &string_subject, const std::string &search_string,
|
||||
}
|
||||
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) {
|
||||
while ((start_pos = string_subject.find(search_string, start_pos)) != std::string::npos) {
|
||||
string_subject.replace(start_pos, search_string.length(), replace_string);
|
||||
start_pos += replace_string.length();
|
||||
}
|
||||
@@ -333,9 +347,9 @@ void ParseAccountString(const std::string &s, std::string &account, std::string
|
||||
auto split = SplitString(s, ':');
|
||||
if (split.size() == 2) {
|
||||
loginserver = split[0];
|
||||
account = split[1];
|
||||
account = split[1];
|
||||
}
|
||||
else if(split.size() == 1) {
|
||||
else if (split.size() == 1) {
|
||||
account = split[0];
|
||||
}
|
||||
}
|
||||
@@ -344,9 +358,11 @@ void ParseAccountString(const std::string &s, std::string &account, std::string
|
||||
|
||||
// normal strncpy doesnt put a null term on copied strings, this one does
|
||||
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
|
||||
char* strn0cpy(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
char *strn0cpy(char *dest, const char *source, uint32 size)
|
||||
{
|
||||
if (!dest) {
|
||||
return 0;
|
||||
}
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return dest;
|
||||
@@ -358,123 +374,159 @@ char* strn0cpy(char* dest, const char* source, uint32 size) {
|
||||
|
||||
// String N w/null Copy Truncated?
|
||||
// return value =true if entire string(source) fit, false if it was truncated
|
||||
bool strn0cpyt(char* dest, const char* source, uint32 size) {
|
||||
if (!dest)
|
||||
bool strn0cpyt(char *dest, const char *source, uint32 size)
|
||||
{
|
||||
if (!dest) {
|
||||
return 0;
|
||||
}
|
||||
if (size == 0 || source == 0) {
|
||||
dest[0] = 0;
|
||||
return false;
|
||||
}
|
||||
strncpy(dest, source, size);
|
||||
dest[size - 1] = 0;
|
||||
return (bool)(source[strlen(dest)] == 0);
|
||||
return (bool) (source[strlen(dest)] == 0);
|
||||
}
|
||||
|
||||
const char *MakeLowerString(const char *source) {
|
||||
const char *MakeLowerString(const char *source)
|
||||
{
|
||||
static char str[128];
|
||||
if (!source)
|
||||
if (!source) {
|
||||
return nullptr;
|
||||
}
|
||||
MakeLowerString(source, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
void MakeLowerString(const char *source, char *target) {
|
||||
void MakeLowerString(const char *source, char *target)
|
||||
{
|
||||
if (!source || !target) {
|
||||
*target = 0;
|
||||
return;
|
||||
}
|
||||
while (*source)
|
||||
{
|
||||
while (*source) {
|
||||
*target = tolower(*source);
|
||||
target++; source++;
|
||||
target++;
|
||||
source++;
|
||||
}
|
||||
*target = 0;
|
||||
}
|
||||
|
||||
uint32 hextoi(const char* num) {
|
||||
if (num == nullptr)
|
||||
uint32 hextoi(const char *num)
|
||||
{
|
||||
if (num == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
if (len < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
uint32 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F') {
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
}
|
||||
else if (num[i] >= 'a' && num[i] <= 'f') {
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
}
|
||||
else if (num[i] >= '0' && num[i] <= '9') {
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 hextoi64(const char* num) {
|
||||
if (num == nullptr)
|
||||
uint64 hextoi64(const char *num)
|
||||
{
|
||||
if (num == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int len = strlen(num);
|
||||
if (len < 3)
|
||||
if (len < 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X'))
|
||||
if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F')
|
||||
uint64 ret = 0;
|
||||
int mul = 1;
|
||||
for (int i = len - 1; i >= 2; i--) {
|
||||
if (num[i] >= 'A' && num[i] <= 'F') {
|
||||
ret += ((num[i] - 'A') + 10) * mul;
|
||||
else if (num[i] >= 'a' && num[i] <= 'f')
|
||||
}
|
||||
else if (num[i] >= 'a' && num[i] <= 'f') {
|
||||
ret += ((num[i] - 'a') + 10) * mul;
|
||||
else if (num[i] >= '0' && num[i] <= '9')
|
||||
}
|
||||
else if (num[i] >= '0' && num[i] <= '9') {
|
||||
ret += (num[i] - '0') * mul;
|
||||
else
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
mul *= 16;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool atobool(const char* iBool) {
|
||||
bool atobool(const char *iBool)
|
||||
{
|
||||
|
||||
if (iBool == nullptr)
|
||||
if (iBool == nullptr) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "true"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "true")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "false"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "false")) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "yes"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "yes")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "no"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "no")) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "on"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "on")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "off"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "off")) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enable"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "enable")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disable"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "disable")) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "enabled"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "enabled")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "disabled"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "disabled")) {
|
||||
return false;
|
||||
if (!strcasecmp(iBool, "y"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "y")) {
|
||||
return true;
|
||||
if (!strcasecmp(iBool, "n"))
|
||||
}
|
||||
if (!strcasecmp(iBool, "n")) {
|
||||
return false;
|
||||
if (atoi(iBool))
|
||||
}
|
||||
if (atoi(iBool)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -483,21 +535,19 @@ char *CleanMobName(const char *in, char *out)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = j = 0; i < strlen(in); i++)
|
||||
{
|
||||
for (i = j = 0; i < strlen(in); i++) {
|
||||
// convert _ to space.. any other conversions like this? I *think* this
|
||||
// is the only non alpha char that's not stripped but converted.
|
||||
if (in[i] == '_')
|
||||
{
|
||||
if (in[i] == '_') {
|
||||
out[j++] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped
|
||||
else {
|
||||
if (isalpha(in[i]) || (in[i] == '`')) { // numbers, #, or any other crap just gets skipped
|
||||
out[j++] = in[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
out[j] = 0; // terimnate the string before returning it
|
||||
out[j] = 0; // terimnate the string before returning it
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -505,8 +555,9 @@ char *CleanMobName(const char *in, char *out)
|
||||
void RemoveApostrophes(std::string &s)
|
||||
{
|
||||
for (unsigned int i = 0; i < s.length(); ++i)
|
||||
if (s[i] == '\'')
|
||||
if (s[i] == '\'') {
|
||||
s[i] = '_';
|
||||
}
|
||||
}
|
||||
|
||||
char *RemoveApostrophes(const char *s)
|
||||
@@ -516,8 +567,9 @@ char *RemoveApostrophes(const char *s)
|
||||
strcpy(NewString, s);
|
||||
|
||||
for (unsigned int i = 0; i < strlen(NewString); ++i)
|
||||
if (NewString[i] == '\'')
|
||||
if (NewString[i] == '\'') {
|
||||
NewString[i] = '_';
|
||||
}
|
||||
|
||||
return NewString;
|
||||
}
|
||||
@@ -536,11 +588,12 @@ const char *ConvertArrayF(float input, char *returnchar)
|
||||
|
||||
bool isAlphaNumeric(const char *text)
|
||||
{
|
||||
for (unsigned int charIndex = 0; charIndex<strlen(text); charIndex++) {
|
||||
for (unsigned int charIndex = 0; charIndex < strlen(text); charIndex++) {
|
||||
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
|
||||
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
|
||||
(text[charIndex] < '0' || text[charIndex] > '9'))
|
||||
(text[charIndex] < '0' || text[charIndex] > '9')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -595,13 +648,374 @@ std::string numberToWords(unsigned long long int n)
|
||||
}
|
||||
|
||||
// first letter capitalized and rest made lower case
|
||||
std::string FormatName(const std::string& char_name)
|
||||
std::string FormatName(const std::string &char_name)
|
||||
{
|
||||
std::string formatted(char_name);
|
||||
if (!formatted.empty())
|
||||
{
|
||||
if (!formatted.empty()) {
|
||||
std::transform(formatted.begin(), formatted.end(), formatted.begin(), ::tolower);
|
||||
formatted[0] = ::toupper(formatted[0]);
|
||||
}
|
||||
return formatted;
|
||||
}
|
||||
|
||||
bool IsAllowedWorldServerCharacterList(char c)
|
||||
{
|
||||
const char *valid_characters = ":[](){}.!@#$%^&*-=+<>/\\|'\"";
|
||||
if (strchr(valid_characters, c)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SanitizeWorldServerName(char *name)
|
||||
{
|
||||
std::string server_long_name = name;
|
||||
|
||||
strcpy(name, SanitizeWorldServerName(server_long_name).c_str());
|
||||
}
|
||||
|
||||
std::string SanitizeWorldServerName(std::string server_long_name)
|
||||
{
|
||||
server_long_name.erase(
|
||||
std::remove_if(
|
||||
server_long_name.begin(),
|
||||
server_long_name.end(),
|
||||
[](char c) {
|
||||
return !(std::isalpha(c) || std::isalnum(c) || std::isspace(c) || IsAllowedWorldServerCharacterList(c));
|
||||
}
|
||||
), server_long_name.end()
|
||||
);
|
||||
|
||||
server_long_name = trim(server_long_name);
|
||||
|
||||
// bad word filter
|
||||
for (auto &piece: split_string(server_long_name, " ")) {
|
||||
for (auto &word: GetBadWords()) {
|
||||
// for shorter words that can actually be part of legitimate words
|
||||
// make sure that it isn't part of another word by matching on a space
|
||||
if (str_tolower(piece) == word) {
|
||||
find_replace(
|
||||
server_long_name,
|
||||
piece,
|
||||
repeat("*", (int) word.length())
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto pos = str_tolower(piece).find(word);
|
||||
if (str_tolower(piece).find(word) != std::string::npos && piece.length() > 4 && word.length() > 4) {
|
||||
auto found_word = piece.substr(pos, word.length());
|
||||
std::string replaced_piece = piece.substr(pos, word.length());
|
||||
|
||||
find_replace(
|
||||
server_long_name,
|
||||
replaced_piece,
|
||||
repeat("*", (int) word.length())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return server_long_name;
|
||||
}
|
||||
|
||||
std::string repeat(std::string s, int n)
|
||||
{
|
||||
std::string s1 = s;
|
||||
for (int i = 1; i < n; i++) {
|
||||
s += s1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetBadWords()
|
||||
{
|
||||
return std::vector<std::string>{
|
||||
"2g1c",
|
||||
"acrotomophilia",
|
||||
"anal",
|
||||
"anilingus",
|
||||
"anus",
|
||||
"apeshit",
|
||||
"arsehole",
|
||||
"ass",
|
||||
"asshole",
|
||||
"assmunch",
|
||||
"autoerotic",
|
||||
"babeland",
|
||||
"bangbros",
|
||||
"bangbus",
|
||||
"bareback",
|
||||
"barenaked",
|
||||
"bastard",
|
||||
"bastardo",
|
||||
"bastinado",
|
||||
"bbw",
|
||||
"bdsm",
|
||||
"beaner",
|
||||
"beaners",
|
||||
"beaver",
|
||||
"beastiality",
|
||||
"bestiality",
|
||||
"bimbos",
|
||||
"birdlock",
|
||||
"bitch",
|
||||
"bitches",
|
||||
"blowjob",
|
||||
"blumpkin",
|
||||
"bollocks",
|
||||
"bondage",
|
||||
"boner",
|
||||
"boob",
|
||||
"boobs",
|
||||
"bukkake",
|
||||
"bulldyke",
|
||||
"bullshit",
|
||||
"bung",
|
||||
"bunghole",
|
||||
"busty",
|
||||
"butt",
|
||||
"buttcheeks",
|
||||
"butthole",
|
||||
"camel toe",
|
||||
"camgirl",
|
||||
"camslut",
|
||||
"camwhore",
|
||||
"carpetmuncher",
|
||||
"cialis",
|
||||
"circlejerk",
|
||||
"clit",
|
||||
"clitoris",
|
||||
"clusterfuck",
|
||||
"cock",
|
||||
"cocks",
|
||||
"coprolagnia",
|
||||
"coprophilia",
|
||||
"cornhole",
|
||||
"coon",
|
||||
"coons",
|
||||
"creampie",
|
||||
"cum",
|
||||
"cumming",
|
||||
"cumshot",
|
||||
"cumshots",
|
||||
"cunnilingus",
|
||||
"cunt",
|
||||
"darkie",
|
||||
"daterape",
|
||||
"deepthroat",
|
||||
"dendrophilia",
|
||||
"dick",
|
||||
"dildo",
|
||||
"dingleberry",
|
||||
"dingleberries",
|
||||
"doggiestyle",
|
||||
"doggystyle",
|
||||
"dolcett",
|
||||
"domination",
|
||||
"dominatrix",
|
||||
"dommes",
|
||||
"hump",
|
||||
"dvda",
|
||||
"ecchi",
|
||||
"ejaculation",
|
||||
"erotic",
|
||||
"erotism",
|
||||
"escort",
|
||||
"eunuch",
|
||||
"fag",
|
||||
"faggot",
|
||||
"fecal",
|
||||
"felch",
|
||||
"fellatio",
|
||||
"feltch",
|
||||
"femdom",
|
||||
"figging",
|
||||
"fingerbang",
|
||||
"fingering",
|
||||
"fisting",
|
||||
"footjob",
|
||||
"frotting",
|
||||
"fuck",
|
||||
"fuckin",
|
||||
"fucking",
|
||||
"fucktards",
|
||||
"fudgepacker",
|
||||
"futanari",
|
||||
"gangbang",
|
||||
"gangbang",
|
||||
"gaysex",
|
||||
"genitals",
|
||||
"goatcx",
|
||||
"goatse",
|
||||
"gokkun",
|
||||
"goodpoop",
|
||||
"goregasm",
|
||||
"grope",
|
||||
"g-spot",
|
||||
"guro",
|
||||
"handjob",
|
||||
"hentai",
|
||||
"homoerotic",
|
||||
"honkey",
|
||||
"hooker",
|
||||
"horny",
|
||||
"humping",
|
||||
"incest",
|
||||
"intercourse",
|
||||
"jailbait",
|
||||
"jigaboo",
|
||||
"jiggaboo",
|
||||
"jiggerboo",
|
||||
"jizz",
|
||||
"juggs",
|
||||
"kike",
|
||||
"kinbaku",
|
||||
"kinkster",
|
||||
"kinky",
|
||||
"knobbing",
|
||||
"livesex",
|
||||
"lolita",
|
||||
"lovemaking",
|
||||
"masturbate",
|
||||
"masturbating",
|
||||
"masturbation",
|
||||
"milf",
|
||||
"mong",
|
||||
"motherfucker",
|
||||
"muffdiving",
|
||||
"nambla",
|
||||
"nawashi",
|
||||
"negro",
|
||||
"neonazi",
|
||||
"nigga",
|
||||
"nigger",
|
||||
"nimphomania",
|
||||
"nipple",
|
||||
"nipples",
|
||||
"nsfw",
|
||||
"nude",
|
||||
"nudity",
|
||||
"nutten",
|
||||
"nympho",
|
||||
"nymphomania",
|
||||
"octopussy",
|
||||
"omorashi",
|
||||
"orgasm",
|
||||
"orgy",
|
||||
"paedophile",
|
||||
"paki",
|
||||
"panties",
|
||||
"panty",
|
||||
"pedobear",
|
||||
"pedophile",
|
||||
"pegging",
|
||||
"penis",
|
||||
"pikey",
|
||||
"pissing",
|
||||
"pisspig",
|
||||
"playboy",
|
||||
"ponyplay",
|
||||
"poof",
|
||||
"poon",
|
||||
"poontang",
|
||||
"punany",
|
||||
"poopchute",
|
||||
"porn",
|
||||
"porno",
|
||||
"pornography",
|
||||
"pthc",
|
||||
"pubes",
|
||||
"pussy",
|
||||
"queaf",
|
||||
"queef",
|
||||
"quim",
|
||||
"raghead",
|
||||
"rape",
|
||||
"raping",
|
||||
"rapist",
|
||||
"rectum",
|
||||
"rimjob",
|
||||
"rimming",
|
||||
"sadism",
|
||||
"santorum",
|
||||
"scat",
|
||||
"schlong",
|
||||
"scissoring",
|
||||
"semen",
|
||||
"sex",
|
||||
"sexcam",
|
||||
"sexo",
|
||||
"sexy",
|
||||
"sexual",
|
||||
"sexually",
|
||||
"sexuality",
|
||||
"shemale",
|
||||
"shibari",
|
||||
"shit",
|
||||
"shitblimp",
|
||||
"shitty",
|
||||
"shota",
|
||||
"shrimping",
|
||||
"skeet",
|
||||
"slanteye",
|
||||
"slut",
|
||||
"s&m",
|
||||
"smut",
|
||||
"snatch",
|
||||
"snowballing",
|
||||
"sodomize",
|
||||
"sodomy",
|
||||
"spastic",
|
||||
"spic",
|
||||
"splooge",
|
||||
"spooge",
|
||||
"spunk",
|
||||
"strapon",
|
||||
"strappado",
|
||||
"suck",
|
||||
"sucks",
|
||||
"swastika",
|
||||
"swinger",
|
||||
"threesome",
|
||||
"throating",
|
||||
"thumbzilla",
|
||||
"tight white",
|
||||
"tit",
|
||||
"tits",
|
||||
"titties",
|
||||
"titty",
|
||||
"topless",
|
||||
"tosser",
|
||||
"towelhead",
|
||||
"tranny",
|
||||
"tribadism",
|
||||
"tubgirl",
|
||||
"tushy",
|
||||
"twat",
|
||||
"twink",
|
||||
"twinkie",
|
||||
"undressing",
|
||||
"upskirt",
|
||||
"urophilia",
|
||||
"vagina",
|
||||
"viagra",
|
||||
"vibrator",
|
||||
"vorarephilia",
|
||||
"voyeur",
|
||||
"voyeurweb",
|
||||
"voyuer",
|
||||
"vulva",
|
||||
"wank",
|
||||
"wetback",
|
||||
"whore",
|
||||
"worldsex",
|
||||
"xx",
|
||||
"xxx",
|
||||
"yaoi",
|
||||
"yiffy",
|
||||
"zoophilia"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -208,6 +208,11 @@ void RemoveApostrophes(std::string &s);
|
||||
std::string convert2digit(int n, std::string suffix);
|
||||
std::string numberToWords(unsigned long long int n);
|
||||
std::string FormatName(const std::string& char_name);
|
||||
bool IsAllowedWorldServerCharacterList(char c);
|
||||
void SanitizeWorldServerName(char *name);
|
||||
std::string SanitizeWorldServerName(std::string server_long_name);
|
||||
std::string repeat(std::string s, int n);
|
||||
std::vector<std::string> GetBadWords();
|
||||
|
||||
template<typename InputIterator, typename OutputIterator>
|
||||
auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9172
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9174
|
||||
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028
|
||||
|
||||
@@ -22,7 +22,7 @@ SET(eqlogin_headers
|
||||
loginserver_command_handler.h
|
||||
loginserver_webserver.h
|
||||
login_server.h
|
||||
login_structures.h
|
||||
login_types.h
|
||||
options.h
|
||||
server_manager.h
|
||||
world_server.h
|
||||
|
||||
@@ -1,28 +1,9 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "account_management.h"
|
||||
#include "login_server.h"
|
||||
#include "../common/event/task_scheduler.h"
|
||||
#include "../common/event/event_loop.h"
|
||||
#include "../common/net/dns.h"
|
||||
#include "../common/string_util.h"
|
||||
|
||||
extern LoginServer server;
|
||||
EQ::Event::TaskScheduler task_runner;
|
||||
@@ -300,6 +281,8 @@ bool AccountManagement::UpdateLoginserverWorldAdminAccountPasswordById(
|
||||
return updated_account;
|
||||
}
|
||||
|
||||
constexpr int REQUEST_TIMEOUT_MS = 1500;
|
||||
|
||||
/**
|
||||
* @param in_account_username
|
||||
* @param in_account_password
|
||||
@@ -395,19 +378,33 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
|
||||
}
|
||||
);
|
||||
|
||||
EQ::Net::DNSLookup(
|
||||
"login.eqemulator.net", 5999, false, [&](const std::string &addr) {
|
||||
if (addr.empty()) {
|
||||
ret = 0;
|
||||
running = false;
|
||||
}
|
||||
auto s = SplitString(server.options.GetEQEmuLoginServerAddress(), ':');
|
||||
if (s.size() == 2) {
|
||||
auto address = s[0];
|
||||
auto port = std::stoi(s[1]);
|
||||
|
||||
mgr.Connect(addr, 5999);
|
||||
}
|
||||
);
|
||||
EQ::Net::DNSLookup(
|
||||
address, port, false, [&](const std::string &addr) {
|
||||
if (addr.empty()) {
|
||||
ret = 0;
|
||||
running = false;
|
||||
}
|
||||
|
||||
mgr.Connect(addr, port);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
||||
|
||||
auto &loop = EQ::EventLoop::Get();
|
||||
while (running) {
|
||||
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > REQUEST_TIMEOUT_MS) {
|
||||
LogInfo("[CheckExternalLoginserverUserCredentials] Deadline exceeded [{}]", REQUEST_TIMEOUT_MS);
|
||||
running = false;
|
||||
}
|
||||
|
||||
loop.Process();
|
||||
}
|
||||
|
||||
@@ -416,4 +413,115 @@ uint32 AccountManagement::CheckExternalLoginserverUserCredentials(
|
||||
);
|
||||
|
||||
return res.get();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 AccountManagement::HealthCheckUserLogin()
|
||||
{
|
||||
std::string in_account_username = "healthcheckuser";
|
||||
std::string in_account_password = "healthcheckpassword";
|
||||
|
||||
auto res = task_runner.Enqueue(
|
||||
[&]() -> uint32 {
|
||||
bool running = true;
|
||||
uint32 ret = 0;
|
||||
|
||||
EQ::Net::DaybreakConnectionManager mgr;
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> c;
|
||||
|
||||
mgr.OnNewConnection(
|
||||
[&](std::shared_ptr<EQ::Net::DaybreakConnection> connection) {
|
||||
c = connection;
|
||||
}
|
||||
);
|
||||
|
||||
mgr.OnConnectionStateChange(
|
||||
[&](
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> conn,
|
||||
EQ::Net::DbProtocolStatus from,
|
||||
EQ::Net::DbProtocolStatus to
|
||||
) {
|
||||
if (EQ::Net::StatusConnected == to) {
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.PutUInt16(0, 1); //OP_SessionReady
|
||||
p.PutUInt32(2, 2);
|
||||
c->QueuePacket(p);
|
||||
}
|
||||
else if (EQ::Net::StatusDisconnected == to) {
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
mgr.OnPacketRecv(
|
||||
[&](std::shared_ptr<EQ::Net::DaybreakConnection> conn, const EQ::Net::Packet &p) {
|
||||
auto opcode = p.GetUInt16(0);
|
||||
switch (opcode) {
|
||||
case 0x0017: //OP_ChatMessage
|
||||
{
|
||||
size_t buffer_len =
|
||||
in_account_username.length() + in_account_password.length() + 2;
|
||||
|
||||
std::unique_ptr<char[]> buffer(new char[buffer_len]);
|
||||
|
||||
strcpy(&buffer[0], in_account_username.c_str());
|
||||
strcpy(&buffer[in_account_username.length() + 1], in_account_password.c_str());
|
||||
|
||||
size_t encrypted_len = buffer_len;
|
||||
|
||||
if (encrypted_len % 8 > 0) {
|
||||
encrypted_len = ((encrypted_len / 8) + 1) * 8;
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket p;
|
||||
p.Resize(12 + encrypted_len);
|
||||
p.PutUInt16(0, 2); //OP_Login
|
||||
p.PutUInt32(2, 3);
|
||||
|
||||
eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true);
|
||||
c->QueuePacket(p);
|
||||
break;
|
||||
}
|
||||
case 0x0018: {
|
||||
auto encrypt_size = p.Length() - 12;
|
||||
if (encrypt_size % 8 > 0) {
|
||||
encrypt_size = (encrypt_size / 8) * 8;
|
||||
}
|
||||
|
||||
std::unique_ptr<char[]> decrypted(new char[encrypt_size]);
|
||||
|
||||
eqcrypt_block((char *) p.Data() + 12, encrypt_size, &decrypted[0], false);
|
||||
|
||||
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
|
||||
auto response_error = sp.GetUInt16(1);
|
||||
|
||||
{
|
||||
// we only care to see the response code
|
||||
ret = response_error;
|
||||
running = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
mgr.Connect("127.0.0.1", 5999);
|
||||
|
||||
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
|
||||
auto &loop = EQ::EventLoop::Get();
|
||||
while (running) {
|
||||
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() > 2000) {
|
||||
ret = 0;
|
||||
running = false;
|
||||
}
|
||||
|
||||
loop.Process();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
);
|
||||
|
||||
return res.get();
|
||||
}
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_ACCOUNT_MANAGEMENT_H
|
||||
#define EQEMU_ACCOUNT_MANAGEMENT_H
|
||||
|
||||
@@ -108,6 +89,8 @@ public:
|
||||
uint32 in_account_id,
|
||||
const std::string &in_account_password_hash
|
||||
);
|
||||
|
||||
static uint32 HealthCheckUserLogin();
|
||||
};
|
||||
|
||||
|
||||
|
||||
+150
-199
@@ -1,29 +1,10 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "client.h"
|
||||
#include "login_server.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "encryption.h"
|
||||
#include "account_management.h"
|
||||
|
||||
extern LoginServer server;
|
||||
|
||||
@@ -33,17 +14,17 @@ extern LoginServer server;
|
||||
*/
|
||||
Client::Client(std::shared_ptr<EQStreamInterface> c, LSClientVersion v)
|
||||
{
|
||||
connection = c;
|
||||
version = v;
|
||||
status = cs_not_sent_session_ready;
|
||||
account_id = 0;
|
||||
play_server_id = 0;
|
||||
play_sequence_id = 0;
|
||||
m_connection = c;
|
||||
m_client_version = v;
|
||||
m_client_status = cs_not_sent_session_ready;
|
||||
m_account_id = 0;
|
||||
m_play_server_id = 0;
|
||||
m_play_sequence_id = 0;
|
||||
}
|
||||
|
||||
bool Client::Process()
|
||||
{
|
||||
EQApplicationPacket *app = connection->PopPacket();
|
||||
EQApplicationPacket *app = m_connection->PopPacket();
|
||||
while (app) {
|
||||
if (server.options.IsTraceOn()) {
|
||||
LogDebug("Application packet received from client (size {0})", app->Size());
|
||||
@@ -53,9 +34,9 @@ bool Client::Process()
|
||||
DumpPacket(app);
|
||||
}
|
||||
|
||||
if (status == cs_failed_to_login) {
|
||||
if (m_client_status == cs_failed_to_login) {
|
||||
delete app;
|
||||
app = connection->PopPacket();
|
||||
app = m_connection->PopPacket();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -112,7 +93,7 @@ bool Client::Process()
|
||||
}
|
||||
|
||||
delete app;
|
||||
app = connection->PopPacket();
|
||||
app = m_connection->PopPacket();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -126,7 +107,7 @@ bool Client::Process()
|
||||
*/
|
||||
void Client::Handle_SessionReady(const char *data, unsigned int size)
|
||||
{
|
||||
if (status != cs_not_sent_session_ready) {
|
||||
if (m_client_status != cs_not_sent_session_ready) {
|
||||
LogError("Session ready received again after already being received");
|
||||
return;
|
||||
}
|
||||
@@ -136,39 +117,23 @@ void Client::Handle_SessionReady(const char *data, unsigned int size)
|
||||
return;
|
||||
}
|
||||
|
||||
status = cs_waiting_for_login;
|
||||
m_client_status = cs_waiting_for_login;
|
||||
|
||||
/**
|
||||
* The packets are mostly the same but slightly different between the two versions
|
||||
* The packets are identical between the two versions
|
||||
*/
|
||||
if (version == cv_sod) {
|
||||
auto *outapp = new EQApplicationPacket(OP_ChatMessage, 17);
|
||||
outapp->pBuffer[0] = 0x02;
|
||||
outapp->pBuffer[10] = 0x01;
|
||||
outapp->pBuffer[11] = 0x65;
|
||||
auto *outapp = new EQApplicationPacket(OP_ChatMessage, sizeof(LoginHandShakeReply_Struct));
|
||||
auto buf = reinterpret_cast<LoginHandShakeReply_Struct*>(outapp->pBuffer);
|
||||
buf->base_header.sequence = 0x02;
|
||||
buf->base_reply.success = true;
|
||||
buf->base_reply.error_str_id = 0x65; // 101 "No Error"
|
||||
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(outapp);
|
||||
}
|
||||
|
||||
connection->QueuePacket(outapp);
|
||||
delete outapp;
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(outapp);
|
||||
}
|
||||
else {
|
||||
const char *msg = "ChatMessage";
|
||||
auto *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg));
|
||||
outapp->pBuffer[0] = 0x02;
|
||||
outapp->pBuffer[10] = 0x01;
|
||||
outapp->pBuffer[11] = 0x65;
|
||||
strcpy((char *) (outapp->pBuffer + 15), msg);
|
||||
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(outapp);
|
||||
}
|
||||
|
||||
connection->QueuePacket(outapp);
|
||||
delete outapp;
|
||||
}
|
||||
m_connection->QueuePacket(outapp);
|
||||
delete outapp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,19 +144,23 @@ void Client::Handle_SessionReady(const char *data, unsigned int size)
|
||||
*/
|
||||
void Client::Handle_Login(const char *data, unsigned int size)
|
||||
{
|
||||
if (status != cs_waiting_for_login) {
|
||||
if (m_client_status != cs_waiting_for_login) {
|
||||
LogError("Login received after already having logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((size - 12) % 8 != 0) {
|
||||
LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size);
|
||||
// login user/pass are variable length after unencrypted opcode and base message header (size includes opcode)
|
||||
constexpr int header_size = sizeof(uint16_t) + sizeof(LoginBaseMessage_Struct);
|
||||
int data_size = size - header_size;
|
||||
|
||||
if (size <= header_size) {
|
||||
LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < sizeof(LoginLoginRequest_Struct)) {
|
||||
LogError("Login received packet of size: {0}, this would cause a buffer overflow, discarding", size);
|
||||
if (data_size % 8 != 0) {
|
||||
LogError("Login received packet of size: {0}, this would cause a block corruption, discarding", size);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -208,13 +177,14 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
std::string db_account_password_hash;
|
||||
|
||||
std::string outbuffer;
|
||||
outbuffer.resize(size - 12);
|
||||
outbuffer.resize(data_size);
|
||||
if (outbuffer.length() == 0) {
|
||||
LogError("Corrupt buffer sent to server, no length");
|
||||
return;
|
||||
}
|
||||
|
||||
auto r = eqcrypt_block(data + 10, size - 12, &outbuffer[0], 0);
|
||||
// data starts at base message header (opcode not included)
|
||||
auto r = eqcrypt_block(data + sizeof(LoginBaseMessage_Struct), data_size, &outbuffer[0], 0);
|
||||
if (r == nullptr) {
|
||||
LogError("Failed to decrypt eqcrypt block");
|
||||
return;
|
||||
@@ -228,7 +198,8 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&llrs, data, sizeof(LoginLoginRequest_Struct));
|
||||
// only need to copy the base header for reply options, ignore login info
|
||||
memcpy(&m_llrs, data, sizeof(LoginBaseMessage_Struct));
|
||||
|
||||
bool result = false;
|
||||
if (outbuffer[0] == 0 && outbuffer[1] == 0) {
|
||||
@@ -236,7 +207,7 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
cred = (&outbuffer[2 + user.length()]);
|
||||
result = server.db->GetLoginTokenDataFromToken(
|
||||
cred,
|
||||
connection->GetRemoteAddr(),
|
||||
m_connection->GetRemoteAddr(),
|
||||
db_account_id,
|
||||
db_loginserver,
|
||||
user
|
||||
@@ -252,11 +223,16 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
user = components[1];
|
||||
}
|
||||
|
||||
// health checks
|
||||
if (ProcessHealthCheck(user)) {
|
||||
DoFailedLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Attempting password based login [{0}] login [{1}] user [{2}]",
|
||||
"Attempting password based login [{0}] login [{1}]",
|
||||
user,
|
||||
db_loginserver,
|
||||
user
|
||||
db_loginserver
|
||||
);
|
||||
|
||||
ParseAccountString(user, user, db_loginserver);
|
||||
@@ -267,7 +243,7 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
LogDebug("[VerifyLoginHash] Success [{0}]", (result ? "true" : "false"));
|
||||
}
|
||||
else {
|
||||
status = cs_creating_account;
|
||||
m_client_status = cs_creating_account;
|
||||
AttemptLoginAccountCreation(user, cred, db_loginserver);
|
||||
|
||||
return;
|
||||
@@ -305,14 +281,14 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
||||
*/
|
||||
void Client::Handle_Play(const char *data)
|
||||
{
|
||||
if (status != cs_logged_in) {
|
||||
if (m_client_status != cs_logged_in) {
|
||||
LogError("Client sent a play request when they were not logged in, discarding");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *play = (const PlayEverquestRequest_Struct *) data;
|
||||
auto server_id_in = (unsigned int) play->ServerNumber;
|
||||
auto sequence_in = (unsigned int) play->Sequence;
|
||||
auto server_id_in = (unsigned int) play->server_number;
|
||||
auto sequence_in = (unsigned int) play->base_header.sequence;
|
||||
|
||||
if (server.options.IsTraceOn()) {
|
||||
LogInfo(
|
||||
@@ -323,10 +299,10 @@ void Client::Handle_Play(const char *data)
|
||||
);
|
||||
}
|
||||
|
||||
this->play_server_id = (unsigned int) play->ServerNumber;
|
||||
play_sequence_id = sequence_in;
|
||||
play_server_id = server_id_in;
|
||||
server.server_manager->SendUserToWorldRequest(server_id_in, account_id, loginserver_name);
|
||||
m_play_server_id = (unsigned int) play->server_number;
|
||||
m_play_sequence_id = sequence_in;
|
||||
m_play_server_id = server_id_in;
|
||||
server.server_manager->SendUserToWorldRequest(server_id_in, m_account_id, m_loginserver_name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,14 +310,13 @@ void Client::Handle_Play(const char *data)
|
||||
*/
|
||||
void Client::SendServerListPacket(uint32 seq)
|
||||
{
|
||||
EQApplicationPacket *outapp = server.server_manager->CreateServerListPacket(this, seq);
|
||||
auto outapp = server.server_manager->CreateServerListPacket(this, seq);
|
||||
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(outapp);
|
||||
DumpPacket(outapp.get());
|
||||
}
|
||||
|
||||
connection->QueuePacket(outapp);
|
||||
delete outapp;
|
||||
m_connection->QueuePacket(outapp.get());
|
||||
}
|
||||
|
||||
void Client::SendPlayResponse(EQApplicationPacket *outapp)
|
||||
@@ -350,12 +325,12 @@ void Client::SendPlayResponse(EQApplicationPacket *outapp)
|
||||
LogDebug("Sending play response for {0}", GetAccountName());
|
||||
// server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size);
|
||||
}
|
||||
connection->QueuePacket(outapp);
|
||||
m_connection->QueuePacket(outapp);
|
||||
}
|
||||
|
||||
void Client::GenerateKey()
|
||||
{
|
||||
key.clear();
|
||||
m_key.clear();
|
||||
int count = 0;
|
||||
while (count < 10) {
|
||||
static const char key_selection[] =
|
||||
@@ -367,7 +342,7 @@ void Client::GenerateKey()
|
||||
'6', '7', '8', '9'
|
||||
};
|
||||
|
||||
key.append((const char *) &key_selection[random.Int(0, 35)], 1);
|
||||
m_key.append((const char *) &key_selection[m_random.Int(0, 35)], 1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -383,6 +358,8 @@ void Client::AttemptLoginAccountCreation(
|
||||
const std::string &loginserver
|
||||
)
|
||||
{
|
||||
LogInfo("[AttemptLoginAccountCreation] user [{}] loginserver [{}]", user, loginserver);
|
||||
|
||||
#ifdef LSPX
|
||||
if (loginserver == "eqemu") {
|
||||
LogInfo("Attempting login account creation via '{0}'", loginserver);
|
||||
@@ -393,59 +370,19 @@ void Client::AttemptLoginAccountCreation(
|
||||
return;
|
||||
}
|
||||
|
||||
if (server.options.GetEQEmuLoginServerAddress().length() == 0) {
|
||||
DoFailedLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
auto addr_components = SplitString(server.options.GetEQEmuLoginServerAddress(), ':');
|
||||
if (addr_components.size() != 2) {
|
||||
DoFailedLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
stored_user = user;
|
||||
stored_pass = pass;
|
||||
|
||||
auto address = addr_components[0];
|
||||
auto port = std::stoi(addr_components[1]);
|
||||
EQ::Net::DNSLookup(
|
||||
address, port, false, [=](const std::string &addr) {
|
||||
if (addr.empty()) {
|
||||
DoFailedLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
login_connection_manager.reset(new EQ::Net::DaybreakConnectionManager());
|
||||
login_connection_manager->OnNewConnection(
|
||||
std::bind(
|
||||
&Client::LoginOnNewConnection,
|
||||
this,
|
||||
std::placeholders::_1
|
||||
)
|
||||
);
|
||||
login_connection_manager->OnConnectionStateChange(
|
||||
std::bind(
|
||||
&Client::LoginOnStatusChange,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2,
|
||||
std::placeholders::_3
|
||||
)
|
||||
);
|
||||
login_connection_manager->OnPacketRecv(
|
||||
std::bind(
|
||||
&Client::LoginOnPacketRecv,
|
||||
this,
|
||||
std::placeholders::_1,
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
|
||||
login_connection_manager->Connect(addr, port);
|
||||
}
|
||||
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(
|
||||
user,
|
||||
pass
|
||||
);
|
||||
|
||||
if (account_id > 0) {
|
||||
LogInfo("[AttemptLoginAccountCreation] Found and creating eqemu account [{}]", account_id);
|
||||
CreateEQEmuAccount(user, pass, account_id);
|
||||
return;
|
||||
}
|
||||
|
||||
DoFailedLogin();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -461,26 +398,36 @@ void Client::AttemptLoginAccountCreation(
|
||||
|
||||
void Client::DoFailedLogin()
|
||||
{
|
||||
stored_user.clear();
|
||||
stored_pass.clear();
|
||||
m_stored_user.clear();
|
||||
m_stored_pass.clear();
|
||||
|
||||
EQApplicationPacket outapp(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct));
|
||||
auto *login_failed = (LoginLoginFailed_Struct *) outapp.pBuffer;
|
||||
// unencrypted
|
||||
LoginBaseMessage_Struct base_header{};
|
||||
base_header.sequence = m_llrs.sequence; // login (3)
|
||||
base_header.encrypt_type = m_llrs.encrypt_type;
|
||||
|
||||
login_failed->unknown1 = llrs.unknown1;
|
||||
login_failed->unknown2 = llrs.unknown2;
|
||||
login_failed->unknown3 = llrs.unknown3;
|
||||
login_failed->unknown4 = llrs.unknown4;
|
||||
login_failed->unknown5 = llrs.unknown5;
|
||||
// encrypted
|
||||
PlayerLoginReply_Struct login_reply{};
|
||||
login_reply.base_reply.success = false;
|
||||
login_reply.base_reply.error_str_id = 105; // Error - The username and/or password were not valid
|
||||
|
||||
memcpy(login_failed->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData));
|
||||
char encrypted_buffer[80] = {0};
|
||||
auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block for failed login");
|
||||
}
|
||||
|
||||
constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer);
|
||||
EQApplicationPacket outapp(OP_LoginAccepted, outsize);
|
||||
outapp.WriteData(&base_header, sizeof(base_header));
|
||||
outapp.WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(&outapp);
|
||||
}
|
||||
|
||||
connection->QueuePacket(&outapp);
|
||||
status = cs_failed_to_login;
|
||||
m_connection->QueuePacket(&outapp);
|
||||
m_client_status = cs_failed_to_login;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,68 +523,64 @@ void Client::DoSuccessfulLogin(
|
||||
const std::string &db_loginserver
|
||||
)
|
||||
{
|
||||
stored_user.clear();
|
||||
stored_pass.clear();
|
||||
m_stored_user.clear();
|
||||
m_stored_pass.clear();
|
||||
|
||||
server.client_manager->RemoveExistingClient(db_account_id, db_loginserver);
|
||||
|
||||
in_addr in{};
|
||||
in.s_addr = connection->GetRemoteIP();
|
||||
in.s_addr = m_connection->GetRemoteIP();
|
||||
|
||||
server.db->UpdateLSAccountData(db_account_id, std::string(inet_ntoa(in)));
|
||||
GenerateKey();
|
||||
|
||||
account_id = db_account_id;
|
||||
account_name = in_account_name;
|
||||
loginserver_name = db_loginserver;
|
||||
m_account_id = db_account_id;
|
||||
m_account_name = in_account_name;
|
||||
m_loginserver_name = db_loginserver;
|
||||
|
||||
auto *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80);
|
||||
auto *login_accepted = (LoginAccepted_Struct *) outapp->pBuffer;
|
||||
login_accepted->unknown1 = llrs.unknown1;
|
||||
login_accepted->unknown2 = llrs.unknown2;
|
||||
login_accepted->unknown3 = llrs.unknown3;
|
||||
login_accepted->unknown4 = llrs.unknown4;
|
||||
login_accepted->unknown5 = llrs.unknown5;
|
||||
// unencrypted
|
||||
LoginBaseMessage_Struct base_header{};
|
||||
base_header.sequence = m_llrs.sequence;
|
||||
base_header.compressed = false;
|
||||
base_header.encrypt_type = m_llrs.encrypt_type;
|
||||
base_header.unk3 = m_llrs.unk3;
|
||||
|
||||
auto *login_failed_attempts = new LoginFailedAttempts_Struct;
|
||||
memset(login_failed_attempts, 0, sizeof(LoginFailedAttempts_Struct));
|
||||
|
||||
login_failed_attempts->failed_attempts = 0;
|
||||
login_failed_attempts->message = 0x01;
|
||||
login_failed_attempts->lsid = db_account_id;
|
||||
login_failed_attempts->unknown3[3] = 0x03;
|
||||
login_failed_attempts->unknown4[3] = 0x02;
|
||||
login_failed_attempts->unknown5[0] = 0xe7;
|
||||
login_failed_attempts->unknown5[1] = 0x03;
|
||||
login_failed_attempts->unknown6[0] = 0xff;
|
||||
login_failed_attempts->unknown6[1] = 0xff;
|
||||
login_failed_attempts->unknown6[2] = 0xff;
|
||||
login_failed_attempts->unknown6[3] = 0xff;
|
||||
login_failed_attempts->unknown7[0] = 0xa0;
|
||||
login_failed_attempts->unknown7[1] = 0x05;
|
||||
login_failed_attempts->unknown8[3] = 0x02;
|
||||
login_failed_attempts->unknown9[0] = 0xff;
|
||||
login_failed_attempts->unknown9[1] = 0x03;
|
||||
login_failed_attempts->unknown11[0] = 0x63;
|
||||
login_failed_attempts->unknown12[0] = 0x01;
|
||||
memcpy(login_failed_attempts->key, key.c_str(), key.size());
|
||||
// not serializing any of the variable length strings so just use struct directly
|
||||
PlayerLoginReply_Struct login_reply{};
|
||||
login_reply.base_reply.success = true;
|
||||
login_reply.base_reply.error_str_id = 101; // No Error
|
||||
login_reply.unk1 = 0;
|
||||
login_reply.unk2 = 0;
|
||||
login_reply.lsid = db_account_id;
|
||||
login_reply.failed_attempts = 0;
|
||||
login_reply.show_player_count = server.options.IsShowPlayerCountEnabled();
|
||||
login_reply.offer_min_days = 99;
|
||||
login_reply.offer_min_views = -1;
|
||||
login_reply.offer_cooldown_minutes = 0;
|
||||
login_reply.web_offer_number = 0;
|
||||
login_reply.web_offer_min_days = 99;
|
||||
login_reply.web_offer_min_views = -1;
|
||||
login_reply.web_offer_cooldown_minutes = 0;
|
||||
memcpy(login_reply.key, m_key.c_str(), m_key.size());
|
||||
|
||||
char encrypted_buffer[80] = {0};
|
||||
auto rc = eqcrypt_block((const char *) login_failed_attempts, 75, encrypted_buffer, 1);
|
||||
auto rc = eqcrypt_block((const char*)&login_reply, sizeof(login_reply), encrypted_buffer, 1);
|
||||
if (rc == nullptr) {
|
||||
LogDebug("Failed to encrypt eqcrypt block");
|
||||
}
|
||||
|
||||
memcpy(login_accepted->encrypt, encrypted_buffer, 80);
|
||||
constexpr int outsize = sizeof(LoginBaseMessage_Struct) + sizeof(encrypted_buffer);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_LoginAccepted, outsize);
|
||||
outapp->WriteData(&base_header, sizeof(base_header));
|
||||
outapp->WriteData(&encrypted_buffer, sizeof(encrypted_buffer));
|
||||
|
||||
if (server.options.IsDumpOutPacketsOn()) {
|
||||
DumpPacket(outapp);
|
||||
DumpPacket(outapp.get());
|
||||
}
|
||||
|
||||
connection->QueuePacket(outapp);
|
||||
delete outapp;
|
||||
m_connection->QueuePacket(outapp.get());
|
||||
|
||||
status = cs_logged_in;
|
||||
m_client_status = cs_logged_in;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,7 +632,7 @@ void Client::CreateEQEmuAccount(
|
||||
*/
|
||||
void Client::LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection)
|
||||
{
|
||||
login_connection = connection;
|
||||
m_login_connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -751,16 +694,16 @@ void Client::LoginSendSessionReady()
|
||||
p.PutUInt16(0, 1); //OP_SessionReady
|
||||
p.PutUInt32(2, 2);
|
||||
|
||||
login_connection->QueuePacket(p);
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
void Client::LoginSendLogin()
|
||||
{
|
||||
size_t buffer_len = stored_user.length() + stored_pass.length() + 2;
|
||||
size_t buffer_len = m_stored_user.length() + m_stored_pass.length() + 2;
|
||||
std::unique_ptr<char[]> buffer(new char[buffer_len]);
|
||||
|
||||
strcpy(&buffer[0], stored_user.c_str());
|
||||
strcpy(&buffer[stored_user.length() + 1], stored_pass.c_str());
|
||||
strcpy(&buffer[0], m_stored_user.c_str());
|
||||
strcpy(&buffer[m_stored_user.length() + 1], m_stored_pass.c_str());
|
||||
|
||||
size_t encrypted_len = buffer_len;
|
||||
|
||||
@@ -775,7 +718,7 @@ void Client::LoginSendLogin()
|
||||
|
||||
eqcrypt_block(&buffer[0], buffer_len, (char *) p.Data() + 12, true);
|
||||
|
||||
login_connection->QueuePacket(p);
|
||||
m_login_connection->QueuePacket(p);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -796,7 +739,7 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p)
|
||||
EQ::Net::StaticPacket sp(&decrypted[0], encrypt_size);
|
||||
auto response_error = sp.GetUInt16(1);
|
||||
|
||||
login_connection_manager->OnConnectionStateChange(
|
||||
m_login_connection_manager->OnConnectionStateChange(
|
||||
std::bind(
|
||||
&Client::LoginOnStatusChangeIgnored,
|
||||
this,
|
||||
@@ -809,18 +752,26 @@ void Client::LoginProcessLoginResponse(const EQ::Net::Packet &p)
|
||||
if (response_error > 101) {
|
||||
LogDebug("response [{0}] failed login", response_error);
|
||||
DoFailedLogin();
|
||||
login_connection->Close();
|
||||
m_login_connection->Close();
|
||||
}
|
||||
else {
|
||||
LogDebug(
|
||||
"response [{0}] login succeeded user [{1}]",
|
||||
response_error,
|
||||
stored_user
|
||||
m_stored_user
|
||||
);
|
||||
|
||||
auto m_dbid = sp.GetUInt32(8);
|
||||
|
||||
CreateEQEmuAccount(stored_user, stored_pass, m_dbid);
|
||||
login_connection->Close();
|
||||
CreateEQEmuAccount(m_stored_user, m_stored_pass, m_dbid);
|
||||
m_login_connection->Close();
|
||||
}
|
||||
}
|
||||
bool Client::ProcessHealthCheck(std::string username)
|
||||
{
|
||||
if (username == "healthcheckuser") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+24
-56
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_CLIENT_H
|
||||
#define EQEMU_CLIENT_H
|
||||
|
||||
@@ -27,22 +7,9 @@
|
||||
#include "../common/eq_stream_intf.h"
|
||||
#include "../common/net/dns.h"
|
||||
#include "../common/net/daybreak_connection.h"
|
||||
#include "login_structures.h"
|
||||
#include "login_types.h"
|
||||
#include <memory>
|
||||
|
||||
enum LSClientVersion {
|
||||
cv_titanium,
|
||||
cv_sod
|
||||
};
|
||||
|
||||
enum LSClientStatus {
|
||||
cs_not_sent_session_ready,
|
||||
cs_waiting_for_login,
|
||||
cs_creating_account,
|
||||
cs_failed_to_login,
|
||||
cs_logged_in
|
||||
};
|
||||
|
||||
/**
|
||||
* Client class, controls a single client and it's connection to the login server
|
||||
*/
|
||||
@@ -116,49 +83,49 @@ public:
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
unsigned int GetAccountID() const { return account_id; }
|
||||
unsigned int GetAccountID() const { return m_account_id; }
|
||||
|
||||
/**
|
||||
* Gets the loginserver name of this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::string GetLoginServerName() const { return loginserver_name; }
|
||||
std::string GetLoginServerName() const { return m_loginserver_name; }
|
||||
|
||||
/**
|
||||
* Gets the account name of this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::string GetAccountName() const { return account_name; }
|
||||
std::string GetAccountName() const { return m_account_name; }
|
||||
|
||||
/**
|
||||
* Gets the key generated at login for this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::string GetKey() const { return key; }
|
||||
std::string GetKey() const { return m_key; }
|
||||
|
||||
/**
|
||||
* Gets the server selected to be played on for this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
unsigned int GetPlayServerID() const { return play_server_id; }
|
||||
unsigned int GetPlayServerID() const { return m_play_server_id; }
|
||||
|
||||
/**
|
||||
* Gets the play sequence state for this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
unsigned int GetPlaySequence() const { return play_sequence_id; }
|
||||
unsigned int GetPlaySequence() const { return m_play_sequence_id; }
|
||||
|
||||
/**
|
||||
* Gets the connection for this client
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
std::shared_ptr<EQStreamInterface> GetConnection() { return connection; }
|
||||
std::shared_ptr<EQStreamInterface> GetConnection() { return m_connection; }
|
||||
|
||||
/**
|
||||
* Attempts to create a login account
|
||||
@@ -195,24 +162,24 @@ public:
|
||||
void CreateEQEmuAccount(const std::string &in_account_name, const std::string &in_account_password, unsigned int loginserver_account_id);
|
||||
|
||||
private:
|
||||
EQ::Random random;
|
||||
std::shared_ptr<EQStreamInterface> connection;
|
||||
LSClientVersion version;
|
||||
LSClientStatus status;
|
||||
EQ::Random m_random;
|
||||
std::shared_ptr<EQStreamInterface> m_connection;
|
||||
LSClientVersion m_client_version;
|
||||
LSClientStatus m_client_status;
|
||||
|
||||
std::string account_name;
|
||||
unsigned int account_id;
|
||||
std::string loginserver_name;
|
||||
unsigned int play_server_id;
|
||||
unsigned int play_sequence_id;
|
||||
std::string key;
|
||||
std::string m_account_name;
|
||||
unsigned int m_account_id;
|
||||
std::string m_loginserver_name;
|
||||
unsigned int m_play_server_id;
|
||||
unsigned int m_play_sequence_id;
|
||||
std::string m_key;
|
||||
|
||||
std::unique_ptr<EQ::Net::DaybreakConnectionManager> login_connection_manager;
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> login_connection;
|
||||
LoginLoginRequest_Struct llrs;
|
||||
std::unique_ptr<EQ::Net::DaybreakConnectionManager> m_login_connection_manager;
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> m_login_connection;
|
||||
LoginBaseMessage_Struct m_llrs;
|
||||
|
||||
std::string stored_user;
|
||||
std::string stored_pass;
|
||||
std::string m_stored_user;
|
||||
std::string m_stored_pass;
|
||||
void LoginOnNewConnection(std::shared_ptr<EQ::Net::DaybreakConnection> connection);
|
||||
void LoginOnStatusChange(
|
||||
std::shared_ptr<EQ::Net::DaybreakConnection> conn,
|
||||
@@ -228,6 +195,7 @@ private:
|
||||
void LoginSendSessionReady();
|
||||
void LoginSendLogin();
|
||||
void LoginProcessLoginResponse(const EQ::Net::Packet &p);
|
||||
static bool ProcessHealthCheck(std::string username);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "client_manager.h"
|
||||
#include "login_server.h"
|
||||
|
||||
@@ -25,6 +5,7 @@ extern LoginServer server;
|
||||
extern bool run_server;
|
||||
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/misc.h"
|
||||
|
||||
ClientManager::ClientManager()
|
||||
{
|
||||
@@ -52,8 +33,8 @@ ClientManager::ClientManager()
|
||||
titanium_stream->OnNewConnection(
|
||||
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
|
||||
LogInfo(
|
||||
"New Titanium client connection from {0}:{1}",
|
||||
stream->GetRemoteIP(),
|
||||
"New Titanium client connection from [{0}:{1}]",
|
||||
long2ip(stream->GetRemoteIP()),
|
||||
stream->GetRemotePort()
|
||||
);
|
||||
|
||||
@@ -87,13 +68,13 @@ ClientManager::ClientManager()
|
||||
sod_stream->OnNewConnection(
|
||||
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
|
||||
LogInfo(
|
||||
"New SoD client connection from {0}:{1}",
|
||||
stream->GetRemoteIP(),
|
||||
"New SoD+ client connection from [{0}:{1}]",
|
||||
long2ip(stream->GetRemoteIP()),
|
||||
stream->GetRemotePort()
|
||||
);
|
||||
|
||||
stream->SetOpcodeManager(&sod_ops);
|
||||
Client *c = new Client(stream, cv_sod);
|
||||
auto *c = new Client(stream, cv_sod);
|
||||
clients.push_back(c);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_CLIENTMANAGER_H
|
||||
#define EQEMU_CLIENTMANAGER_H
|
||||
|
||||
|
||||
+44
-66
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "../common/global_define.h"
|
||||
|
||||
#include "database.h"
|
||||
@@ -45,11 +25,6 @@ Database::Database(
|
||||
std::string name
|
||||
)
|
||||
{
|
||||
this->user = user;
|
||||
this->pass = pass;
|
||||
this->host = host;
|
||||
this->name = name;
|
||||
|
||||
uint32 errnum = 0;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
if (!Open(
|
||||
@@ -75,8 +50,8 @@ Database::Database(
|
||||
*/
|
||||
Database::~Database()
|
||||
{
|
||||
if (database) {
|
||||
mysql_close(database);
|
||||
if (m_database) {
|
||||
mysql_close(m_database);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,11 +330,13 @@ void Database::UpdateLoginserverAccountPasswordHash(
|
||||
|
||||
/**
|
||||
* @param short_name
|
||||
* @param long_name
|
||||
* @param login_world_server_admin_id
|
||||
* @return
|
||||
*/
|
||||
Database::DbWorldRegistration Database::GetWorldRegistration(
|
||||
const std::string &short_name,
|
||||
const std::string &long_name,
|
||||
uint32 login_world_server_admin_id
|
||||
)
|
||||
{
|
||||
@@ -375,45 +352,46 @@ Database::DbWorldRegistration Database::GetWorldRegistration(
|
||||
" login_world_servers AS WSR\n"
|
||||
" JOIN login_server_list_types AS SLT ON WSR.login_server_list_type_id = SLT.id\n"
|
||||
"WHERE\n"
|
||||
" WSR.short_name = '{0}' AND WSR.login_server_admin_id = {1} LIMIT 1",
|
||||
" WSR.short_name = '{}' AND WSR.long_name = '{}' AND WSR.login_server_admin_id = {} LIMIT 1",
|
||||
EscapeString(short_name),
|
||||
EscapeString(long_name),
|
||||
login_world_server_admin_id
|
||||
);
|
||||
|
||||
Database::DbWorldRegistration world_registration{};
|
||||
Database::DbWorldRegistration r{};
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() || results.RowCount() != 1) {
|
||||
return world_registration;
|
||||
return r;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
|
||||
world_registration.loaded = true;
|
||||
world_registration.server_id = std::stoi(row[0]);
|
||||
world_registration.server_description = row[1];
|
||||
world_registration.server_list_type = std::stoi(row[3]);
|
||||
world_registration.is_server_trusted = std::stoi(row[2]) > 0;
|
||||
world_registration.server_list_description = row[4];
|
||||
world_registration.server_admin_id = std::stoi(row[5]);
|
||||
r.loaded = true;
|
||||
r.server_id = std::stoi(row[0]);
|
||||
r.server_description = row[1];
|
||||
r.server_list_type = std::stoi(row[3]);
|
||||
r.is_server_trusted = std::stoi(row[2]) > 0;
|
||||
r.server_list_description = row[4];
|
||||
r.server_admin_id = std::stoi(row[5]);
|
||||
|
||||
if (world_registration.server_admin_id <= 0) {
|
||||
return world_registration;
|
||||
if (r.server_admin_id <= 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
auto world_registration_query = fmt::format(
|
||||
"SELECT account_name, account_password FROM login_server_admins WHERE id = {0} LIMIT 1",
|
||||
world_registration.server_admin_id
|
||||
r.server_admin_id
|
||||
);
|
||||
|
||||
auto world_registration_results = QueryDatabase(world_registration_query);
|
||||
if (world_registration_results.Success() && world_registration_results.RowCount() == 1) {
|
||||
auto world_registration_row = world_registration_results.begin();
|
||||
world_registration.server_admin_account_name = world_registration_row[0];
|
||||
world_registration.server_admin_account_password = world_registration_row[1];
|
||||
r.server_admin_account_name = world_registration_row[0];
|
||||
r.server_admin_account_password = world_registration_row[1];
|
||||
}
|
||||
|
||||
return world_registration;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,21 +643,21 @@ Database::DbLoginServerAdmin Database::GetLoginServerAdmin(const std::string &ac
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
Database::DbLoginServerAdmin login_server_admin{};
|
||||
Database::DbLoginServerAdmin r{};
|
||||
if (results.RowCount() == 1) {
|
||||
auto row = results.begin();
|
||||
login_server_admin.loaded = true;
|
||||
login_server_admin.id = std::stoi(row[0]);
|
||||
login_server_admin.account_name = row[1];
|
||||
login_server_admin.account_password = row[2];
|
||||
login_server_admin.first_name = row[3];
|
||||
login_server_admin.last_name = row[4];
|
||||
login_server_admin.email = row[5];
|
||||
login_server_admin.registration_date = row[7];
|
||||
login_server_admin.registration_ip_address = row[8];
|
||||
r.loaded = true;
|
||||
r.id = std::stoi(row[0]);
|
||||
r.account_name = row[1];
|
||||
r.account_password = row[2];
|
||||
r.first_name = row[3];
|
||||
r.last_name = row[4];
|
||||
r.email = row[5];
|
||||
r.registration_date = row[7];
|
||||
r.registration_ip_address = row[8];
|
||||
}
|
||||
|
||||
return login_server_admin;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,20 +679,20 @@ Database::DbLoginServerAccount Database::GetLoginServerAccountByAccountName(
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
|
||||
Database::DbLoginServerAccount login_server_account{};
|
||||
Database::DbLoginServerAccount r{};
|
||||
if (results.RowCount() == 1) {
|
||||
auto row = results.begin();
|
||||
login_server_account.loaded = true;
|
||||
login_server_account.id = std::stoi(row[0]);
|
||||
login_server_account.account_name = row[1];
|
||||
login_server_account.account_password = row[2];
|
||||
login_server_account.account_email = row[3];
|
||||
login_server_account.source_loginserver = row[4];
|
||||
login_server_account.last_ip_address = row[5];
|
||||
login_server_account.last_login_date = row[6];
|
||||
login_server_account.created_at = row[7];
|
||||
login_server_account.updated_at = row[8];
|
||||
r.loaded = true;
|
||||
r.id = std::stoi(row[0]);
|
||||
r.account_name = row[1];
|
||||
r.account_password = row[2];
|
||||
r.account_email = row[3];
|
||||
r.source_loginserver = row[4];
|
||||
r.last_ip_address = row[5];
|
||||
r.last_login_date = row[6];
|
||||
r.created_at = row[7];
|
||||
r.updated_at = row[8];
|
||||
}
|
||||
|
||||
return login_server_account;
|
||||
return r;
|
||||
}
|
||||
|
||||
+5
-24
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_DATABASEMYSQL_H
|
||||
#define EQEMU_DATABASEMYSQL_H
|
||||
|
||||
@@ -32,7 +12,7 @@
|
||||
class Database : public DBcore {
|
||||
public:
|
||||
|
||||
Database() { database = nullptr; }
|
||||
Database() { m_database = nullptr; }
|
||||
|
||||
/**
|
||||
* Constructor, tries to set our database to connect to the supplied options.
|
||||
@@ -49,7 +29,7 @@ public:
|
||||
* Destructor, frees our database if needed.
|
||||
*/
|
||||
~Database();
|
||||
bool IsConnected() { return (database != nullptr); }
|
||||
bool IsConnected() { return (m_database != nullptr); }
|
||||
|
||||
/**
|
||||
* Retrieves the login data (password hash and account id) from the account name provided needed for client login procedure.
|
||||
@@ -158,11 +138,13 @@ public:
|
||||
* Returns true if the record was found, false otherwise
|
||||
*
|
||||
* @param short_name
|
||||
* @param long_name
|
||||
* @param login_world_server_admin_id
|
||||
* @return
|
||||
*/
|
||||
Database::DbWorldRegistration GetWorldRegistration(
|
||||
const std::string &short_name,
|
||||
const std::string &long_name,
|
||||
uint32 login_world_server_admin_id
|
||||
);
|
||||
|
||||
@@ -298,8 +280,7 @@ public:
|
||||
);
|
||||
|
||||
protected:
|
||||
std::string user, pass, host, port, name;
|
||||
MYSQL *database{};
|
||||
MYSQL *m_database{};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMUCAPI__H
|
||||
#define EQEMUCAPI__H
|
||||
|
||||
|
||||
@@ -1,29 +1,7 @@
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_LOGINSERVER_H
|
||||
#define EQEMU_LOGINSERVER_H
|
||||
|
||||
#include <utility>
|
||||
#include "../common/json_config.h"
|
||||
#include "database.h"
|
||||
#include "encryption.h"
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_LOGINSTRUCTURES_H
|
||||
#define EQEMU_LOGINSTRUCTURES_H
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct LoginChatMessage_Struct {
|
||||
short Unknown0;
|
||||
uint32 Unknown1;
|
||||
uint32 Unknown2;
|
||||
uint32 Unknown3;
|
||||
uint8 Unknown4;
|
||||
char ChatMessage[1];
|
||||
};
|
||||
|
||||
struct LoginLoginRequest_Struct {
|
||||
short unknown1;
|
||||
short unknown2;
|
||||
short unknown3;
|
||||
short unknown4;
|
||||
short unknown5;
|
||||
char unknown6[16];
|
||||
};
|
||||
|
||||
struct LoginAccepted_Struct {
|
||||
short unknown1;
|
||||
short unknown2;
|
||||
short unknown3;
|
||||
short unknown4;
|
||||
short unknown5;
|
||||
char encrypt[80];
|
||||
};
|
||||
|
||||
struct LoginFailedAttempts_Struct {
|
||||
char message; //0x01
|
||||
char unknown2[7]; //0x00
|
||||
uint32 lsid;
|
||||
char key[11]; //10 char + null term;
|
||||
uint32 failed_attempts;
|
||||
char unknown3[4]; //0x00, 0x00, 0x00, 0x03
|
||||
char unknown4[4]; //0x00, 0x00, 0x00, 0x02
|
||||
char unknown5[4]; //0xe7, 0x03, 0x00, 0x00
|
||||
char unknown6[4]; //0xff, 0xff, 0xff, 0xff
|
||||
char unknown7[4]; //0xa0, 0x05, 0x00, 0x00
|
||||
char unknown8[4]; //0x00, 0x00, 0x00, 0x02
|
||||
char unknown9[4]; //0xff, 0x03, 0x00, 0x00
|
||||
char unknown10[4]; //0x00, 0x00, 0x00, 0x00
|
||||
char unknown11[4]; //0x63, 0x00, 0x00, 0x00
|
||||
char unknown12[4]; //0x01, 0x00, 0x00, 0x00
|
||||
char unknown13[4]; //0x00, 0x00, 0x00, 0x00
|
||||
char unknown14[4]; //0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
struct LoginLoginFailed_Struct {
|
||||
short unknown1;
|
||||
short unknown2;
|
||||
short unknown3;
|
||||
short unknown4;
|
||||
short unknown5;
|
||||
char unknown6[74];
|
||||
};
|
||||
|
||||
struct ServerListHeader_Struct {
|
||||
|
||||
uint32 Unknown1;
|
||||
uint32 Unknown2;
|
||||
uint32 Unknown3;
|
||||
uint32 Unknown4;
|
||||
uint32 NumberOfServers;
|
||||
};
|
||||
|
||||
struct PlayEverquestRequest_Struct {
|
||||
uint16 Sequence;
|
||||
uint32 Unknown1;
|
||||
uint32 Unknown2;
|
||||
uint32 ServerNumber;
|
||||
};
|
||||
|
||||
struct PlayEverquestResponse_Struct {
|
||||
uint8 Sequence;
|
||||
uint8 Unknown1[9];
|
||||
uint8 Allowed;
|
||||
uint16 Message;
|
||||
uint8 Unknown2[3];
|
||||
uint32 ServerNumber;
|
||||
};
|
||||
|
||||
static const unsigned char FailedLoginResponseData[] = {
|
||||
0xf6, 0x85, 0x9c, 0x23, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16,
|
||||
0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16,
|
||||
0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3
|
||||
};
|
||||
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#ifndef EQEMU_LOGINSTRUCTURES_H
|
||||
#define EQEMU_LOGINSTRUCTURES_H
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
// unencrypted base message header in all packets
|
||||
struct LoginBaseMessage_Struct {
|
||||
int32_t sequence; // request type/login sequence (2: handshake, 3: login, 4: serverlist, ...)
|
||||
bool compressed; // true: deflated
|
||||
int8_t encrypt_type; // 1: invert (unused) 2: des (2 for encrypted player logins and order expansions) (client uses what it sent, ignores in reply)
|
||||
int32_t unk3; // unused?
|
||||
};
|
||||
|
||||
struct LoginBaseReplyMessage_Struct {
|
||||
bool success; // 0: failure (shows error string) 1: success
|
||||
int32_t error_str_id; // last error eqlsstr id, default: 101 (no error)
|
||||
char str[1]; // variable length, unknown (may be unused, this struct is a common pattern elsewhere)
|
||||
};
|
||||
|
||||
struct LoginHandShakeReply_Struct {
|
||||
LoginBaseMessage_Struct base_header;
|
||||
LoginBaseReplyMessage_Struct base_reply;
|
||||
char unknown[1]; // variable length string
|
||||
};
|
||||
|
||||
// for reference, login buffer is variable (minimum size 8 due to encryption)
|
||||
struct PlayerLogin_Struct {
|
||||
LoginBaseMessage_Struct base_header;
|
||||
char username[1];
|
||||
char password[1];
|
||||
};
|
||||
|
||||
// variable length, can use directly if not serializing strings
|
||||
struct PlayerLoginReply_Struct {
|
||||
// base header excluded to make struct data easier to encrypt
|
||||
//LoginBaseMessage_Struct base_header;
|
||||
LoginBaseReplyMessage_Struct base_reply;
|
||||
|
||||
int8_t unk1; // (default: 0)
|
||||
int8_t unk2; // (default: 0)
|
||||
int32_t lsid; // (default: -1)
|
||||
char key[11]; // client reads until null (variable length)
|
||||
int32_t failed_attempts;
|
||||
bool show_player_count; // admin flag, enables admin button and shows server player counts (default: false)
|
||||
int32_t offer_min_days; // guess, needs more investigation, maybe expansion offers (default: 99)
|
||||
int32_t offer_min_views; // guess (default: -1)
|
||||
int32_t offer_cooldown_minutes; // guess (default: 0)
|
||||
int32_t web_offer_number; // web order view number, 0 nothing (default: 0)
|
||||
int32_t web_offer_min_days; // number of days to show offer (based on first offer time in client eqls ini) (default: 99)
|
||||
int32_t web_offer_min_views; // mininum views, -1 for no minimum, 0 for never shows (based on client eqls ini) (default: -1)
|
||||
int32_t web_offer_cooldown_minutes; // minimum minutes between offers (based on last offer time in client eqls ini) (default: 0)
|
||||
char username[1]; // variable length, if not empty client attempts to re-login to server select when quitting from char select and sends this in a struct
|
||||
char unknown[1]; // variable length, password unlikely? client doesn't send this on re-login from char select
|
||||
};
|
||||
|
||||
// variable length, for reference
|
||||
struct LoginClientServerData_Struct {
|
||||
char ip[1];
|
||||
int32_t server_type; // legends, preferred, standard
|
||||
int32_t server_id;
|
||||
char server_name[1];
|
||||
char country_code[1]; // if doesn't match client locale then server is colored dark grey in list and joining is prevented (to block for "us" use one of "kr", "tw", "jp", "de", "fr", or "cn") (ISO 3166-1 alpha-2)
|
||||
char language_code[1];
|
||||
int32_t server_status; // see ServerStatusFlags
|
||||
int32_t player_count;
|
||||
};
|
||||
|
||||
// variable length, for reference
|
||||
struct ServerListReply_Struct {
|
||||
LoginBaseMessage_Struct base_header;
|
||||
LoginBaseReplyMessage_Struct base_reply;
|
||||
|
||||
int32_t server_count;
|
||||
LoginClientServerData_Struct servers[0];
|
||||
};
|
||||
|
||||
struct PlayEverquestRequest_Struct {
|
||||
LoginBaseMessage_Struct base_header;
|
||||
uint32 server_number;
|
||||
};
|
||||
|
||||
// SCJoinServerReply
|
||||
struct PlayEverquestResponse_Struct {
|
||||
LoginBaseMessage_Struct base_header;
|
||||
LoginBaseReplyMessage_Struct base_reply;
|
||||
uint32 server_number;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
enum LSClientVersion {
|
||||
cv_titanium,
|
||||
cv_sod
|
||||
};
|
||||
|
||||
enum LSClientStatus {
|
||||
cs_not_sent_session_ready,
|
||||
cs_waiting_for_login,
|
||||
cs_creating_account,
|
||||
cs_failed_to_login,
|
||||
cs_logged_in
|
||||
};
|
||||
|
||||
namespace LS {
|
||||
namespace ServerStatusFlags {
|
||||
enum eServerStatusFlags {
|
||||
Up = 0, // default
|
||||
Down = 1,
|
||||
Unused = 2,
|
||||
Locked = 4 // can be combined with Down to show "Locked (Down)"
|
||||
};
|
||||
}
|
||||
|
||||
namespace ServerTypeFlags {
|
||||
enum eServerTypeFlags {
|
||||
None = 0,
|
||||
Standard = 1,
|
||||
Unknown2 = 2,
|
||||
Unknown4 = 4,
|
||||
Preferred = 8,
|
||||
Legends = 16 // can be combined with Preferred flag to override color in Legends section with Preferred color (green)
|
||||
};
|
||||
}
|
||||
|
||||
enum ServerType {
|
||||
Standard = 3,
|
||||
Preferred = 2,
|
||||
Legends = 1,
|
||||
};
|
||||
|
||||
namespace ErrStr {
|
||||
constexpr static int ERROR_NONE = 101; // No Error
|
||||
constexpr static int ERROR_UNKNOWN = 102; // Error - Unknown Error Occurred
|
||||
constexpr static int ERROR_ACTIVE_CHARACTER = 111; // Error 1018: You currently have an active character on that EverQuest Server, please allow a minute for synchronization and try again.
|
||||
constexpr static int ERROR_SERVER_UNAVAILABLE = 326; // That server is currently unavailable. Please check the EverQuest webpage for current server status and try again later.
|
||||
constexpr static int ERROR_ACCOUNT_SUSPENDED = 337; // This account is currently suspended. Please contact customer service for more information.
|
||||
constexpr static int ERROR_ACCOUNT_BANNED = 338; // This account is currently banned. Please contact customer service for more information.
|
||||
constexpr static int ERROR_WORLD_MAX_CAPACITY = 339; // The world server is currently at maximum capacity and not allowing further logins until the number of players online decreases. Please try again later.
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
},
|
||||
"worldservers": {
|
||||
"unregistered_allowed": true,
|
||||
"show_player_count": false,
|
||||
"dev_test_servers_list_bottom": false,
|
||||
"special_character_start_list_bottom": false,
|
||||
"reject_duplicate_servers": false
|
||||
},
|
||||
"web_api": {
|
||||
|
||||
@@ -5,7 +5,7 @@ CREATE TABLE `login_accounts` (
|
||||
`account_password` text NOT NULL,
|
||||
`account_email` varchar(100) NOT NULL,
|
||||
`source_loginserver` varchar(64) DEFAULT NULL,
|
||||
`last_ip_address` varchar(15) NOT NULL,
|
||||
`last_ip_address` varchar(30) NOT NULL,
|
||||
`last_login_date` datetime NOT NULL,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT current_timestamp(),
|
||||
@@ -22,7 +22,7 @@ CREATE TABLE `login_server_admins` (
|
||||
`last_name` varchar(50) NOT NULL,
|
||||
`email` varchar(100) NOT NULL,
|
||||
`registration_date` datetime NOT NULL,
|
||||
`registration_ip_address` varchar(15) NOT NULL,
|
||||
`registration_ip_address` varchar(30) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
|
||||
|
||||
@@ -45,7 +45,7 @@ CREATE TABLE `login_world_servers` (
|
||||
`tag_description` varchar(50) NOT NULL DEFAULT '',
|
||||
`login_server_list_type_id` int(11) NOT NULL,
|
||||
`last_login_date` datetime DEFAULT NULL,
|
||||
`last_ip_address` varchar(15) DEFAULT NULL,
|
||||
`last_ip_address` varchar(30) DEFAULT NULL,
|
||||
`login_server_admin_id` int(11) NOT NULL,
|
||||
`is_server_trusted` int(11) NOT NULL,
|
||||
`note` varchar(255) DEFAULT NULL,
|
||||
@@ -61,4 +61,4 @@ CREATE TABLE `login_api_tokens` (
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 <iostream>
|
||||
#include <random>
|
||||
#include "loginserver_command_handler.h"
|
||||
@@ -58,6 +38,7 @@ namespace LoginserverCommandHandler {
|
||||
function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens;
|
||||
function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount;
|
||||
function_map["world-admin:update"] = &LoginserverCommandHandler::UpdateLoginserverWorldAdminAccountPassword;
|
||||
function_map["health:check-login"] = &LoginserverCommandHandler::HealthCheckLogin;
|
||||
|
||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||
}
|
||||
@@ -301,4 +282,24 @@ namespace LoginserverCommandHandler {
|
||||
cmd(3).str()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @param cmd
|
||||
* @param description
|
||||
*/
|
||||
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||
{
|
||||
description = "Checks login health using a test user";
|
||||
|
||||
std::vector<std::string> arguments = {};
|
||||
std::vector<std::string> options = {};
|
||||
|
||||
if (cmd[{"-h", "--help"}]) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogInfo("[CLI] [HealthCheck] Response code [{}]", AccountManagement::HealthCheckUserLogin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "iostream"
|
||||
#include "../common/cli/eqemu_command_handler.h"
|
||||
|
||||
@@ -34,6 +14,7 @@ namespace LoginserverCommandHandler {
|
||||
void UpdateLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
void CheckExternalLoginserverUserCredentials(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
void UpdateLoginserverWorldAdminAccountPassword(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
void HealthCheckLogin(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "loginserver_webserver.h"
|
||||
#include "server_manager.h"
|
||||
#include "login_server.h"
|
||||
@@ -51,14 +31,15 @@ namespace LoginserverWebserver {
|
||||
auto iter = server.server_manager->getWorldServers().begin();
|
||||
while (iter != server.server_manager->getWorldServers().end()) {
|
||||
Json::Value row;
|
||||
row["server_long_name"] = (*iter)->GetServerLongName();
|
||||
row["server_short_name"] = (*iter)->GetServerLongName();
|
||||
row["server_list_id"] = (*iter)->GetServerListID();
|
||||
row["server_status"] = (*iter)->GetStatus();
|
||||
row["zones_booted"] = (*iter)->GetZonesBooted();
|
||||
row["local_ip"] = (*iter)->GetLocalIP();
|
||||
row["remote_ip"] = (*iter)->GetRemoteIP();
|
||||
row["players_online"] = (*iter)->GetPlayersOnline();
|
||||
row["server_long_name"] = (*iter)->GetServerLongName();
|
||||
row["server_short_name"] = (*iter)->GetServerShortName();
|
||||
row["server_list_type_id"] = (*iter)->GetServerListID();
|
||||
row["server_status"] = (*iter)->GetStatus();
|
||||
row["zones_booted"] = (*iter)->GetZonesBooted();
|
||||
row["local_ip"] = (*iter)->GetLocalIP();
|
||||
row["remote_ip"] = (*iter)->GetRemoteIP();
|
||||
row["players_online"] = (*iter)->GetPlayersOnline();
|
||||
row["world_id"] = (*iter)->GetServerId();
|
||||
response.append(row);
|
||||
++iter;
|
||||
}
|
||||
@@ -316,6 +297,24 @@ namespace LoginserverWebserver {
|
||||
LoginserverWebserver::SendResponse(response, res);
|
||||
}
|
||||
);
|
||||
|
||||
api.Get(
|
||||
"/probes/healthcheck", [](const httplib::Request &request, httplib::Response &res) {
|
||||
Json::Value response;
|
||||
uint32 login_response = AccountManagement::HealthCheckUserLogin();
|
||||
|
||||
response["status"] = login_response;
|
||||
if (login_response == 0) {
|
||||
response["message"] = "Process unresponsive, exiting...";
|
||||
LogInfo("Probes healthcheck unresponsive, exiting...");
|
||||
}
|
||||
|
||||
LoginserverWebserver::SendResponse(response, res);
|
||||
if (login_response == 0) {
|
||||
std::exit(0);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_LOGINSERVER_WEBSERVER_H
|
||||
#define EQEMU_LOGINSERVER_WEBSERVER_H
|
||||
|
||||
|
||||
+41
-26
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "../common/global_define.h"
|
||||
#include "../common/types.h"
|
||||
#include "../common/opcodemgr.h"
|
||||
@@ -30,6 +10,7 @@
|
||||
#include "login_server.h"
|
||||
#include "loginserver_webserver.h"
|
||||
#include "loginserver_command_handler.h"
|
||||
#include "../common/string_util.h"
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
@@ -40,6 +21,7 @@ LoginServer server;
|
||||
EQEmuLogSys LogSys;
|
||||
bool run_server = true;
|
||||
|
||||
void ResolveAddresses();
|
||||
void CatchSignal(int sig_num)
|
||||
{
|
||||
}
|
||||
@@ -80,8 +62,30 @@ void LoadServerConfig()
|
||||
"worldservers",
|
||||
"reject_duplicate_servers",
|
||||
false
|
||||
));
|
||||
server.options.AllowUnregistered(server.config.GetVariableBool("worldservers", "unregistered_allowed", true));
|
||||
)
|
||||
);
|
||||
server.options.SetShowPlayerCount(server.config.GetVariableBool("worldservers", "show_player_count", false));
|
||||
server.options.AllowUnregistered(
|
||||
server.config.GetVariableBool(
|
||||
"worldservers",
|
||||
"unregistered_allowed",
|
||||
true
|
||||
)
|
||||
);
|
||||
server.options.SetWorldDevTestServersListBottom(
|
||||
server.config.GetVariableBool(
|
||||
"worldservers",
|
||||
"dev_test_servers_list_bottom",
|
||||
false
|
||||
)
|
||||
);
|
||||
server.options.SetWorldSpecialCharacterStartListBottom(
|
||||
server.config.GetVariableBool(
|
||||
"worldservers",
|
||||
"special_character_start_list_bottom",
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Account
|
||||
@@ -138,11 +142,13 @@ void start_web_server()
|
||||
|
||||
httplib::Server api;
|
||||
|
||||
api.set_logger([](const auto& req, const auto& res) {
|
||||
if (!req.path.empty()) {
|
||||
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port);
|
||||
api.set_logger(
|
||||
[](const auto &req, const auto &res) {
|
||||
if (!req.path.empty()) {
|
||||
LogInfo("[API] Request [{}] via [{}:{}]", req.path, req.remote_addr, req.remote_port);
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
LoginserverWebserver::RegisterRoutes(api);
|
||||
api.listen("0.0.0.0", web_api_port);
|
||||
@@ -256,6 +262,15 @@ int main(int argc, char **argv)
|
||||
#endif
|
||||
LogInfo("[Config] [WorldServer] IsRejectingDuplicateServers [{0}]", server.options.IsRejectingDuplicateServers());
|
||||
LogInfo("[Config] [WorldServer] IsUnregisteredAllowed [{0}]", server.options.IsUnregisteredAllowed());
|
||||
LogInfo("[Config] [WorldServer] ShowPlayerCount [{0}]", server.options.IsShowPlayerCountEnabled());
|
||||
LogInfo(
|
||||
"[Config] [WorldServer] DevAndTestServersListBottom [{0}]",
|
||||
server.options.IsWorldDevTestServersListBottom()
|
||||
);
|
||||
LogInfo(
|
||||
"[Config] [WorldServer] SpecialCharactersStartListBottom [{0}]",
|
||||
server.options.IsWorldSpecialCharacterStartListBottom()
|
||||
);
|
||||
LogInfo("[Config] [Security] GetEncryptionMode [{0}]", server.options.GetEncryptionMode());
|
||||
LogInfo("[Config] [Security] IsTokenLoginAllowed [{0}]", server.options.IsTokenLoginAllowed());
|
||||
LogInfo("[Config] [Security] IsPasswordLoginAllowed [{0}]", server.options.IsPasswordLoginAllowed());
|
||||
|
||||
+27
-20
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_OPTIONS_H
|
||||
#define EQEMU_OPTIONS_H
|
||||
|
||||
@@ -133,6 +113,29 @@ public:
|
||||
inline void UpdateInsecurePasswords(bool b) { update_insecure_passwords = b; }
|
||||
inline bool IsUpdatingInsecurePasswords() const { return update_insecure_passwords; }
|
||||
|
||||
inline bool IsShowPlayerCountEnabled() const
|
||||
{
|
||||
return show_player_count;
|
||||
}
|
||||
inline void SetShowPlayerCount(bool show_player_count)
|
||||
{
|
||||
Options::show_player_count = show_player_count;
|
||||
}
|
||||
inline bool IsWorldDevTestServersListBottom() const { return world_dev_test_servers_list_bottom; }
|
||||
inline void SetWorldDevTestServersListBottom(bool dev_test_servers_list_bottom)
|
||||
{
|
||||
Options::world_dev_test_servers_list_bottom = dev_test_servers_list_bottom;
|
||||
}
|
||||
|
||||
inline bool IsWorldSpecialCharacterStartListBottom() const
|
||||
{
|
||||
return world_special_character_start_list_bottom;
|
||||
}
|
||||
inline void SetWorldSpecialCharacterStartListBottom(bool world_special_character_start_list_bottom)
|
||||
{
|
||||
Options::world_special_character_start_list_bottom = world_special_character_start_list_bottom;
|
||||
}
|
||||
|
||||
private:
|
||||
bool allow_unregistered;
|
||||
bool trace;
|
||||
@@ -140,8 +143,11 @@ private:
|
||||
bool dump_in_packets;
|
||||
bool dump_out_packets;
|
||||
bool reject_duplicate_servers;
|
||||
bool world_dev_test_servers_list_bottom;
|
||||
bool world_special_character_start_list_bottom;
|
||||
bool allow_token_login;
|
||||
bool allow_password_login;
|
||||
bool show_player_count;
|
||||
bool auto_create_accounts;
|
||||
bool auto_link_accounts;
|
||||
bool update_insecure_passwords;
|
||||
@@ -150,5 +156,6 @@ private:
|
||||
std::string default_loginserver_name;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+68
-173
@@ -1,26 +1,6 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "server_manager.h"
|
||||
#include "login_server.h"
|
||||
#include "login_structures.h"
|
||||
#include "login_types.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../common/eqemu_logsys.h"
|
||||
@@ -33,15 +13,15 @@ ServerManager::ServerManager()
|
||||
{
|
||||
int listen_port = server.config.GetVariableInt("general", "listen_port", 5998);
|
||||
|
||||
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
|
||||
m_server_connection = std::make_unique<EQ::Net::ServertalkServer>();
|
||||
EQ::Net::ServertalkServerOptions opts;
|
||||
opts.port = listen_port;
|
||||
opts.ipv6 = false;
|
||||
server_connection->Listen(opts);
|
||||
opts.ipv6 = false;
|
||||
m_server_connection->Listen(opts);
|
||||
|
||||
LogInfo("Loginserver now listening on port [{0}]", listen_port);
|
||||
|
||||
server_connection->OnConnectionIdentified(
|
||||
m_server_connection->OnConnectionIdentified(
|
||||
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> world_connection) {
|
||||
LogInfo(
|
||||
"New World Server connection from {0}:{1}",
|
||||
@@ -49,8 +29,8 @@ ServerManager::ServerManager()
|
||||
world_connection->Handle()->RemotePort()
|
||||
);
|
||||
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
auto iter = m_world_servers.begin();
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter)->GetConnection()->Handle()->RemoteIP().compare(world_connection->Handle()->RemoteIP()) ==
|
||||
0 &&
|
||||
(*iter)->GetConnection()->Handle()->RemotePort() == world_connection->Handle()->RemotePort()) {
|
||||
@@ -61,27 +41,27 @@ ServerManager::ServerManager()
|
||||
world_connection->Handle()->RemotePort()
|
||||
);
|
||||
|
||||
world_servers.erase(iter);
|
||||
m_world_servers.erase(iter);
|
||||
break;
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
world_servers.push_back(std::make_unique<WorldServer>(world_connection));
|
||||
m_world_servers.push_back(std::make_unique<WorldServer>(world_connection));
|
||||
}
|
||||
);
|
||||
|
||||
server_connection->OnConnectionRemoved(
|
||||
m_server_connection->OnConnectionRemoved(
|
||||
"World", [this](std::shared_ptr<EQ::Net::ServertalkServerConnection> c) {
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
auto iter = m_world_servers.begin();
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter)->GetConnection()->GetUUID() == c->GetUUID()) {
|
||||
LogInfo(
|
||||
"World server {0} has been disconnected, removing.",
|
||||
(*iter)->GetServerLongName()
|
||||
);
|
||||
world_servers.erase(iter);
|
||||
m_world_servers.erase(iter);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,8 +80,8 @@ ServerManager::~ServerManager() = default;
|
||||
*/
|
||||
WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, int port)
|
||||
{
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
auto iter = m_world_servers.begin();
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter)->GetConnection()->Handle()->RemoteIP() == ip_address &&
|
||||
(*iter)->GetConnection()->Handle()->RemotePort()) {
|
||||
return (*iter).get();
|
||||
@@ -117,9 +97,8 @@ WorldServer *ServerManager::GetServerByAddress(const std::string &ip_address, in
|
||||
* @param sequence
|
||||
* @return
|
||||
*/
|
||||
EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint32 sequence)
|
||||
std::unique_ptr<EQApplicationPacket> ServerManager::CreateServerListPacket(Client *client, uint32 sequence)
|
||||
{
|
||||
unsigned int packet_size = sizeof(ServerListHeader_Struct);
|
||||
unsigned int server_count = 0;
|
||||
in_addr in{};
|
||||
in.s_addr = client->GetConnection()->GetRemoteIP();
|
||||
@@ -127,144 +106,60 @@ EQApplicationPacket *ServerManager::CreateServerListPacket(Client *client, uint3
|
||||
|
||||
LogDebug("ServerManager::CreateServerListPacket via client address [{0}]", client_ip);
|
||||
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
if (!(*iter)->IsAuthorized()) {
|
||||
for (const auto& world_server : m_world_servers)
|
||||
{
|
||||
if (world_server->IsAuthorized()) {
|
||||
++server_count;
|
||||
}
|
||||
}
|
||||
|
||||
SerializeBuffer buf;
|
||||
|
||||
// LoginBaseMessage_Struct header
|
||||
buf.WriteInt32(sequence);
|
||||
buf.WriteInt8(0);
|
||||
buf.WriteInt8(0);
|
||||
buf.WriteInt32(0);
|
||||
|
||||
// LoginBaseReplyMessage_Struct
|
||||
buf.WriteInt8(true); // success (no error)
|
||||
buf.WriteInt32(0x65); // 101 "No Error" eqlsstr
|
||||
buf.WriteString("");
|
||||
|
||||
// ServerListReply_Struct
|
||||
buf.WriteInt32(server_count);
|
||||
|
||||
for (const auto& world_server : m_world_servers)
|
||||
{
|
||||
if (!world_server->IsAuthorized()) {
|
||||
LogDebug(
|
||||
"ServerManager::CreateServerListPacket | Server [{0}] via IP [{1}] is not authorized to be listed",
|
||||
(*iter)->GetServerLongName(),
|
||||
(*iter)->GetConnection()->Handle()->RemoteIP()
|
||||
"ServerManager::CreateServerListPacket | Server [{}] via IP [{}] is not authorized to be listed",
|
||||
world_server->GetServerLongName(),
|
||||
world_server->GetConnection()->Handle()->RemoteIP()
|
||||
);
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
|
||||
if (world_ip == client_ip) {
|
||||
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24;
|
||||
bool use_local_ip = false;
|
||||
|
||||
LogDebug(
|
||||
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)",
|
||||
client->GetAccountName(),
|
||||
client_ip,
|
||||
(*iter)->GetServerLongName(),
|
||||
(*iter)->GetLocalIP()
|
||||
);
|
||||
}
|
||||
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
|
||||
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetLocalIP().size() + 24;
|
||||
|
||||
LogDebug(
|
||||
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Local)",
|
||||
client->GetAccountName(),
|
||||
client_ip,
|
||||
(*iter)->GetServerLongName(),
|
||||
(*iter)->GetLocalIP()
|
||||
);
|
||||
}
|
||||
else {
|
||||
packet_size += (*iter)->GetServerLongName().size() + (*iter)->GetRemoteIP().size() + 24;
|
||||
|
||||
LogDebug(
|
||||
"CreateServerListPacket | Building list entry | Client [{0}] IP [{1}] Server Long Name [{2}] Server IP [{3}] (Remote)",
|
||||
client->GetAccountName(),
|
||||
client_ip,
|
||||
(*iter)->GetServerLongName(),
|
||||
(*iter)->GetRemoteIP()
|
||||
);
|
||||
std::string world_ip = world_server->GetConnection()->Handle()->RemoteIP();
|
||||
if (world_ip == client_ip || IpUtil::IsIpInPrivateRfc1918(client_ip)) {
|
||||
use_local_ip = true;
|
||||
}
|
||||
|
||||
server_count++;
|
||||
++iter;
|
||||
LogDebug(
|
||||
"CreateServerListPacket | Building list entry | Client [{}] IP [{}] Server Long Name [{}] Server IP [{}] ({})",
|
||||
client->GetAccountName(),
|
||||
client_ip,
|
||||
world_server->GetServerLongName(),
|
||||
use_local_ip ? world_server->GetLocalIP() : world_server->GetRemoteIP(),
|
||||
use_local_ip ? "Local" : "Remote"
|
||||
);
|
||||
|
||||
world_server->SerializeForClientServerList(buf, use_local_ip);
|
||||
}
|
||||
|
||||
auto *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size);
|
||||
auto *server_list = (ServerListHeader_Struct *) outapp->pBuffer;
|
||||
|
||||
server_list->Unknown1 = sequence;
|
||||
server_list->Unknown2 = 0x00000000;
|
||||
server_list->Unknown3 = 0x01650000;
|
||||
|
||||
/**
|
||||
* Not sure what this is but it should be noted setting it to
|
||||
* 0xFFFFFFFF crashes the client so: don't do that.
|
||||
*/
|
||||
server_list->Unknown4 = 0x00000000;
|
||||
server_list->NumberOfServers = server_count;
|
||||
|
||||
unsigned char *data_pointer = outapp->pBuffer;
|
||||
data_pointer += sizeof(ServerListHeader_Struct);
|
||||
|
||||
iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
if (!(*iter)->IsAuthorized()) {
|
||||
++iter;
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string world_ip = (*iter)->GetConnection()->Handle()->RemoteIP();
|
||||
if (world_ip == client_ip) {
|
||||
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
|
||||
data_pointer += ((*iter)->GetLocalIP().size() + 1);
|
||||
}
|
||||
else if (IpUtil::IsIpInPrivateRfc1918(client_ip)) {
|
||||
memcpy(data_pointer, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size());
|
||||
data_pointer += ((*iter)->GetLocalIP().size() + 1);
|
||||
}
|
||||
else {
|
||||
memcpy(data_pointer, (*iter)->GetRemoteIP().c_str(), (*iter)->GetRemoteIP().size());
|
||||
data_pointer += ((*iter)->GetRemoteIP().size() + 1);
|
||||
}
|
||||
|
||||
switch ((*iter)->GetServerListID()) {
|
||||
case 1: {
|
||||
*(unsigned int *) data_pointer = 0x00000030;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
*(unsigned int *) data_pointer = 0x00000009;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*(unsigned int *) data_pointer = 0x00000001;
|
||||
}
|
||||
}
|
||||
|
||||
data_pointer += 4;
|
||||
|
||||
*(unsigned int *) data_pointer = (*iter)->GetServerId();
|
||||
data_pointer += 4;
|
||||
|
||||
memcpy(data_pointer, (*iter)->GetServerLongName().c_str(), (*iter)->GetServerLongName().size());
|
||||
data_pointer += ((*iter)->GetServerLongName().size() + 1);
|
||||
|
||||
memcpy(data_pointer, "EN", 2);
|
||||
data_pointer += 3;
|
||||
|
||||
memcpy(data_pointer, "US", 2);
|
||||
data_pointer += 3;
|
||||
|
||||
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
|
||||
if ((*iter)->GetStatus() < 0) {
|
||||
if ((*iter)->GetZonesBooted() == 0) {
|
||||
*(uint32 *) data_pointer = 0x01;
|
||||
}
|
||||
else {
|
||||
*(uint32 *) data_pointer = 0x04;
|
||||
}
|
||||
}
|
||||
else {
|
||||
*(uint32 *) data_pointer = 0x02;
|
||||
}
|
||||
data_pointer += 4;
|
||||
|
||||
*(uint32 *) data_pointer = (*iter)->GetPlayersOnline();
|
||||
data_pointer += 4;
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
return outapp;
|
||||
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,9 +173,9 @@ void ServerManager::SendUserToWorldRequest(
|
||||
const std::string &client_loginserver
|
||||
)
|
||||
{
|
||||
auto iter = world_servers.begin();
|
||||
auto iter = m_world_servers.begin();
|
||||
bool found = false;
|
||||
while (iter != world_servers.end()) {
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter)->GetServerId() == server_id) {
|
||||
EQ::Net::DynamicPacket outapp;
|
||||
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
|
||||
@@ -316,8 +211,8 @@ bool ServerManager::ServerExists(
|
||||
WorldServer *ignore
|
||||
)
|
||||
{
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
auto iter = m_world_servers.begin();
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter).get() == ignore) {
|
||||
++iter;
|
||||
continue;
|
||||
@@ -343,8 +238,8 @@ void ServerManager::DestroyServerByName(
|
||||
WorldServer *ignore
|
||||
)
|
||||
{
|
||||
auto iter = world_servers.begin();
|
||||
while (iter != world_servers.end()) {
|
||||
auto iter = m_world_servers.begin();
|
||||
while (iter != m_world_servers.end()) {
|
||||
if ((*iter).get() == ignore) {
|
||||
++iter;
|
||||
continue;
|
||||
@@ -353,7 +248,7 @@ void ServerManager::DestroyServerByName(
|
||||
if ((*iter)->GetServerLongName().compare(server_long_name) == 0 &&
|
||||
(*iter)->GetServerShortName().compare(server_short_name) == 0) {
|
||||
(*iter)->GetConnection()->Handle()->Disconnect();
|
||||
iter = world_servers.erase(iter);
|
||||
iter = m_world_servers.erase(iter);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -366,5 +261,5 @@ void ServerManager::DestroyServerByName(
|
||||
*/
|
||||
const std::list<std::unique_ptr<WorldServer>> &ServerManager::getWorldServers() const
|
||||
{
|
||||
return world_servers;
|
||||
return m_world_servers;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_SERVERMANAGER_H
|
||||
#define EQEMU_SERVERMANAGER_H
|
||||
|
||||
@@ -65,7 +45,7 @@ public:
|
||||
* @param sequence
|
||||
* @return
|
||||
*/
|
||||
EQApplicationPacket *CreateServerListPacket(Client *client, uint32 sequence);
|
||||
std::unique_ptr<EQApplicationPacket> CreateServerListPacket(Client *client, uint32 sequence);
|
||||
|
||||
/**
|
||||
* Checks to see if there is a server exists with this name, ignoring option
|
||||
@@ -103,8 +83,8 @@ private:
|
||||
*/
|
||||
WorldServer *GetServerByAddress(const std::string &ip_address, int port);
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
|
||||
std::list<std::unique_ptr<WorldServer>> world_servers;
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> m_server_connection;
|
||||
std::list<std::unique_ptr<WorldServer>> m_world_servers;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+227
-155
@@ -1,28 +1,8 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 "world_server.h"
|
||||
#include "login_server.h"
|
||||
#include "login_structures.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "login_types.h"
|
||||
#include "../common/ip_util.h"
|
||||
#include "../common/string_util.h"
|
||||
|
||||
extern LoginServer server;
|
||||
|
||||
@@ -31,16 +11,16 @@ extern LoginServer server;
|
||||
*/
|
||||
WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> worldserver_connection)
|
||||
{
|
||||
connection = worldserver_connection;
|
||||
zones_booted = 0;
|
||||
players_online = 0;
|
||||
server_status = 0;
|
||||
server_id = 0;
|
||||
server_list_type_id = 0;
|
||||
server_process_type = 0;
|
||||
is_server_authorized = false;
|
||||
is_server_trusted = false;
|
||||
is_server_logged_in = false;
|
||||
m_connection = worldserver_connection;
|
||||
m_zones_booted = 0;
|
||||
m_players_online = 0;
|
||||
m_server_status = 0;
|
||||
m_server_id = 0;
|
||||
m_server_list_type_id = 0;
|
||||
m_server_process_type = 0;
|
||||
m_is_server_authorized = false;
|
||||
m_is_server_trusted = false;
|
||||
m_is_server_logged_in = false;
|
||||
|
||||
worldserver_connection->OnMessage(
|
||||
ServerOP_NewLSInfo,
|
||||
@@ -72,21 +52,25 @@ WorldServer::WorldServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> wo
|
||||
std::bind(&WorldServer::ProcessLSAccountUpdate, this, std::placeholders::_1, std::placeholders::_2)
|
||||
);
|
||||
|
||||
m_keepalive = std::make_unique<EQ::Timer>(1000, true, std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1));
|
||||
m_keepalive = std::make_unique<EQ::Timer>(
|
||||
1000,
|
||||
true,
|
||||
std::bind(&WorldServer::OnKeepAlive, this, std::placeholders::_1)
|
||||
);
|
||||
}
|
||||
|
||||
WorldServer::~WorldServer() = default;
|
||||
|
||||
void WorldServer::Reset()
|
||||
{
|
||||
server_id;
|
||||
zones_booted = 0;
|
||||
players_online = 0;
|
||||
server_status = 0;
|
||||
server_list_type_id = 0;
|
||||
server_process_type = 0;
|
||||
is_server_authorized = false;
|
||||
is_server_logged_in = false;
|
||||
m_server_id;
|
||||
m_zones_booted = 0;
|
||||
m_players_online = 0;
|
||||
m_server_status = 0;
|
||||
m_server_list_type_id = 0;
|
||||
m_server_process_type = 0;
|
||||
m_is_server_authorized = false;
|
||||
m_is_server_logged_in = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,9 +100,15 @@ void WorldServer::ProcessNewLSInfo(uint16_t opcode, const EQ::Net::Packet &packe
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
auto *info = (ServerNewLSInfo_Struct *) packet.Data();
|
||||
|
||||
// if for whatever reason the world server is not sending an address, use the local address it sends
|
||||
std::string remote_ip_addr = info->remote_ip_address;
|
||||
std::string local_ip_addr = info->local_ip_address;
|
||||
if (remote_ip_addr.empty() && !local_ip_addr.empty() && local_ip_addr != "127.0.0.1") {
|
||||
strcpy(info->remote_ip_address, local_ip_addr.c_str());
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"New World Server Info | name [{0}] shortname [{1}] remote_address [{2}] local_address [{3}] account [{4}] password [{5}] server_type [{6}]",
|
||||
info->server_long_name,
|
||||
@@ -208,15 +198,15 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
LogDebug("User-To-World Response received");
|
||||
}
|
||||
|
||||
auto *user_to_world_response = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
||||
auto *r = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
||||
|
||||
LogDebug("Trying to find client with user id of [{0}]", user_to_world_response->lsaccountid);
|
||||
Client *client = server.client_manager->GetClient(user_to_world_response->lsaccountid, "eqemu");
|
||||
LogDebug("Trying to find client with user id of [{0}]", r->lsaccountid);
|
||||
Client *client = server.client_manager->GetClient(r->lsaccountid, "eqemu");
|
||||
if (client) {
|
||||
|
||||
LogDebug(
|
||||
"Found client with user id of [{0}] and account name of [{1}]",
|
||||
user_to_world_response->lsaccountid,
|
||||
r->lsaccountid,
|
||||
client->GetAccountName()
|
||||
);
|
||||
|
||||
@@ -226,11 +216,11 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
);
|
||||
|
||||
auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer;
|
||||
per->Sequence = client->GetPlaySequence();
|
||||
per->ServerNumber = client->GetPlayServerID();
|
||||
per->base_header.sequence = client->GetPlaySequence();
|
||||
per->server_number = client->GetPlayServerID();
|
||||
|
||||
if (user_to_world_response->response > 0) {
|
||||
per->Allowed = 1;
|
||||
if (r->response > 0) {
|
||||
per->base_reply.success = true;
|
||||
SendClientAuth(
|
||||
client->GetConnection()->GetRemoteAddr(),
|
||||
client->GetAccountName(),
|
||||
@@ -240,36 +230,37 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
);
|
||||
}
|
||||
|
||||
switch (user_to_world_response->response) {
|
||||
switch (r->response) {
|
||||
case UserToWorldStatusSuccess:
|
||||
per->Message = 101;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
|
||||
break;
|
||||
case UserToWorldStatusWorldUnavail:
|
||||
per->Message = 326;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
|
||||
break;
|
||||
case UserToWorldStatusSuspended:
|
||||
per->Message = 337;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
|
||||
break;
|
||||
case UserToWorldStatusBanned:
|
||||
per->Message = 338;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
|
||||
break;
|
||||
case UserToWorldStatusWorldAtCapacity:
|
||||
per->Message = 339;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
|
||||
break;
|
||||
case UserToWorldStatusAlreadyOnline:
|
||||
per->Message = 111;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
||||
break;
|
||||
default:
|
||||
per->Message = 102;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (server.options.IsWorldTraceOn()) {
|
||||
LogDebug(
|
||||
"Sending play response: allowed [{0}] sequence [{1}] server number [{2}] message [{3}]",
|
||||
per->Allowed,
|
||||
per->Sequence,
|
||||
per->ServerNumber,
|
||||
per->Message
|
||||
per->base_reply.success,
|
||||
per->base_header.sequence,
|
||||
per->server_number,
|
||||
per->base_reply.error_str_id
|
||||
);
|
||||
|
||||
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
|
||||
@@ -285,7 +276,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
||||
else {
|
||||
LogError(
|
||||
"Received User-To-World Response for [{0}] but could not find the client referenced!",
|
||||
user_to_world_response->lsaccountid
|
||||
r->lsaccountid
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -344,8 +335,8 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
||||
);
|
||||
|
||||
auto *per = (PlayEverquestResponse_Struct *) outapp->pBuffer;
|
||||
per->Sequence = client->GetPlaySequence();
|
||||
per->ServerNumber = client->GetPlayServerID();
|
||||
per->base_header.sequence = client->GetPlaySequence();
|
||||
per->server_number = client->GetPlayServerID();
|
||||
|
||||
LogDebug(
|
||||
"Found sequence and play of [{0}] [{1}]",
|
||||
@@ -356,7 +347,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
||||
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
|
||||
|
||||
if (user_to_world_response->response > 0) {
|
||||
per->Allowed = 1;
|
||||
per->base_reply.success = true;
|
||||
SendClientAuth(
|
||||
client->GetConnection()->GetRemoteAddr(),
|
||||
client->GetAccountName(),
|
||||
@@ -368,34 +359,35 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
||||
|
||||
switch (user_to_world_response->response) {
|
||||
case UserToWorldStatusSuccess:
|
||||
per->Message = 101;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_NONE;
|
||||
break;
|
||||
case UserToWorldStatusWorldUnavail:
|
||||
per->Message = 326;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_SERVER_UNAVAILABLE;
|
||||
break;
|
||||
case UserToWorldStatusSuspended:
|
||||
per->Message = 337;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_SUSPENDED;
|
||||
break;
|
||||
case UserToWorldStatusBanned:
|
||||
per->Message = 338;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACCOUNT_BANNED;
|
||||
break;
|
||||
case UserToWorldStatusWorldAtCapacity:
|
||||
per->Message = 339;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_WORLD_MAX_CAPACITY;
|
||||
break;
|
||||
case UserToWorldStatusAlreadyOnline:
|
||||
per->Message = 111;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_ACTIVE_CHARACTER;
|
||||
break;
|
||||
default:
|
||||
per->Message = 102;
|
||||
per->base_reply.error_str_id = LS::ErrStr::ERROR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (server.options.IsTraceOn()) {
|
||||
LogDebug(
|
||||
"Sending play response with following data, allowed [{0}], sequence {1}, server number {2}, message {3}",
|
||||
per->Allowed,
|
||||
per->Sequence,
|
||||
per->ServerNumber,
|
||||
per->Message
|
||||
per->base_reply.success,
|
||||
per->base_header.sequence,
|
||||
per->server_number,
|
||||
per->base_reply.error_str_id
|
||||
);
|
||||
LogDebug("[Size: [{0}]] {1}", outapp->size, DumpPacketToString(outapp));
|
||||
}
|
||||
@@ -443,7 +435,7 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
|
||||
}
|
||||
|
||||
if (server.options.IsWorldTraceOn()) {
|
||||
LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", short_name);
|
||||
LogDebug("ServerOP_LSAccountUpdate packet received from [{0}]", m_short_name);
|
||||
}
|
||||
|
||||
auto *loginserver_update = (ServerLSAccountUpdate_Struct *) packet.Data();
|
||||
@@ -486,6 +478,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
|
||||
return;
|
||||
}
|
||||
|
||||
SanitizeWorldServerName(new_world_server_info_packet->server_long_name);
|
||||
|
||||
SetAccountPassword(new_world_server_info_packet->account_password)
|
||||
->SetLongName(new_world_server_info_packet->server_long_name)
|
||||
->SetShortName(new_world_server_info_packet->server_short_name)
|
||||
@@ -506,8 +500,8 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
|
||||
else {
|
||||
if (server.server_manager->ServerExists(GetServerLongName(), GetServerShortName(), this)) {
|
||||
LogInfo("World tried to login but there already exists a server that has that name, destroying [{}]",
|
||||
long_name);
|
||||
server.server_manager->DestroyServerByName(long_name, short_name, this);
|
||||
m_long_name);
|
||||
server.server_manager->DestroyServerByName(m_long_name, m_short_name, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +545,7 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
|
||||
Database::DbWorldRegistration
|
||||
world_registration = server.db->GetWorldRegistration(
|
||||
GetServerShortName(),
|
||||
GetServerLongName(),
|
||||
world_server_admin_id
|
||||
);
|
||||
|
||||
@@ -578,6 +573,12 @@ void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct *new_world_server_info
|
||||
GetServerLongName(),
|
||||
GetRemoteIp()
|
||||
);
|
||||
|
||||
WorldServer::FormatWorldServerName(
|
||||
new_world_server_info_packet->server_long_name,
|
||||
world_registration.server_list_type
|
||||
);
|
||||
SetLongName(new_world_server_info_packet->server_long_name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -619,7 +620,7 @@ void WorldServer::SendClientAuth(
|
||||
strncpy(client_auth.loginserver_name, &loginserver_name[0], 64);
|
||||
|
||||
const std::string &client_address(ip);
|
||||
std::string world_address(connection->Handle()->RemoteIP());
|
||||
std::string world_address(m_connection->Handle()->RemoteIP());
|
||||
|
||||
if (client_address == world_address) {
|
||||
client_auth.is_client_from_local_network = 1;
|
||||
@@ -655,13 +656,22 @@ void WorldServer::SendClientAuth(
|
||||
);
|
||||
|
||||
outapp.PutSerialize(0, client_auth);
|
||||
connection->Send(ServerOP_LSClientAuth, outapp);
|
||||
m_connection->Send(ServerOP_LSClientAuth, outapp);
|
||||
|
||||
if (server.options.IsDumpInPacketsOn()) {
|
||||
DumpPacket(ServerOP_LSClientAuth, outapp);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr static int MAX_ACCOUNT_NAME_LENGTH = 30;
|
||||
constexpr static int MAX_ACCOUNT_PASSWORD_LENGTH = 30;
|
||||
constexpr static int MAX_SERVER_LONG_NAME_LENGTH = 200;
|
||||
constexpr static int MAX_SERVER_SHORT_NAME_LENGTH = 50;
|
||||
constexpr static int MAX_SERVER_LOCAL_ADDRESS_LENGTH = 125;
|
||||
constexpr static int MAX_SERVER_REMOTE_ADDRESS_LENGTH = 125;
|
||||
constexpr static int MAX_SERVER_VERSION_LENGTH = 64;
|
||||
constexpr static int MAX_SERVER_PROTOCOL_VERSION = 25;
|
||||
|
||||
/**
|
||||
* @param new_world_server_info_packet
|
||||
* @return
|
||||
@@ -670,41 +680,32 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
|
||||
ServerNewLSInfo_Struct *new_world_server_info_packet
|
||||
)
|
||||
{
|
||||
const int max_account_name_length = 30;
|
||||
const int max_account_password_length = 30;
|
||||
const int max_server_long_name_length = 200;
|
||||
const int max_server_short_name_length = 50;
|
||||
const int max_server_local_address_length = 125;
|
||||
const int max_server_remote_address_length = 125;
|
||||
const int max_server_version_length = 64;
|
||||
const int max_server_protocol_version = 25;
|
||||
|
||||
if (strlen(new_world_server_info_packet->account_name) >= max_account_name_length) {
|
||||
LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", max_account_name_length);
|
||||
if (strlen(new_world_server_info_packet->account_name) >= MAX_ACCOUNT_NAME_LENGTH) {
|
||||
LogError("Handle_NewLSInfo error [account_name] was too long | max [{0}]", MAX_ACCOUNT_NAME_LENGTH);
|
||||
return false;
|
||||
}
|
||||
else if (strlen(new_world_server_info_packet->account_password) >= max_account_password_length) {
|
||||
LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", max_account_password_length);
|
||||
else if (strlen(new_world_server_info_packet->account_password) >= MAX_ACCOUNT_PASSWORD_LENGTH) {
|
||||
LogError("Handle_NewLSInfo error [account_password] was too long | max [{0}]", MAX_ACCOUNT_PASSWORD_LENGTH);
|
||||
return false;
|
||||
}
|
||||
else if (strlen(new_world_server_info_packet->server_long_name) >= max_server_long_name_length) {
|
||||
LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", max_server_long_name_length);
|
||||
else if (strlen(new_world_server_info_packet->server_long_name) >= MAX_SERVER_LONG_NAME_LENGTH) {
|
||||
LogError("Handle_NewLSInfo error [server_long_name] was too long | max [{0}]", MAX_SERVER_LONG_NAME_LENGTH);
|
||||
return false;
|
||||
}
|
||||
else if (strlen(new_world_server_info_packet->server_short_name) >= max_server_short_name_length) {
|
||||
LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", max_server_short_name_length);
|
||||
else if (strlen(new_world_server_info_packet->server_short_name) >= MAX_SERVER_SHORT_NAME_LENGTH) {
|
||||
LogError("Handle_NewLSInfo error [server_short_name] was too long | max [{0}]", MAX_SERVER_SHORT_NAME_LENGTH);
|
||||
return false;
|
||||
}
|
||||
else if (strlen(new_world_server_info_packet->server_version) >= max_server_short_name_length) {
|
||||
LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", max_server_version_length);
|
||||
else if (strlen(new_world_server_info_packet->server_version) >= MAX_SERVER_VERSION_LENGTH) {
|
||||
LogError("Handle_NewLSInfo error [server_version] was too long | max [{0}]", MAX_SERVER_VERSION_LENGTH);
|
||||
return false;
|
||||
}
|
||||
else if (strlen(new_world_server_info_packet->protocol_version) >= max_server_protocol_version) {
|
||||
LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", max_server_protocol_version);
|
||||
else if (strlen(new_world_server_info_packet->protocol_version) >= MAX_SERVER_PROTOCOL_VERSION) {
|
||||
LogError("Handle_NewLSInfo error [protocol_version] was too long | max [{0}]", MAX_SERVER_PROTOCOL_VERSION);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(new_world_server_info_packet->local_ip_address) <= max_server_local_address_length) {
|
||||
if (strlen(new_world_server_info_packet->local_ip_address) <= MAX_SERVER_LOCAL_ADDRESS_LENGTH) {
|
||||
if (strlen(new_world_server_info_packet->local_ip_address) == 0) {
|
||||
LogError("Handle_NewLSInfo error, local address was null, defaulting to localhost");
|
||||
SetLocalIp("127.0.0.1");
|
||||
@@ -714,17 +715,17 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", max_server_local_address_length);
|
||||
LogError("Handle_NewLSInfo error, local address was too long | max [{0}]", MAX_SERVER_LOCAL_ADDRESS_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(new_world_server_info_packet->remote_ip_address) <= max_server_remote_address_length) {
|
||||
if (strlen(new_world_server_info_packet->remote_ip_address) <= MAX_SERVER_REMOTE_ADDRESS_LENGTH) {
|
||||
if (strlen(new_world_server_info_packet->remote_ip_address) == 0) {
|
||||
SetRemoteIp(GetConnection()->Handle()->RemoteIP());
|
||||
|
||||
LogWarning(
|
||||
"Remote address was null, defaulting to stream address [{0}]",
|
||||
remote_ip_address
|
||||
m_remote_ip_address
|
||||
);
|
||||
}
|
||||
else {
|
||||
@@ -736,7 +737,7 @@ bool WorldServer::HandleNewLoginserverInfoValidation(
|
||||
|
||||
LogWarning(
|
||||
"Handle_NewLSInfo remote address was too long, defaulting to stream address [{0}]",
|
||||
remote_ip_address
|
||||
m_remote_ip_address
|
||||
);
|
||||
}
|
||||
|
||||
@@ -796,7 +797,7 @@ bool WorldServer::HandleNewLoginserverRegisteredOnly(
|
||||
if (IsServerTrusted()) {
|
||||
LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world");
|
||||
EQ::Net::DynamicPacket outapp;
|
||||
connection->Send(ServerOP_LSAccountUpdate, outapp);
|
||||
m_connection->Send(ServerOP_LSAccountUpdate, outapp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -847,6 +848,8 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
->SetIsServerTrusted(world_registration.is_server_trusted)
|
||||
->SetServerListTypeId(world_registration.server_list_type);
|
||||
|
||||
SetIsServerAuthorized(true);
|
||||
|
||||
bool does_world_server_pass_authentication_check = (
|
||||
world_registration.server_admin_account_name == GetAccountName() &&
|
||||
WorldServer::ValidateWorldServerAdminLogin(
|
||||
@@ -864,7 +867,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
|
||||
if (does_world_server_have_non_empty_credentials) {
|
||||
if (does_world_server_pass_authentication_check) {
|
||||
SetIsServerAuthorized(true);
|
||||
SetIsServerLoggedIn(true);
|
||||
|
||||
LogInfo(
|
||||
"Server long_name [{0}] short_name [{1}] successfully logged in",
|
||||
@@ -875,16 +878,13 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
if (IsServerTrusted()) {
|
||||
LogDebug("WorldServer::HandleNewLoginserverRegisteredOnly | ServerOP_LSAccountUpdate sent to world");
|
||||
EQ::Net::DynamicPacket outapp;
|
||||
connection->Send(ServerOP_LSAccountUpdate, outapp);
|
||||
m_connection->Send(ServerOP_LSAccountUpdate, outapp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
/**
|
||||
* this is the first of two cases where we should deny access even if unregistered is allowed
|
||||
*/
|
||||
// server is authorized to be on the loginserver list but didn't pass login check
|
||||
LogInfo(
|
||||
"Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database.",
|
||||
"Server long_name [{0}] short_name [{1}] attempted to log in but account and password did not match the entry in the database. Unregistered still allowed",
|
||||
GetServerLongName(),
|
||||
GetServerShortName()
|
||||
);
|
||||
@@ -892,9 +892,7 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
}
|
||||
else {
|
||||
|
||||
/**
|
||||
* this is the second of two cases where we should deny access even if unregistered is allowed
|
||||
*/
|
||||
// server is authorized to be on the loginserver list but didn't pass login check
|
||||
if (!GetAccountName().empty() || !GetAccountPassword().empty()) {
|
||||
LogInfo(
|
||||
"Server [{0}] [{1}] did not login but this server required a password to login",
|
||||
@@ -903,7 +901,6 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
);
|
||||
}
|
||||
else {
|
||||
SetIsServerAuthorized(true);
|
||||
LogInfo(
|
||||
"Server [{0}] [{1}] did not login but unregistered servers are allowed",
|
||||
GetServerLongName(),
|
||||
@@ -939,14 +936,12 @@ bool WorldServer::HandleNewLoginserverInfoUnregisteredAllowed(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto create a registration
|
||||
*/
|
||||
// Auto create a registration
|
||||
if (!server.db->CreateWorldRegistration(
|
||||
GetServerLongName(),
|
||||
GetServerShortName(),
|
||||
GetRemoteIp(),
|
||||
server_id,
|
||||
m_server_id,
|
||||
server_admin_id
|
||||
)) {
|
||||
return false;
|
||||
@@ -1038,13 +1033,56 @@ bool WorldServer::ValidateWorldServerAdminLogin(
|
||||
return false;
|
||||
}
|
||||
|
||||
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const
|
||||
{
|
||||
// see LoginClientServerData_Struct
|
||||
if (use_local_ip) {
|
||||
out.WriteString(GetLocalIP());
|
||||
}
|
||||
else {
|
||||
out.WriteString(GetRemoteIP());
|
||||
}
|
||||
|
||||
switch (GetServerListID()) {
|
||||
case LS::ServerType::Legends:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Legends);
|
||||
break;
|
||||
case LS::ServerType::Preferred:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Preferred);
|
||||
break;
|
||||
default:
|
||||
out.WriteInt32(LS::ServerTypeFlags::Standard);
|
||||
break;
|
||||
}
|
||||
|
||||
out.WriteUInt32(GetServerId());
|
||||
out.WriteString(GetServerLongName());
|
||||
out.WriteString("us"); // country code
|
||||
out.WriteString("en"); // language code
|
||||
|
||||
// 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down)
|
||||
if (GetStatus() < 0) {
|
||||
if (GetZonesBooted() == 0) {
|
||||
out.WriteInt32(LS::ServerStatusFlags::Down);
|
||||
}
|
||||
else {
|
||||
out.WriteInt32(LS::ServerStatusFlags::Locked);
|
||||
}
|
||||
}
|
||||
else {
|
||||
out.WriteInt32(LS::ServerStatusFlags::Up);
|
||||
}
|
||||
|
||||
out.WriteUInt32(GetPlayersOnline());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param in_server_list_id
|
||||
* @return
|
||||
*/
|
||||
WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id)
|
||||
{
|
||||
server_list_type_id = in_server_list_id;
|
||||
m_server_list_type_id = in_server_list_id;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1054,7 +1092,7 @@ WorldServer *WorldServer::SetServerListTypeId(unsigned int in_server_list_id)
|
||||
*/
|
||||
const std::string &WorldServer::GetServerDescription() const
|
||||
{
|
||||
return server_description;
|
||||
return m_server_description;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1062,7 +1100,7 @@ const std::string &WorldServer::GetServerDescription() const
|
||||
*/
|
||||
WorldServer *WorldServer::SetServerDescription(const std::string &in_server_description)
|
||||
{
|
||||
WorldServer::server_description = in_server_description;
|
||||
WorldServer::m_server_description = in_server_description;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1072,7 +1110,7 @@ WorldServer *WorldServer::SetServerDescription(const std::string &in_server_desc
|
||||
*/
|
||||
bool WorldServer::IsServerAuthorized() const
|
||||
{
|
||||
return is_server_authorized;
|
||||
return m_is_server_authorized;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1080,7 +1118,7 @@ bool WorldServer::IsServerAuthorized() const
|
||||
*/
|
||||
WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized)
|
||||
{
|
||||
WorldServer::is_server_authorized = in_is_server_authorized;
|
||||
WorldServer::m_is_server_authorized = in_is_server_authorized;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1090,7 +1128,7 @@ WorldServer *WorldServer::SetIsServerAuthorized(bool in_is_server_authorized)
|
||||
*/
|
||||
bool WorldServer::IsServerLoggedIn() const
|
||||
{
|
||||
return is_server_logged_in;
|
||||
return m_is_server_logged_in;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1098,7 +1136,7 @@ bool WorldServer::IsServerLoggedIn() const
|
||||
*/
|
||||
WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in)
|
||||
{
|
||||
WorldServer::is_server_logged_in = in_is_server_logged_in;
|
||||
WorldServer::m_is_server_logged_in = in_is_server_logged_in;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1108,7 +1146,7 @@ WorldServer *WorldServer::SetIsServerLoggedIn(bool in_is_server_logged_in)
|
||||
*/
|
||||
bool WorldServer::IsServerTrusted() const
|
||||
{
|
||||
return is_server_trusted;
|
||||
return m_is_server_trusted;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1116,7 +1154,7 @@ bool WorldServer::IsServerTrusted() const
|
||||
*/
|
||||
WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted)
|
||||
{
|
||||
WorldServer::is_server_trusted = in_is_server_trusted;
|
||||
WorldServer::m_is_server_trusted = in_is_server_trusted;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1126,7 +1164,7 @@ WorldServer *WorldServer::SetIsServerTrusted(bool in_is_server_trusted)
|
||||
*/
|
||||
WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted)
|
||||
{
|
||||
WorldServer::zones_booted = in_zones_booted;
|
||||
WorldServer::m_zones_booted = in_zones_booted;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1136,7 +1174,7 @@ WorldServer *WorldServer::SetZonesBooted(unsigned int in_zones_booted)
|
||||
*/
|
||||
WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online)
|
||||
{
|
||||
WorldServer::players_online = in_players_online;
|
||||
WorldServer::m_players_online = in_players_online;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1146,7 +1184,7 @@ WorldServer *WorldServer::SetPlayersOnline(unsigned int in_players_online)
|
||||
*/
|
||||
WorldServer *WorldServer::SetServerStatus(int in_server_status)
|
||||
{
|
||||
WorldServer::server_status = in_server_status;
|
||||
WorldServer::m_server_status = in_server_status;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1156,7 +1194,7 @@ WorldServer *WorldServer::SetServerStatus(int in_server_status)
|
||||
*/
|
||||
WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_type)
|
||||
{
|
||||
WorldServer::server_process_type = in_server_process_type;
|
||||
WorldServer::m_server_process_type = in_server_process_type;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1166,7 +1204,7 @@ WorldServer *WorldServer::SetServerProcessType(unsigned int in_server_process_ty
|
||||
*/
|
||||
WorldServer *WorldServer::SetLongName(const std::string &in_long_name)
|
||||
{
|
||||
WorldServer::long_name = in_long_name;
|
||||
WorldServer::m_long_name = in_long_name;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1176,7 +1214,7 @@ WorldServer *WorldServer::SetLongName(const std::string &in_long_name)
|
||||
*/
|
||||
WorldServer *WorldServer::SetShortName(const std::string &in_short_name)
|
||||
{
|
||||
WorldServer::short_name = in_short_name;
|
||||
WorldServer::m_short_name = in_short_name;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1186,7 +1224,7 @@ WorldServer *WorldServer::SetShortName(const std::string &in_short_name)
|
||||
*/
|
||||
WorldServer *WorldServer::SetAccountName(const std::string &in_account_name)
|
||||
{
|
||||
WorldServer::account_name = in_account_name;
|
||||
WorldServer::m_account_name = in_account_name;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1196,7 +1234,7 @@ WorldServer *WorldServer::SetAccountName(const std::string &in_account_name)
|
||||
*/
|
||||
WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_password)
|
||||
{
|
||||
WorldServer::account_password = in_account_password;
|
||||
WorldServer::m_account_password = in_account_password;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1206,7 +1244,7 @@ WorldServer *WorldServer::SetAccountPassword(const std::string &in_account_passw
|
||||
*/
|
||||
WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip)
|
||||
{
|
||||
WorldServer::remote_ip_address = in_remote_ip;
|
||||
WorldServer::m_remote_ip_address = in_remote_ip;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1216,7 +1254,7 @@ WorldServer *WorldServer::SetRemoteIp(const std::string &in_remote_ip)
|
||||
*/
|
||||
WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip)
|
||||
{
|
||||
WorldServer::local_ip = in_local_ip;
|
||||
WorldServer::m_local_ip = in_local_ip;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1226,7 +1264,7 @@ WorldServer *WorldServer::SetLocalIp(const std::string &in_local_ip)
|
||||
*/
|
||||
WorldServer *WorldServer::SetProtocol(const std::string &in_protocol)
|
||||
{
|
||||
WorldServer::protocol = in_protocol;
|
||||
WorldServer::m_protocol = in_protocol;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1236,7 +1274,7 @@ WorldServer *WorldServer::SetProtocol(const std::string &in_protocol)
|
||||
*/
|
||||
WorldServer *WorldServer::SetVersion(const std::string &in_version)
|
||||
{
|
||||
WorldServer::version = in_version;
|
||||
WorldServer::m_version = in_version;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1246,7 +1284,7 @@ WorldServer *WorldServer::SetVersion(const std::string &in_version)
|
||||
*/
|
||||
int WorldServer::GetServerStatus() const
|
||||
{
|
||||
return server_status;
|
||||
return m_server_status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1254,7 +1292,7 @@ int WorldServer::GetServerStatus() const
|
||||
*/
|
||||
unsigned int WorldServer::GetServerListTypeId() const
|
||||
{
|
||||
return server_list_type_id;
|
||||
return m_server_list_type_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,7 +1300,7 @@ unsigned int WorldServer::GetServerListTypeId() const
|
||||
*/
|
||||
unsigned int WorldServer::GetServerProcessType() const
|
||||
{
|
||||
return server_process_type;
|
||||
return m_server_process_type;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1270,7 +1308,7 @@ unsigned int WorldServer::GetServerProcessType() const
|
||||
*/
|
||||
const std::string &WorldServer::GetAccountName() const
|
||||
{
|
||||
return account_name;
|
||||
return m_account_name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1278,7 +1316,7 @@ const std::string &WorldServer::GetAccountName() const
|
||||
*/
|
||||
const std::string &WorldServer::GetAccountPassword() const
|
||||
{
|
||||
return account_password;
|
||||
return m_account_password;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1286,7 +1324,7 @@ const std::string &WorldServer::GetAccountPassword() const
|
||||
*/
|
||||
const std::string &WorldServer::GetRemoteIp() const
|
||||
{
|
||||
return remote_ip_address;
|
||||
return m_remote_ip_address;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1294,7 +1332,7 @@ const std::string &WorldServer::GetRemoteIp() const
|
||||
*/
|
||||
const std::string &WorldServer::GetLocalIp() const
|
||||
{
|
||||
return local_ip;
|
||||
return m_local_ip;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1302,7 +1340,7 @@ const std::string &WorldServer::GetLocalIp() const
|
||||
*/
|
||||
const std::string &WorldServer::GetProtocol() const
|
||||
{
|
||||
return protocol;
|
||||
return m_protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1310,11 +1348,45 @@ const std::string &WorldServer::GetProtocol() const
|
||||
*/
|
||||
const std::string &WorldServer::GetVersion() const
|
||||
{
|
||||
return version;
|
||||
return m_version;
|
||||
}
|
||||
|
||||
void WorldServer::OnKeepAlive(EQ::Timer *t)
|
||||
{
|
||||
ServerPacket pack(ServerOP_KeepAlive, 0);
|
||||
connection->SendPacket(&pack);
|
||||
m_connection->SendPacket(&pack);
|
||||
}
|
||||
|
||||
void WorldServer::FormatWorldServerName(char *name, int8 server_list_type)
|
||||
{
|
||||
std::string server_long_name = name;
|
||||
server_long_name = trim(server_long_name);
|
||||
|
||||
bool name_set_to_bottom = false;
|
||||
if (server_list_type == LS::ServerType::Standard) {
|
||||
if (server.options.IsWorldDevTestServersListBottom()) {
|
||||
std::string s = str_tolower(server_long_name);
|
||||
if (s.find("dev") != std::string::npos) {
|
||||
server_long_name = fmt::format("|D| {}", server_long_name);
|
||||
name_set_to_bottom = true;
|
||||
}
|
||||
else if (s.find("test") != std::string::npos) {
|
||||
server_long_name = fmt::format("|T| {}", server_long_name);
|
||||
name_set_to_bottom = true;
|
||||
}
|
||||
else if (s.find("installer") != std::string::npos) {
|
||||
server_long_name = fmt::format("|I| {}", server_long_name);
|
||||
name_set_to_bottom = true;
|
||||
}
|
||||
}
|
||||
if (server.options.IsWorldSpecialCharacterStartListBottom() && !name_set_to_bottom) {
|
||||
auto first_char = server_long_name.c_str()[0];
|
||||
if (IsAllowedWorldServerCharacterList(first_char) && first_char != '|') {
|
||||
server_long_name = fmt::format("|*| {}", server_long_name);
|
||||
name_set_to_bottom = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strn0cpy(name, server_long_name.c_str(), 201);
|
||||
}
|
||||
|
||||
+35
-53
@@ -1,23 +1,3 @@
|
||||
/**
|
||||
* EQEmulator: Everquest Server Emulator
|
||||
* Copyright (C) 2001-2019 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 EQEMU_WORLDSERVER_H
|
||||
#define EQEMU_WORLDSERVER_H
|
||||
|
||||
@@ -50,44 +30,44 @@ public:
|
||||
/**
|
||||
* Accesses connection, it is intentional that this is not const (trust me).
|
||||
*/
|
||||
std::shared_ptr<EQ::Net::ServertalkServerConnection> GetConnection() { return connection; }
|
||||
void SetConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> c) { connection = c; }
|
||||
std::shared_ptr<EQ::Net::ServertalkServerConnection> GetConnection() { return m_connection; }
|
||||
void SetConnection(std::shared_ptr<EQ::Net::ServertalkServerConnection> c) { m_connection = c; }
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
unsigned int GetServerId() const { return server_id; }
|
||||
unsigned int GetServerId() const { return m_server_id; }
|
||||
WorldServer *SetServerId(unsigned int id)
|
||||
{
|
||||
server_id = id;
|
||||
m_server_id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
std::string GetServerLongName() const { return long_name; }
|
||||
std::string GetServerShortName() const { return short_name; }
|
||||
std::string GetServerLongName() const { return m_long_name; }
|
||||
std::string GetServerShortName() const { return m_short_name; }
|
||||
|
||||
/**
|
||||
* Gets whether the server is authorized to show up on the server list or not
|
||||
* @return
|
||||
*/
|
||||
bool IsAuthorized() const { return is_server_authorized; }
|
||||
std::string GetLocalIP() const { return local_ip; }
|
||||
std::string GetRemoteIP() const { return remote_ip_address; }
|
||||
bool IsAuthorized() const { return m_is_server_authorized; }
|
||||
std::string GetLocalIP() const { return m_local_ip; }
|
||||
std::string GetRemoteIP() const { return m_remote_ip_address; }
|
||||
|
||||
/**
|
||||
* Gets what kind of server this server is (legends, preferred, normal)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
unsigned int GetServerListID() const { return server_list_type_id; }
|
||||
unsigned int GetServerListID() const { return m_server_list_type_id; }
|
||||
WorldServer *SetServerListTypeId(unsigned int in_server_list_id);
|
||||
|
||||
int GetStatus() const { return server_status; }
|
||||
unsigned int GetZonesBooted() const { return zones_booted; }
|
||||
unsigned int GetPlayersOnline() const { return players_online; }
|
||||
int GetStatus() const { return m_server_status; }
|
||||
unsigned int GetZonesBooted() const { return m_zones_booted; }
|
||||
unsigned int GetPlayersOnline() const { return m_players_online; }
|
||||
|
||||
/**
|
||||
* Takes the info struct we received from world and processes it
|
||||
@@ -170,6 +150,8 @@ public:
|
||||
bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration);
|
||||
bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration);
|
||||
|
||||
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const;
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
@@ -184,27 +166,26 @@ private:
|
||||
void ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||
void ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet &packet);
|
||||
|
||||
std::shared_ptr<EQ::Net::ServertalkServerConnection> connection;
|
||||
std::shared_ptr<EQ::Net::ServertalkServerConnection> m_connection;
|
||||
|
||||
unsigned int zones_booted;
|
||||
unsigned int players_online;
|
||||
int server_status;
|
||||
unsigned int server_id;
|
||||
unsigned int server_list_type_id;
|
||||
unsigned int server_process_type;
|
||||
std::string server_description;
|
||||
std::string long_name;
|
||||
std::string short_name;
|
||||
std::string account_name;
|
||||
std::string account_password;
|
||||
std::string remote_ip_address;
|
||||
std::string local_ip;
|
||||
std::string protocol;
|
||||
std::string version;
|
||||
|
||||
bool is_server_authorized;
|
||||
bool is_server_logged_in;
|
||||
bool is_server_trusted;
|
||||
unsigned int m_zones_booted;
|
||||
unsigned int m_players_online;
|
||||
int m_server_status;
|
||||
unsigned int m_server_id;
|
||||
unsigned int m_server_list_type_id;
|
||||
unsigned int m_server_process_type;
|
||||
std::string m_server_description;
|
||||
std::string m_long_name;
|
||||
std::string m_short_name;
|
||||
std::string m_account_name;
|
||||
std::string m_account_password;
|
||||
std::string m_remote_ip_address;
|
||||
std::string m_local_ip;
|
||||
std::string m_protocol;
|
||||
std::string m_version;
|
||||
bool m_is_server_authorized;
|
||||
bool m_is_server_logged_in;
|
||||
bool m_is_server_trusted;
|
||||
|
||||
/**
|
||||
* Keepalive
|
||||
@@ -213,6 +194,7 @@ private:
|
||||
void OnKeepAlive(EQ::Timer *t);
|
||||
std::unique_ptr<EQ::Timer> m_keepalive;
|
||||
|
||||
static void FormatWorldServerName(char *name, int8 server_list_type);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -157,7 +157,7 @@ foreach my $table_to_generate (@tables) {
|
||||
$table_found_in_schema = 0;
|
||||
}
|
||||
|
||||
if ($table_found_in_schema == 0) {
|
||||
if ($table_found_in_schema == 0 && ($requested_table_to_generate eq "" || $requested_table_to_generate eq "all")) {
|
||||
print "Table [$table_to_generate] not found in schema, skipping\n";
|
||||
next;
|
||||
}
|
||||
|
||||
@@ -426,6 +426,8 @@
|
||||
9170|2021_03_03_instance_safereturns.sql|SHOW TABLES LIKE 'character_instance_safereturns'|empty|
|
||||
9171|2021_03_30_remove_dz_is_current_member.sql|SHOW COLUMNS FROM `dynamic_zone_members` LIKE 'is_current_member'|not_empty|
|
||||
9172|2021_05_21_shared_tasks.sql|SHOW TABLES LIKE 'shared_tasks'|empty|
|
||||
9173|2021_09_14_zone_lava_damage.sql|SHOW COLUMNS FROM `zone` LIKE 'lava_damage'|empty|
|
||||
9174|2021_10_09_not_null_door_columns.sql|SELECT * FROM db_version WHERE version >= 9174|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE login_accounts MODIFY COLUMN last_ip_address VARCHAR(30) NOT NULL;
|
||||
ALTER TABLE login_server_admins MODIFY COLUMN registration_ip_address VARCHAR(30) NOT NULL;
|
||||
ALTER TABLE login_world_servers MODIFY COLUMN last_ip_address VARCHAR(30) DEFAULT NULL;
|
||||
@@ -10,4 +10,4 @@ CREATE TABLE `character_instance_safereturns` (
|
||||
`safe_heading` float NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `character_id` (`character_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE zone ADD lava_damage INT(11) NULL DEFAULT '50' AFTER underworld_teleport_index, ADD min_lava_damage INT(11) NOT NULL DEFAULT '10' AFTER lava_damage;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- update any null columns to non-null value first to avoid data truncation errors
|
||||
-- this will likely only affect the buffer column
|
||||
update `doors` set `doors`.`dest_x` = 0 where `doors`.`dest_x` is null;
|
||||
update `doors` set `doors`.`dest_y` = 0 where `doors`.`dest_y` is null;
|
||||
update `doors` set `doors`.`dest_z` = 0 where `doors`.`dest_z` is null;
|
||||
update `doors` set `doors`.`dest_heading` = 0 where `doors`.`dest_heading` is null;
|
||||
update `doors` set `doors`.`invert_state` = 0 where `doors`.`invert_state` is null;
|
||||
update `doors` set `doors`.`incline` = 0 where `doors`.`incline` is null;
|
||||
update `doors` set `doors`.`buffer` = 0 where `doors`.`buffer` is null;
|
||||
|
||||
ALTER TABLE `doors`
|
||||
CHANGE COLUMN `dest_x` `dest_x` FLOAT NOT NULL DEFAULT '0' AFTER `dest_instance`,
|
||||
CHANGE COLUMN `dest_y` `dest_y` FLOAT NOT NULL DEFAULT '0' AFTER `dest_x`,
|
||||
CHANGE COLUMN `dest_z` `dest_z` FLOAT NOT NULL DEFAULT '0' AFTER `dest_y`,
|
||||
CHANGE COLUMN `dest_heading` `dest_heading` FLOAT NOT NULL DEFAULT '0' AFTER `dest_z`,
|
||||
CHANGE COLUMN `invert_state` `invert_state` INT(11) NOT NULL DEFAULT '0' AFTER `dest_heading`,
|
||||
CHANGE COLUMN `incline` `incline` INT(11) NOT NULL DEFAULT '0' AFTER `invert_state`,
|
||||
CHANGE COLUMN `buffer` `buffer` FLOAT NOT NULL DEFAULT '0' AFTER `size`;
|
||||
+57
-59
@@ -1343,71 +1343,67 @@ void ClientList::GetClients(const char *zone_name, std::vector<ClientListEntry *
|
||||
|
||||
void ClientList::SendClientVersionSummary(const char *Name)
|
||||
{
|
||||
uint32 ClientTitaniumCount = 0;
|
||||
uint32 ClientSoFCount = 0;
|
||||
uint32 ClientSoDCount = 0;
|
||||
uint32 ClientUnderfootCount = 0;
|
||||
uint32 ClientRoFCount = 0;
|
||||
uint32 ClientRoF2Count = 0;
|
||||
|
||||
std::vector<uint32> unique_ips;
|
||||
std::map<EQ::versions::ClientVersion,int> client_count = {
|
||||
{ EQ::versions::ClientVersion::Titanium, 0 },
|
||||
{ EQ::versions::ClientVersion::SoF, 0 },
|
||||
{ EQ::versions::ClientVersion::SoD, 0 },
|
||||
{ EQ::versions::ClientVersion::UF, 0 },
|
||||
{ EQ::versions::ClientVersion::RoF, 0 },
|
||||
{ EQ::versions::ClientVersion::RoF2, 0 }
|
||||
};
|
||||
|
||||
LinkedListIterator<ClientListEntry*> Iterator(clientlist);
|
||||
|
||||
Iterator.Reset();
|
||||
|
||||
while(Iterator.MoreElements())
|
||||
{
|
||||
while (Iterator.MoreElements()) {
|
||||
ClientListEntry* CLE = Iterator.GetData();
|
||||
if (CLE && CLE->zone()) {
|
||||
auto client_version = CLE->GetClientVersion();
|
||||
if (
|
||||
client_version >= (uint8) EQ::versions::ClientVersion::Titanium &&
|
||||
client_version <= (uint8) EQ::versions::ClientVersion::RoF2
|
||||
) {
|
||||
client_count[(EQ::versions::ClientVersion)client_version]++;
|
||||
}
|
||||
|
||||
if(CLE && CLE->zone())
|
||||
{
|
||||
switch(CLE->GetClientVersion())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
++ClientTitaniumCount;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
++ClientSoFCount;
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
++ClientSoDCount;
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
++ClientUnderfootCount;
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
++ClientRoFCount;
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
++ClientRoF2Count;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
if (std::find(unique_ips.begin(), unique_ips.begin(), CLE->GetIP()) == unique_ips.end()) {
|
||||
unique_ips.push_back(CLE->GetIP());
|
||||
}
|
||||
}
|
||||
|
||||
Iterator.Advance();
|
||||
|
||||
}
|
||||
|
||||
zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i Titanium, %i SoF, %i SoD, %i UF, %i RoF, %i RoF2 clients currently connected.",
|
||||
ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount, ClientRoF2Count);
|
||||
uint32 total_clients = (
|
||||
client_count[EQ::versions::ClientVersion::Titanium] +
|
||||
client_count[EQ::versions::ClientVersion::SoF] +
|
||||
client_count[EQ::versions::ClientVersion::SoD] +
|
||||
client_count[EQ::versions::ClientVersion::UF] +
|
||||
client_count[EQ::versions::ClientVersion::RoF] +
|
||||
client_count[EQ::versions::ClientVersion::RoF2]
|
||||
);
|
||||
|
||||
zoneserver_list.SendEmoteMessage(
|
||||
Name,
|
||||
0,
|
||||
0,
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"There {} {} Titanium, {} SoF, {} SoD, {} UF, {} RoF, and {} RoF2 Client{} currently connected for a total of {} Client{} and {} Unique IP{} connected.",
|
||||
(total_clients != 1 ? "are" : "is"),
|
||||
client_count[EQ::versions::ClientVersion::Titanium],
|
||||
client_count[EQ::versions::ClientVersion::SoF],
|
||||
client_count[EQ::versions::ClientVersion::SoD],
|
||||
client_count[EQ::versions::ClientVersion::UF],
|
||||
client_count[EQ::versions::ClientVersion::RoF],
|
||||
client_count[EQ::versions::ClientVersion::RoF2],
|
||||
(total_clients != 1 ? "s" : ""),
|
||||
total_clients,
|
||||
(total_clients != 1 ? "s" : ""),
|
||||
unique_ips.size(),
|
||||
(unique_ips.size() != 1 ? "s" : "")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void ClientList::OnTick(EQ::Timer *t)
|
||||
@@ -1589,12 +1585,14 @@ void ClientList::SendCharacterMessage(ClientListEntry* character, int chat_type,
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t pack_size = sizeof(CZMessagePlayer_Struct);
|
||||
auto pack = std::make_unique<ServerPacket>(ServerOP_CZMessagePlayer, pack_size);
|
||||
auto buf = reinterpret_cast<CZMessagePlayer_Struct*>(pack->pBuffer);
|
||||
uint32_t pack_size = sizeof(CZMessage_Struct);
|
||||
auto pack = std::make_unique<ServerPacket>(ServerOP_CZMessage, pack_size);
|
||||
auto buf = reinterpret_cast<CZMessage_Struct*>(pack->pBuffer);
|
||||
buf->update_type = CZUpdateType_ClientName;
|
||||
buf->update_identifier = 0;
|
||||
buf->type = chat_type;
|
||||
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
|
||||
strn0cpy(buf->message, message.c_str(), sizeof(buf->message));
|
||||
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
|
||||
|
||||
character->Server()->SendPacket(pack.get());
|
||||
}
|
||||
@@ -1633,7 +1631,7 @@ void ClientList::SendCharacterMessageID(ClientListEntry* character,
|
||||
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
|
||||
buf->string_id = eqstr_id;
|
||||
buf->chat_type = chat_type;
|
||||
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
|
||||
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
|
||||
buf->args_size = args_size;
|
||||
memcpy(buf->args, serialized_args.buffer(), serialized_args.size());
|
||||
|
||||
|
||||
+17
-10
@@ -778,8 +778,14 @@ void ConsoleWorldShutdown(
|
||||
zoneserver_list.WorldShutDown(0, 0);
|
||||
}
|
||||
else if (strcasecmp(args[0].c_str(), "disable") == 0) {
|
||||
connection->SendLine("<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
zoneserver_list.SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
connection->SendLine("[SYSTEM] World shutdown has been aborted.");
|
||||
zoneserver_list.SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Chat::Yellow,
|
||||
"[SYSTEM] World shutdown has been aborted."
|
||||
);
|
||||
zoneserver_list.shutdowntimer->Disable();
|
||||
zoneserver_list.reminder->Disable();
|
||||
}
|
||||
@@ -825,14 +831,15 @@ void ConsoleSignalCharByName(
|
||||
}
|
||||
|
||||
connection->SendLine(StringFormat("Signal Sent to %s with ID %i", (char *) args[0].c_str(), atoi(args[1].c_str())));
|
||||
uint32 message_len = strlen((char *) args[0].c_str()) + 1;
|
||||
auto pack = new ServerPacket(
|
||||
ServerOP_CZSignalClientByName,
|
||||
sizeof(CZClientSignalByName_Struct) + message_len
|
||||
);
|
||||
CZClientSignalByName_Struct *CZSC = (CZClientSignalByName_Struct *) pack->pBuffer;
|
||||
strn0cpy(CZSC->character_name, (char *) args[0].c_str(), 64);
|
||||
CZSC->signal = atoi(args[1].c_str());
|
||||
uint32 message_len = strlen((char *) args[0].c_str()) + 1;
|
||||
auto pack = new ServerPacket(ServerOP_CZSignal, sizeof(CZSignal_Struct) + message_len);
|
||||
CZSignal_Struct* CZS = (CZSignal_Struct*) pack->pBuffer;
|
||||
uint8 update_type = CZUpdateType_ClientName;
|
||||
int update_identifier = 0;
|
||||
CZS->update_type = update_type;
|
||||
CZS->update_identifier = update_identifier;
|
||||
CZS->signal = atoi(args[1].c_str());
|
||||
strn0cpy(CZS->client_name, (char *) args[0].c_str(), 64);
|
||||
zoneserver_list.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
+14
-2
@@ -750,8 +750,20 @@ void Console::ProcessCommand(const char* command) {
|
||||
zoneserver_list.WorldShutDown(0, 0);
|
||||
}
|
||||
else if(strcasecmp(sep.arg[1], "disable") == 0) {
|
||||
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
zoneserver_list.SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Chat::Yellow,
|
||||
"[SYSTEM] World shutdown has been aborted."
|
||||
);
|
||||
zoneserver_list.SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Chat::Yellow,
|
||||
"[SYSTEM] World shutdown has been aborted."
|
||||
);
|
||||
zoneserver_list.shutdowntimer->Disable();
|
||||
zoneserver_list.reminder->Disable();
|
||||
}
|
||||
|
||||
+107
-76
@@ -46,12 +46,12 @@ extern volatile bool RunLoops;
|
||||
|
||||
LoginServer::LoginServer(const char *iAddress, uint16 iPort, const char *Account, const char *Password, bool legacy)
|
||||
{
|
||||
strn0cpy(LoginServerAddress, iAddress, 256);
|
||||
LoginServerPort = iPort;
|
||||
LoginAccount = Account;
|
||||
LoginPassword = Password;
|
||||
CanAccountUpdate = false;
|
||||
IsLegacy = legacy;
|
||||
strn0cpy(m_loginserver_address, iAddress, 256);
|
||||
m_loginserver_port = iPort;
|
||||
m_login_account = Account;
|
||||
m_login_password = Password;
|
||||
m_can_account_update = false;
|
||||
m_is_legacy = legacy;
|
||||
Connect();
|
||||
}
|
||||
|
||||
@@ -93,7 +93,10 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
if (Config->Locked) {
|
||||
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
|
||||
LogDebug("[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid);
|
||||
LogDebug(
|
||||
"[ProcessUsertoWorldReqLeg] Server locked and status is not high enough for account_id [{0}]",
|
||||
utwr->lsaccountid
|
||||
);
|
||||
utwrs->response = UserToWorldStatusWorldUnavail;
|
||||
SendPacket(&outpack);
|
||||
return;
|
||||
@@ -171,7 +174,10 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
if (Config->Locked == true) {
|
||||
if (status < (RuleI(GM, MinStatusToBypassLockedServer))) {
|
||||
LogDebug("[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]", utwr->lsaccountid);
|
||||
LogDebug(
|
||||
"[ProcessUsertoWorldReq] Server locked and status is not high enough for account_id [{0}]",
|
||||
utwr->lsaccountid
|
||||
);
|
||||
utwrs->response = UserToWorldStatusWorldUnavail;
|
||||
SendPacket(&outpack);
|
||||
return;
|
||||
@@ -320,61 +326,63 @@ void LoginServer::ProcessLSAccountUpdate(uint16_t opcode, EQ::Net::Packet &p)
|
||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||
|
||||
LogNetcode("Received ServerOP_LSAccountUpdate packet from loginserver");
|
||||
CanAccountUpdate = true;
|
||||
m_can_account_update = true;
|
||||
}
|
||||
|
||||
bool LoginServer::Connect()
|
||||
{
|
||||
char errbuf[1024];
|
||||
if ((LoginServerIP = ResolveIP(LoginServerAddress, errbuf)) == 0) {
|
||||
LogInfo("Unable to resolve [{}] to an IP", LoginServerAddress);
|
||||
if ((m_loginserver_ip = ResolveIP(m_loginserver_address, errbuf)) == 0) {
|
||||
LogInfo("Unable to resolve [{}] to an IP", m_loginserver_address);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LoginServerIP == 0 || LoginServerPort == 0) {
|
||||
if (m_loginserver_ip == 0 || m_loginserver_port == 0) {
|
||||
LogInfo(
|
||||
"Connect info incomplete, cannot connect: [{0}:{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsLegacy) {
|
||||
legacy_client = std::make_unique<EQ::Net::ServertalkLegacyClient>(LoginServerAddress, LoginServerPort, false);
|
||||
legacy_client->OnConnect(
|
||||
if (m_is_legacy) {
|
||||
m_legacy_client = std::make_unique<EQ::Net::ServertalkLegacyClient>(
|
||||
m_loginserver_address,
|
||||
m_loginserver_port,
|
||||
false
|
||||
);
|
||||
m_legacy_client->OnConnect(
|
||||
[this](EQ::Net::ServertalkLegacyClient *client) {
|
||||
if (client) {
|
||||
LogInfo(
|
||||
"Connected to Legacy Loginserver: [{0}:{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
|
||||
SendInfo();
|
||||
SendStatus();
|
||||
zoneserver_list.SendLSZones();
|
||||
|
||||
statusupdate_timer = std::make_unique<EQ::Timer>(
|
||||
|
||||
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
|
||||
SendStatus();
|
||||
}
|
||||
|
||||
m_statusupdate_timer = std::make_unique<EQ::Timer>(
|
||||
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
|
||||
SendStatus();
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
LogInfo(
|
||||
"Could not connect to Legacy Loginserver: [{0}:{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_UsertoWorldReqLeg,
|
||||
std::bind(
|
||||
&LoginServer::ProcessUsertoWorldReqLeg,
|
||||
@@ -383,7 +391,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_UsertoWorldReq,
|
||||
std::bind(
|
||||
&LoginServer::ProcessUsertoWorldReq,
|
||||
@@ -392,7 +400,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_LSClientAuthLeg,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSClientAuthLegacy,
|
||||
@@ -401,7 +409,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_LSClientAuth,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSClientAuth,
|
||||
@@ -410,7 +418,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_LSFatalError,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSFatalError,
|
||||
@@ -419,7 +427,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_SystemwideMessage,
|
||||
std::bind(
|
||||
&LoginServer::ProcessSystemwideMessage,
|
||||
@@ -428,7 +436,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_LSRemoteAddr,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSRemoteAddr,
|
||||
@@ -437,7 +445,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
legacy_client->OnMessage(
|
||||
m_legacy_client->OnMessage(
|
||||
ServerOP_LSAccountUpdate,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSAccountUpdate,
|
||||
@@ -448,37 +456,43 @@ bool LoginServer::Connect()
|
||||
);
|
||||
}
|
||||
else {
|
||||
client = std::make_unique<EQ::Net::ServertalkClient>(LoginServerAddress, LoginServerPort, false, "World", "");
|
||||
client->OnConnect(
|
||||
m_client = std::make_unique<EQ::Net::ServertalkClient>(
|
||||
m_loginserver_address,
|
||||
m_loginserver_port,
|
||||
false,
|
||||
"World",
|
||||
""
|
||||
);
|
||||
m_client->OnConnect(
|
||||
[this](EQ::Net::ServertalkClient *client) {
|
||||
if (client) {
|
||||
LogInfo(
|
||||
"Connected to Loginserver: [{0}:{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
SendInfo();
|
||||
SendStatus();
|
||||
zoneserver_list.SendLSZones();
|
||||
|
||||
statusupdate_timer = std::make_unique<EQ::Timer>(
|
||||
m_statusupdate_timer = std::make_unique<EQ::Timer>(
|
||||
|
||||
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
|
||||
SendStatus();
|
||||
}
|
||||
);
|
||||
LoginServer_StatusUpdateInterval, true, [this](EQ::Timer *t) {
|
||||
SendStatus();
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
LogInfo(
|
||||
"Could not connect to Loginserver: [{0}:{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_UsertoWorldReqLeg,
|
||||
std::bind(
|
||||
&LoginServer::ProcessUsertoWorldReqLeg,
|
||||
@@ -487,7 +501,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_UsertoWorldReq,
|
||||
std::bind(
|
||||
&LoginServer::ProcessUsertoWorldReq,
|
||||
@@ -496,7 +510,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_LSClientAuthLeg,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSClientAuthLegacy,
|
||||
@@ -505,7 +519,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_LSClientAuth,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSClientAuth,
|
||||
@@ -514,7 +528,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_LSFatalError,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSFatalError,
|
||||
@@ -523,7 +537,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_SystemwideMessage,
|
||||
std::bind(
|
||||
&LoginServer::ProcessSystemwideMessage,
|
||||
@@ -532,7 +546,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_LSRemoteAddr,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSRemoteAddr,
|
||||
@@ -541,7 +555,7 @@ bool LoginServer::Connect()
|
||||
std::placeholders::_2
|
||||
)
|
||||
);
|
||||
client->OnMessage(
|
||||
m_client->OnMessage(
|
||||
ServerOP_LSAccountUpdate,
|
||||
std::bind(
|
||||
&LoginServer::ProcessLSAccountUpdate,
|
||||
@@ -552,7 +566,10 @@ bool LoginServer::Connect()
|
||||
);
|
||||
}
|
||||
|
||||
m_keepalive = std::make_unique<EQ::Timer>(1000, true, std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1));
|
||||
m_keepalive = std::make_unique<EQ::Timer>(
|
||||
1000,
|
||||
true,
|
||||
std::bind(&LoginServer::OnKeepAlive, this, std::placeholders::_1));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -566,28 +583,42 @@ void LoginServer::SendInfo()
|
||||
pack->size = sizeof(ServerNewLSInfo_Struct);
|
||||
pack->pBuffer = new uchar[pack->size];
|
||||
memset(pack->pBuffer, 0, pack->size);
|
||||
ServerNewLSInfo_Struct *lsi = (ServerNewLSInfo_Struct *) pack->pBuffer;
|
||||
strcpy(lsi->protocol_version, EQEMU_PROTOCOL_VERSION);
|
||||
strcpy(lsi->server_version, LOGIN_VERSION);
|
||||
strcpy(lsi->server_long_name, Config->LongName.c_str());
|
||||
strcpy(lsi->server_short_name, Config->ShortName.c_str());
|
||||
strn0cpy(lsi->account_name, LoginAccount.c_str(), 30);
|
||||
strn0cpy(lsi->account_password, LoginPassword.c_str(), 30);
|
||||
|
||||
auto *l = (ServerNewLSInfo_Struct *) pack->pBuffer;
|
||||
strcpy(l->protocol_version, EQEMU_PROTOCOL_VERSION);
|
||||
strcpy(l->server_version, LOGIN_VERSION);
|
||||
strcpy(l->server_long_name, Config->LongName.c_str());
|
||||
strcpy(l->server_short_name, Config->ShortName.c_str());
|
||||
strn0cpy(l->account_name, m_login_account.c_str(), 30);
|
||||
strn0cpy(l->account_password, m_login_password.c_str(), 30);
|
||||
if (Config->WorldAddress.length()) {
|
||||
strcpy(lsi->remote_ip_address, Config->WorldAddress.c_str());
|
||||
strcpy(l->remote_ip_address, Config->WorldAddress.c_str());
|
||||
}
|
||||
if (Config->LocalAddress.length()) {
|
||||
strcpy(lsi->local_ip_address, Config->LocalAddress.c_str());
|
||||
strcpy(l->local_ip_address, Config->LocalAddress.c_str());
|
||||
}
|
||||
else {
|
||||
auto local_addr = IsLegacy ? legacy_client->Handle()->LocalIP() : client->Handle()->LocalIP();
|
||||
strcpy(lsi->local_ip_address, local_addr.c_str());
|
||||
WorldConfig::SetLocalAddress(lsi->local_ip_address);
|
||||
auto local_addr = m_is_legacy ? m_legacy_client->Handle()->LocalIP() : m_client->Handle()->LocalIP();
|
||||
strcpy(l->local_ip_address, local_addr.c_str());
|
||||
WorldConfig::SetLocalAddress(l->local_ip_address);
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"[LoginServer::SendInfo] protocol_version [{}] server_version [{}] long_name [{}] short_name [{}] account_name [{}] remote_ip_address [{}] local_ip [{}]",
|
||||
l->protocol_version,
|
||||
l->server_version,
|
||||
l->server_long_name,
|
||||
l->server_short_name,
|
||||
l->account_name,
|
||||
l->remote_ip_address,
|
||||
l->local_ip_address
|
||||
);
|
||||
|
||||
SendPacket(pack);
|
||||
delete pack;
|
||||
}
|
||||
|
||||
|
||||
void LoginServer::SendStatus()
|
||||
{
|
||||
auto pack = new ServerPacket;
|
||||
@@ -618,14 +649,14 @@ void LoginServer::SendStatus()
|
||||
*/
|
||||
void LoginServer::SendPacket(ServerPacket *pack)
|
||||
{
|
||||
if (IsLegacy) {
|
||||
if (legacy_client) {
|
||||
legacy_client->SendPacket(pack);
|
||||
if (m_is_legacy) {
|
||||
if (m_legacy_client) {
|
||||
m_legacy_client->SendPacket(pack);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (client) {
|
||||
client->SendPacket(pack);
|
||||
if (m_client) {
|
||||
m_client->SendPacket(pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -636,11 +667,11 @@ void LoginServer::SendAccountUpdate(ServerPacket *pack)
|
||||
if (CanUpdate()) {
|
||||
LogInfo(
|
||||
"Sending ServerOP_LSAccountUpdate packet to loginserver: [{0}]:[{1}]",
|
||||
LoginServerAddress,
|
||||
LoginServerPort
|
||||
m_loginserver_address,
|
||||
m_loginserver_port
|
||||
);
|
||||
strn0cpy(ls_account_update->worldaccount, LoginAccount.c_str(), 30);
|
||||
strn0cpy(ls_account_update->worldpassword, LoginPassword.c_str(), 30);
|
||||
strn0cpy(ls_account_update->worldaccount, m_login_account.c_str(), 30);
|
||||
strn0cpy(ls_account_update->worldpassword, m_login_password.c_str(), 30);
|
||||
SendPacket(pack);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-33
@@ -1,20 +1,3 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
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 LOGINSERVER_H
|
||||
#define LOGINSERVER_H
|
||||
|
||||
@@ -42,20 +25,20 @@ public:
|
||||
void SendAccountUpdate(ServerPacket *pack);
|
||||
bool Connected()
|
||||
{
|
||||
if (IsLegacy) {
|
||||
if (legacy_client) {
|
||||
return legacy_client->Connected();
|
||||
if (m_is_legacy) {
|
||||
if (m_legacy_client) {
|
||||
return m_legacy_client->Connected();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (client) {
|
||||
return client->Connected();
|
||||
if (m_client) {
|
||||
return m_client->Connected();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool CanUpdate() { return CanAccountUpdate; }
|
||||
bool CanUpdate() { return m_can_account_update; }
|
||||
|
||||
private:
|
||||
void ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p);
|
||||
@@ -70,15 +53,15 @@ private:
|
||||
void OnKeepAlive(EQ::Timer *t);
|
||||
std::unique_ptr<EQ::Timer> m_keepalive;
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> client;
|
||||
std::unique_ptr<EQ::Net::ServertalkLegacyClient> legacy_client;
|
||||
std::unique_ptr<EQ::Timer> statusupdate_timer;
|
||||
char LoginServerAddress[256];
|
||||
uint32 LoginServerIP;
|
||||
uint16 LoginServerPort;
|
||||
std::string LoginAccount;
|
||||
std::string LoginPassword;
|
||||
bool CanAccountUpdate;
|
||||
bool IsLegacy;
|
||||
std::unique_ptr<EQ::Net::ServertalkClient> m_client;
|
||||
std::unique_ptr<EQ::Net::ServertalkLegacyClient> m_legacy_client;
|
||||
std::unique_ptr<EQ::Timer> m_statusupdate_timer;
|
||||
char m_loginserver_address[256];
|
||||
uint32 m_loginserver_ip;
|
||||
uint16 m_loginserver_port;
|
||||
std::string m_login_account;
|
||||
std::string m_login_password;
|
||||
bool m_can_account_update;
|
||||
bool m_is_legacy;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1311,7 +1311,7 @@ void SharedTaskManager::SendMembersMessageID(
|
||||
for (const auto &member : shared_task->GetMembers()) {
|
||||
auto character = client_list.FindCLEByCharacterID(member.character_id);
|
||||
if (character && character->Server()) {
|
||||
strn0cpy(buf->character_name, character->name(), sizeof(buf->character_name));
|
||||
strn0cpy(buf->client_name, character->name(), sizeof(buf->client_name));
|
||||
character->Server()->SendPacket(pack.get());
|
||||
}
|
||||
}
|
||||
|
||||
+101
-90
@@ -645,7 +645,12 @@ bool WorldDatabase::GetStartZone(
|
||||
}
|
||||
|
||||
void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){
|
||||
if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
|
||||
int sof_start_zone_id = RuleI(World, SoFStartZoneID);
|
||||
if (sof_start_zone_id > 0) {
|
||||
in_pp->zone_id = sof_start_zone_id;
|
||||
in_cc->start_zone = in_pp->zone_id;
|
||||
}
|
||||
else if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
|
||||
in_pp->zone_id = in_cc->start_zone;
|
||||
}
|
||||
else {
|
||||
@@ -653,105 +658,111 @@ void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCrea
|
||||
in_pp->y = in_pp->binds[0].y = -20.0f;
|
||||
in_pp->z = in_pp->binds[0].z = 0.79f;
|
||||
in_pp->heading = in_pp->binds[0].heading = 0.0f;
|
||||
in_pp->zone_id = in_pp->binds[0].zone_id = 394; // Crescent Reach.
|
||||
in_pp->zone_id = in_pp->binds[0].zone_id = Zones::CRESCENT; // Crescent Reach.
|
||||
}
|
||||
}
|
||||
|
||||
void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc)
|
||||
{
|
||||
switch (in_cc->start_zone)
|
||||
{
|
||||
case 0:
|
||||
int titanium_start_zone_id = RuleI(World, TitaniumStartZoneID);
|
||||
if (titanium_start_zone_id > 0) {
|
||||
in_pp->zone_id = titanium_start_zone_id;
|
||||
in_pp->binds[0].zone_id = titanium_start_zone_id;
|
||||
} else {
|
||||
switch (in_cc->start_zone)
|
||||
{
|
||||
if (in_cc->deity == 203) // Cazic-Thule Erudites go to Paineel
|
||||
case StartZoneIndex::Odus:
|
||||
{
|
||||
in_pp->zone_id = 75; // paineel
|
||||
in_pp->binds[0].zone_id = 75;
|
||||
if (in_cc->deity == EQ::deity::DeityCazicThule) // Cazic-Thule Erudites go to Paineel
|
||||
{
|
||||
in_pp->zone_id = Zones::PAINEEL; // paineel
|
||||
in_pp->binds[0].zone_id = Zones::PAINEEL;
|
||||
}
|
||||
else
|
||||
{
|
||||
in_pp->zone_id = Zones::ERUDNEXT; // erudnext
|
||||
in_pp->binds[0].zone_id = Zones::TOX; // tox
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case StartZoneIndex::Qeynos:
|
||||
{
|
||||
in_pp->zone_id = 24; // erudnext
|
||||
in_pp->binds[0].zone_id = 38; // tox
|
||||
in_pp->zone_id = Zones::QEYNOS2; // qeynos2
|
||||
in_pp->binds[0].zone_id = Zones::QEYNOS2; // qeynos2
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Halas:
|
||||
{
|
||||
in_pp->zone_id = Zones::HALAS; // halas
|
||||
in_pp->binds[0].zone_id = Zones::EVERFROST; // everfrost
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Rivervale:
|
||||
{
|
||||
in_pp->zone_id = Zones::RIVERVALE; // rivervale
|
||||
in_pp->binds[0].zone_id = Zones::KITHICOR; // kithicor
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Freeport:
|
||||
{
|
||||
in_pp->zone_id = Zones::FREPORTW; // freportw
|
||||
in_pp->binds[0].zone_id = Zones::FREPORTW; // freportw
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Neriak:
|
||||
{
|
||||
in_pp->zone_id = Zones::NERIAKA; // neriaka
|
||||
in_pp->binds[0].zone_id = Zones::NEKTULOS; // nektulos
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Grobb:
|
||||
{
|
||||
in_pp->zone_id = Zones::GROBB; // grobb
|
||||
in_pp->binds[0].zone_id = Zones::INNOTHULE; // innothule
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Oggok:
|
||||
{
|
||||
in_pp->zone_id = Zones::OGGOK; // oggok
|
||||
in_pp->binds[0].zone_id = Zones::FEERROTT; // feerrott
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Kaladim:
|
||||
{
|
||||
in_pp->zone_id = Zones::KALADIMA; // kaladima
|
||||
in_pp->binds[0].zone_id = Zones::BUTCHER; // butcher
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::GreaterFaydark:
|
||||
{
|
||||
in_pp->zone_id = Zones::GFAYDARK; // gfaydark
|
||||
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Felwithe:
|
||||
{
|
||||
in_pp->zone_id = Zones::FELWITHEA; // felwithea
|
||||
in_pp->binds[0].zone_id = Zones::GFAYDARK; // gfaydark
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Akanon:
|
||||
{
|
||||
in_pp->zone_id = Zones::AKANON; // akanon
|
||||
in_pp->binds[0].zone_id = Zones::STEAMFONT; // steamfont
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::Cabilis:
|
||||
{
|
||||
in_pp->zone_id = Zones::CABWEST; // cabwest
|
||||
in_pp->binds[0].zone_id = Zones::FIELDOFBONE; // fieldofbone
|
||||
break;
|
||||
}
|
||||
case StartZoneIndex::SharVahl:
|
||||
{
|
||||
in_pp->zone_id = Zones::SHARVAHL; // sharvahl
|
||||
in_pp->binds[0].zone_id = Zones::SHARVAHL; // sharvahl
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
in_pp->zone_id = 2; // qeynos2
|
||||
in_pp->binds[0].zone_id = 2; // qeynos2
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
in_pp->zone_id = 29; // halas
|
||||
in_pp->binds[0].zone_id = 30; // everfrost
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
in_pp->zone_id = 19; // rivervale
|
||||
in_pp->binds[0].zone_id = 20; // kithicor
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
in_pp->zone_id = 9; // freportw
|
||||
in_pp->binds[0].zone_id = 9; // freportw
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
in_pp->zone_id = 40; // neriaka
|
||||
in_pp->binds[0].zone_id = 25; // nektulos
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
{
|
||||
in_pp->zone_id = 52; // gukta
|
||||
in_pp->binds[0].zone_id = 46; // innothule
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
in_pp->zone_id = 49; // oggok
|
||||
in_pp->binds[0].zone_id = 47; // feerrott
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
in_pp->zone_id = 60; // kaladima
|
||||
in_pp->binds[0].zone_id = 68; // butcher
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
{
|
||||
in_pp->zone_id = 54; // gfaydark
|
||||
in_pp->binds[0].zone_id = 54; // gfaydark
|
||||
break;
|
||||
}
|
||||
case 10:
|
||||
{
|
||||
in_pp->zone_id = 61; // felwithea
|
||||
in_pp->binds[0].zone_id = 54; // gfaydark
|
||||
break;
|
||||
}
|
||||
case 11:
|
||||
{
|
||||
in_pp->zone_id = 55; // akanon
|
||||
in_pp->binds[0].zone_id = 56; // steamfont
|
||||
break;
|
||||
}
|
||||
case 12:
|
||||
{
|
||||
in_pp->zone_id = 82; // cabwest
|
||||
in_pp->binds[0].zone_id = 78; // fieldofbone
|
||||
break;
|
||||
}
|
||||
case 13:
|
||||
{
|
||||
in_pp->zone_id = 155; // sharvahl
|
||||
in_pp->binds[0].zone_id = 155; // sharvahl
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-3
@@ -110,7 +110,16 @@ void ZSList::Process() {
|
||||
}
|
||||
|
||||
if (reminder && reminder->Check() && shutdowntimer) {
|
||||
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime() / 1000) / 60));
|
||||
SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"[SYSTEM] World will be shutting down in {} minutes.",
|
||||
((shutdowntimer->GetRemainingTime() / 1000) / 60)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,7 +686,16 @@ void ZSList::UpdateUCSServerAvailable(bool ucss_available) {
|
||||
void ZSList::WorldShutDown(uint32 time, uint32 interval)
|
||||
{
|
||||
if (time > 0) {
|
||||
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60));
|
||||
SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
Chat::Yellow,
|
||||
fmt::format(
|
||||
"[SYSTEM] World will be shutting down in {} minutes.",
|
||||
(time / 60)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
time *= 1000;
|
||||
interval *= 1000;
|
||||
@@ -690,7 +708,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval)
|
||||
reminder->Start();
|
||||
}
|
||||
else {
|
||||
SendEmoteMessage(0, 0, 0, 15, "<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now.");
|
||||
SendEmoteMessage(0, 0, 0, 15, "[SYSTEM] World is shutting down.");
|
||||
auto pack = new ServerPacket;
|
||||
pack->opcode = ServerOP_ShutdownAll;
|
||||
pack->size = 0;
|
||||
|
||||
+30
-80
@@ -40,6 +40,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "expedition_message.h"
|
||||
#include "shared_task_world_messaging.h"
|
||||
#include "../common/shared_tasks.h"
|
||||
#include "shared_task_manager.h"
|
||||
|
||||
extern ClientList client_list;
|
||||
extern GroupLFPList LFPGroupList;
|
||||
@@ -50,6 +51,8 @@ extern volatile bool UCSServerAvailable_;
|
||||
extern AdventureManager adventure_manager;
|
||||
extern UCSConnection UCSLink;
|
||||
extern QueryServConnection QSLink;
|
||||
extern SharedTaskManager shared_task_manager;
|
||||
|
||||
void CatchSignal(int sig_num);
|
||||
|
||||
ZoneServer::ZoneServer(std::shared_ptr<EQ::Net::ServertalkServerConnection> connection, EQ::Net::ConsoleServer *console)
|
||||
@@ -450,6 +453,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 3; // offline
|
||||
scm->chan_num = ChatChannel_TellEcho;
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
// ideally this would be trimming off the message too, oh well
|
||||
sender->Server()->SendPacket(pack);
|
||||
@@ -463,6 +467,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 2; // queue full
|
||||
scm->chan_num = ChatChannel_TellEcho;
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
sender->Server()->SendPacket(pack);
|
||||
}
|
||||
@@ -478,6 +483,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
break;
|
||||
scm->noreply = true;
|
||||
scm->queued = 1; // queued
|
||||
scm->chan_num = ChatChannel_TellEcho;
|
||||
strcpy(scm->deliverto, scm->from);
|
||||
sender->Server()->SendPacket(pack);
|
||||
}
|
||||
@@ -608,7 +614,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
|
||||
if (sci->local_address[0]) {
|
||||
strn0cpy(client_local_address, sci->local_address, 250);
|
||||
LogInfo("Zone specified local address [{}]", sci->address);
|
||||
LogInfo("Zone specified local address [{}]", sci->local_address);
|
||||
}
|
||||
|
||||
if (sci->process_id) {
|
||||
@@ -1240,98 +1246,42 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
QSLink.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_CZCastSpellPlayer:
|
||||
case ServerOP_CZCastSpellGroup:
|
||||
case ServerOP_CZCastSpellRaid:
|
||||
case ServerOP_CZCastSpellGuild:
|
||||
case ServerOP_CZMarqueePlayer:
|
||||
case ServerOP_CZMarqueeGroup:
|
||||
case ServerOP_CZMarqueeRaid:
|
||||
case ServerOP_CZMarqueeGuild:
|
||||
case ServerOP_CZMessagePlayer:
|
||||
case ServerOP_CZMessageGroup:
|
||||
case ServerOP_CZMessageRaid:
|
||||
case ServerOP_CZMessageGuild:
|
||||
case ServerOP_CZMovePlayer:
|
||||
case ServerOP_CZMoveGroup:
|
||||
case ServerOP_CZMoveRaid:
|
||||
case ServerOP_CZMoveGuild:
|
||||
case ServerOP_CZMoveInstancePlayer:
|
||||
case ServerOP_CZMoveInstanceGroup:
|
||||
case ServerOP_CZMoveInstanceRaid:
|
||||
case ServerOP_CZMoveInstanceGuild:
|
||||
case ServerOP_CZRemoveSpellPlayer:
|
||||
case ServerOP_CZRemoveSpellGroup:
|
||||
case ServerOP_CZRemoveSpellRaid:
|
||||
case ServerOP_CZRemoveSpellGuild:
|
||||
case ServerOP_CZSetEntityVariableByClientName:
|
||||
case ServerOP_CZSetEntityVariableByNPCTypeID:
|
||||
case ServerOP_CZSetEntityVariableByGroupID:
|
||||
case ServerOP_CZSetEntityVariableByRaidID:
|
||||
case ServerOP_CZSetEntityVariableByGuildID:
|
||||
case ServerOP_CZSignalNPC:
|
||||
case ServerOP_CZSignalClient:
|
||||
case ServerOP_CZSignalClientByName:
|
||||
case ServerOP_CZSignalGroup:
|
||||
case ServerOP_CZSignalRaid:
|
||||
case ServerOP_CZSignalGuild:
|
||||
case ServerOP_CZTaskActivityResetPlayer:
|
||||
case ServerOP_CZTaskActivityResetGroup:
|
||||
case ServerOP_CZTaskActivityResetRaid:
|
||||
case ServerOP_CZTaskActivityResetGuild:
|
||||
case ServerOP_CZTaskActivityUpdatePlayer:
|
||||
case ServerOP_CZTaskActivityUpdateGroup:
|
||||
case ServerOP_CZTaskActivityUpdateRaid:
|
||||
case ServerOP_CZTaskActivityUpdateGuild:
|
||||
case ServerOP_CZTaskAssignPlayer:
|
||||
case ServerOP_CZTaskAssignGroup:
|
||||
case ServerOP_CZTaskAssignRaid:
|
||||
case ServerOP_CZTaskAssignGuild:
|
||||
case ServerOP_CZTaskDisablePlayer:
|
||||
case ServerOP_CZTaskDisableGroup:
|
||||
case ServerOP_CZTaskDisableRaid:
|
||||
case ServerOP_CZTaskDisableGuild:
|
||||
case ServerOP_CZTaskEnablePlayer:
|
||||
case ServerOP_CZTaskEnableGroup:
|
||||
case ServerOP_CZTaskEnableRaid:
|
||||
case ServerOP_CZTaskEnableGuild:
|
||||
case ServerOP_CZTaskFailPlayer:
|
||||
case ServerOP_CZTaskFailGroup:
|
||||
case ServerOP_CZTaskFailRaid:
|
||||
case ServerOP_CZTaskFailGuild:
|
||||
case ServerOP_CZTaskRemovePlayer:
|
||||
case ServerOP_CZTaskRemoveGroup:
|
||||
case ServerOP_CZTaskRemoveRaid:
|
||||
case ServerOP_CZTaskRemoveGuild:
|
||||
case ServerOP_CZDialogueWindow:
|
||||
case ServerOP_CZLDoNUpdate:
|
||||
case ServerOP_WWAssignTask:
|
||||
case ServerOP_WWCastSpell:
|
||||
case ServerOP_WWDisableTask:
|
||||
case ServerOP_WWEnableTask:
|
||||
case ServerOP_WWFailTask:
|
||||
case ServerOP_CZMarquee:
|
||||
case ServerOP_CZMessage:
|
||||
case ServerOP_CZMove:
|
||||
case ServerOP_CZSetEntityVariable:
|
||||
case ServerOP_CZSignal:
|
||||
case ServerOP_CZSpell:
|
||||
case ServerOP_CZTaskUpdate:
|
||||
case ServerOP_WWDialogueWindow:
|
||||
case ServerOP_WWLDoNUpdate:
|
||||
case ServerOP_WWMarquee:
|
||||
case ServerOP_WWMessage:
|
||||
case ServerOP_WWMove:
|
||||
case ServerOP_WWMoveInstance:
|
||||
case ServerOP_WWRemoveSpell:
|
||||
case ServerOP_WWRemoveTask:
|
||||
case ServerOP_WWResetActivity:
|
||||
case ServerOP_WWSetEntityVariableClient:
|
||||
case ServerOP_WWSetEntityVariableNPC:
|
||||
case ServerOP_WWSignalClient:
|
||||
case ServerOP_WWSignalNPC:
|
||||
case ServerOP_WWUpdateActivity:
|
||||
case ServerOP_WWSetEntityVariable:
|
||||
case ServerOP_WWSignal:
|
||||
case ServerOP_WWSpell:
|
||||
case ServerOP_WWTaskUpdate:
|
||||
case ServerOP_DepopAllPlayersCorpses:
|
||||
case ServerOP_DepopPlayerCorpse:
|
||||
case ServerOP_ReloadTitles:
|
||||
case ServerOP_SpawnStatusChange:
|
||||
case ServerOP_ReloadTasks:
|
||||
case ServerOP_ReloadWorld:
|
||||
case ServerOP_UpdateSpawn:
|
||||
{
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadTasks:
|
||||
{
|
||||
// world needs to update its copy of task data as well
|
||||
shared_task_manager.LoadTaskData();
|
||||
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ChangeSharedMem: {
|
||||
std::string hotfix_name = std::string((char*)pack->pBuffer);
|
||||
|
||||
@@ -1362,7 +1312,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
case ServerOP_CZClientMessageString:
|
||||
{
|
||||
auto buf = reinterpret_cast<CZClientMessageString_Struct*>(pack->pBuffer);
|
||||
client_list.SendPacket(buf->character_name, pack);
|
||||
client_list.SendPacket(buf->client_name, pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ExpeditionLockout:
|
||||
|
||||
@@ -26,6 +26,7 @@ SET(zone_sources
|
||||
dialogue_window.cpp
|
||||
dynamic_zone.cpp
|
||||
effects.cpp
|
||||
elixir.cpp
|
||||
embparser.cpp
|
||||
embparser_api.cpp
|
||||
embperl.cpp
|
||||
@@ -80,6 +81,7 @@ SET(zone_sources
|
||||
fearpath.cpp
|
||||
forage.cpp
|
||||
global_loot_manager.cpp
|
||||
gm_commands/door_manipulation.cpp
|
||||
groups.cpp
|
||||
guild.cpp
|
||||
guild_mgr.cpp
|
||||
@@ -120,6 +122,7 @@ SET(zone_sources
|
||||
perl_player_corpse.cpp
|
||||
perl_questitem.cpp
|
||||
perl_raids.cpp
|
||||
perl_spell.cpp
|
||||
perlpacket.cpp
|
||||
petitions.cpp
|
||||
pets.cpp
|
||||
@@ -185,6 +188,7 @@ SET(zone_headers
|
||||
doors.h
|
||||
dialogue_window.h
|
||||
dynamic_zone.h
|
||||
elixir.h
|
||||
embparser.h
|
||||
embperl.h
|
||||
embxs.h
|
||||
@@ -198,6 +202,7 @@ SET(zone_headers
|
||||
fastmath.h
|
||||
forage.h
|
||||
global_loot_manager.h
|
||||
gm_commands/door_manipulation.h
|
||||
groups.h
|
||||
guild_mgr.h
|
||||
hate_list.h
|
||||
|
||||
+14
-13
@@ -69,10 +69,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
|
||||
|
||||
for (int x = 0; x < MAX_SWARM_PETS; x++)
|
||||
{
|
||||
if (spells[spell_id].effectid[x] == SE_TemporaryPets)
|
||||
if (spells[spell_id].effect_id[x] == SE_TemporaryPets)
|
||||
{
|
||||
pet.count = spells[spell_id].base[x];
|
||||
pet.duration = spells[spell_id].max[x];
|
||||
pet.count = spells[spell_id].base_value[x];
|
||||
pet.duration = spells[spell_id].max_value[x];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,8 @@ void Client::ResetAA() {
|
||||
m_pp.raid_leadership_points = 0;
|
||||
m_pp.group_leadership_exp = 0;
|
||||
m_pp.raid_leadership_exp = 0;
|
||||
|
||||
|
||||
database.DeleteCharacterAAs(CharacterID());
|
||||
database.DeleteCharacterLeadershipAAs(CharacterID());
|
||||
}
|
||||
|
||||
@@ -764,7 +765,7 @@ void Client::InspectBuffs(Client* Inspector, int Rank)
|
||||
continue;
|
||||
ib->spell_id[packet_index] = buffs[i].spellid;
|
||||
if (Rank > 1)
|
||||
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
|
||||
ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buff_duration_formula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining;
|
||||
packet_index++;
|
||||
}
|
||||
|
||||
@@ -904,8 +905,8 @@ void Client::SendAlternateAdvancementRank(int aa_id, int level) {
|
||||
outapp->SetWritePosition(sizeof(AARankInfo_Struct));
|
||||
for(auto &effect : rank->effects) {
|
||||
outapp->WriteSInt32(effect.effect_id);
|
||||
outapp->WriteSInt32(effect.base1);
|
||||
outapp->WriteSInt32(effect.base2);
|
||||
outapp->WriteSInt32(effect.base_value);
|
||||
outapp->WriteSInt32(effect.limit_value);
|
||||
outapp->WriteSInt32(effect.slot);
|
||||
}
|
||||
|
||||
@@ -1229,7 +1230,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
|
||||
}
|
||||
//
|
||||
// Modern clients don't require pet targeted for AA casts that are ST_Pet
|
||||
if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
|
||||
if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet)
|
||||
target_id = GetPetID();
|
||||
|
||||
// extra handling for cast_not_standing spells
|
||||
@@ -1249,7 +1250,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
|
||||
else {
|
||||
// Bards can cast instant cast AAs while they are casting another song
|
||||
if (spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
|
||||
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
|
||||
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) {
|
||||
return;
|
||||
}
|
||||
ExpendAlternateAdvancementCharge(ability->id);
|
||||
@@ -1285,8 +1286,8 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
|
||||
}
|
||||
|
||||
for(auto &effect : rank->effects) {
|
||||
if(effect.effect_id == SE_HastenedAASkill && effect.base2 == ability_in->id) {
|
||||
return effect.base1;
|
||||
if(effect.effect_id == SE_HastenedAASkill && effect.limit_value == ability_in->id) {
|
||||
return effect.base_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1744,8 +1745,8 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_map<int, std
|
||||
int rank_id = atoi(row[0]);
|
||||
effect.slot = atoi(row[1]);
|
||||
effect.effect_id = atoi(row[2]);
|
||||
effect.base1 = atoi(row[3]);
|
||||
effect.base2 = atoi(row[4]);
|
||||
effect.base_value = atoi(row[3]);
|
||||
effect.limit_value = atoi(row[4]);
|
||||
|
||||
if(effect.slot < 1)
|
||||
continue;
|
||||
|
||||
@@ -29,8 +29,8 @@ struct RankEffect
|
||||
{
|
||||
int slot;
|
||||
int effect_id;
|
||||
int base1;
|
||||
int base2;
|
||||
int base_value;
|
||||
int limit_value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+38
-26
@@ -851,6 +851,17 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
|
||||
size_mod *= RuleR(Combat,HitBoxMod); // used for testing sizemods on different races.
|
||||
size_mod *= fixed_size_mod; // used to extend the size_mod
|
||||
|
||||
// Melee chasing fleeing mobs is borked. The client updates don't
|
||||
// come to the server quickly enough, especially when mob is running
|
||||
// and/or PC has good run speed. This change is a hack, but it greatly
|
||||
// improved playability and "you are too far away" while chasing
|
||||
// a fleeing mob. The Blind check is to make sure that this does not
|
||||
// apply to disoriented fleeing mobs who need proximity to turn and fight.
|
||||
if (other->currently_fleeing && !other->IsBlind())
|
||||
{
|
||||
size_mod *= 3;
|
||||
}
|
||||
|
||||
// prevention of ridiculously sized hit boxes
|
||||
if (size_mod > 10000)
|
||||
size_mod = size_mod / 7;
|
||||
@@ -863,7 +874,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
|
||||
|
||||
bool DoLoSCheck = true;
|
||||
float max_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0));
|
||||
float min_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
|
||||
float min_distance = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
|
||||
|
||||
if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2))
|
||||
DoLoSCheck = false; //Ignore line of sight check
|
||||
@@ -873,12 +884,12 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
|
||||
|
||||
max_dist = max_dist * max_dist;
|
||||
|
||||
if (!min_dist)
|
||||
min_dist = size_mod; //Default to melee range
|
||||
if (!min_distance)
|
||||
min_distance = size_mod; //Default to melee range
|
||||
else
|
||||
min_dist = min_dist * min_dist;
|
||||
min_distance = min_distance * min_distance;
|
||||
|
||||
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist))
|
||||
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_distance && _DistNoRoot <= max_dist))
|
||||
SetPseudoRoot(true);
|
||||
else
|
||||
SetPseudoRoot(false);
|
||||
@@ -901,6 +912,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -991,16 +1003,16 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
default_aggro = target_hp / 15;
|
||||
|
||||
for (int o = 0; o < EFFECT_COUNT; o++) {
|
||||
switch (spells[spell_id].effectid[o]) {
|
||||
switch (spells[spell_id].effect_id[o]) {
|
||||
case SE_CurrentHPOnce:
|
||||
case SE_CurrentHP: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if(val < 0)
|
||||
AggroAmount -= val;
|
||||
break;
|
||||
}
|
||||
case SE_MovementSpeed: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount += default_aggro;
|
||||
break;
|
||||
@@ -1008,7 +1020,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
case SE_AttackSpeed:
|
||||
case SE_AttackSpeed2:
|
||||
case SE_AttackSpeed3: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 100)
|
||||
AggroAmount += default_aggro;
|
||||
break;
|
||||
@@ -1026,7 +1038,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
break;
|
||||
case SE_ACv2:
|
||||
case SE_ArmorClass: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount += default_aggro;
|
||||
break;
|
||||
@@ -1044,19 +1056,19 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
case SE_INT:
|
||||
case SE_WIS:
|
||||
case SE_CHA: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount += 10;
|
||||
break;
|
||||
}
|
||||
case SE_ResistAll: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount += 50;
|
||||
break;
|
||||
}
|
||||
case SE_AllStats: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount += 70;
|
||||
break;
|
||||
@@ -1098,7 +1110,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
case SE_ManaRegen_v2:
|
||||
case SE_ManaPool:
|
||||
case SE_CurrentEndurance: {
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
if (val < 0)
|
||||
AggroAmount -= val * 2;
|
||||
break;
|
||||
@@ -1110,7 +1122,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
break;
|
||||
case SE_ReduceHate:
|
||||
case SE_InstantHate:
|
||||
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
|
||||
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base_value[o], spells[spell_id].max_value[o], slevel, spell_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1121,8 +1133,8 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
if (dispel && target && target->GetHateAmount(this) < 100)
|
||||
AggroAmount += 50;
|
||||
|
||||
if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash)
|
||||
AggroAmount = spells[spell_id].HateAdded;
|
||||
if (spells[spell_id].hate_added > 0) // overrides the hate (ex. tash)
|
||||
AggroAmount = spells[spell_id].hate_added;
|
||||
|
||||
if (GetOwner() && IsPet())
|
||||
AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;
|
||||
@@ -1137,10 +1149,10 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
|
||||
// initial aggro gets a bonus 100 besides for dispel or hate override
|
||||
// We add this 100 in AddToHateList so we need to account for the oddities here
|
||||
if (dispel && spells[spell_id].HateAdded > 0 && !on_hatelist)
|
||||
if (dispel && spells[spell_id].hate_added > 0 && !on_hatelist)
|
||||
AggroAmount -= 100;
|
||||
|
||||
return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro;
|
||||
return AggroAmount + spells[spell_id].bonus_hate + nonModifiedAggro;
|
||||
}
|
||||
|
||||
//healing and buffing aggro
|
||||
@@ -1151,7 +1163,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
|
||||
bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default
|
||||
|
||||
for (int o = 0; o < EFFECT_COUNT; o++) {
|
||||
switch (spells[spell_id].effectid[o]) {
|
||||
switch (spells[spell_id].effect_id[o]) {
|
||||
case SE_CurrentHP:
|
||||
case SE_PercentalHeal:
|
||||
{
|
||||
@@ -1161,7 +1173,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
|
||||
}
|
||||
// hate based on base healing power of the spell
|
||||
int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o],
|
||||
spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id);
|
||||
spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id);
|
||||
if (val > 0) {
|
||||
if (heal_possible < val)
|
||||
val = heal_possible; // capped to amount healed
|
||||
@@ -1177,7 +1189,7 @@ int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possib
|
||||
}
|
||||
case SE_Rune:
|
||||
AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o],
|
||||
spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id) * 2;
|
||||
spells[spell_id].base_value[o], spells[spell_id].max_value[o], GetLevel(), spell_id) * 2;
|
||||
ignore_default_buff = true;
|
||||
break;
|
||||
case SE_HealOverTime:
|
||||
@@ -1257,7 +1269,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
|
||||
|
||||
if(!caster) return false;
|
||||
|
||||
if(spells[spell_id].ResistDiff <= -600)
|
||||
if(spells[spell_id].resist_difficulty <= -600)
|
||||
return true;
|
||||
|
||||
float resist_check = 0;
|
||||
@@ -1272,9 +1284,9 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
|
||||
return true;
|
||||
|
||||
if (RuleB(Spells, CharismaCharmDuration))
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,false,0,true,true);
|
||||
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster,false,0,true,true);
|
||||
else
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true);
|
||||
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0, false, true);
|
||||
|
||||
//2: The mob makes a resistance check against the charm
|
||||
if (resist_check == 100)
|
||||
@@ -1298,7 +1310,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
|
||||
{
|
||||
// Assume this is a harmony/pacify spell
|
||||
// If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro.
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0,true);
|
||||
resist_check = ResistSpell(spells[spell_id].resist_type, spell_id, caster, false,0,true);
|
||||
if (resist_check == 100)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -478,7 +478,6 @@ Json::Value ApiGetMobListDetail(EQ::Net::WebsocketServerConnection *connection,
|
||||
row["has_temp_pets_active"] = mob->HasTempPetsActive();
|
||||
row["has_two_hand_blunt_equiped"] = mob->HasTwoHandBluntEquiped();
|
||||
row["has_two_hander_equipped"] = mob->HasTwoHanderEquipped();
|
||||
row["has_virus"] = mob->HasVirus();
|
||||
row["hate_summon"] = mob->HateSummon();
|
||||
row["helm_texture"] = mob->GetHelmTexture();
|
||||
row["hp"] = mob->GetHP();
|
||||
|
||||
+321
-127
@@ -59,6 +59,7 @@ extern FastMath g_Math;
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h TuneAttackAnimation
|
||||
EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* weapon, EQ::skills::SkillType skillinuse)
|
||||
{
|
||||
// Determine animation
|
||||
@@ -140,14 +141,30 @@ EQ::skills::SkillType Mob::AttackAnimation(int Hand, const EQ::ItemInstance* wea
|
||||
}
|
||||
|
||||
// If we're attacking with the secondary hand, play the dual wield anim
|
||||
if (Hand == EQ::invslot::slotSecondary) // DW anim
|
||||
if (Hand == EQ::invslot::slotSecondary) {// DW anim
|
||||
type = animDualWield;
|
||||
|
||||
//allow animation chance to fire to be similar to your dw chance
|
||||
if (GetDualWieldingSameDelayWeapons() == 2) {
|
||||
SetDualWieldingSameDelayWeapons(3);
|
||||
}
|
||||
}
|
||||
|
||||
//If both weapons have same delay this allows a chance for DW animation
|
||||
if (GetDualWieldingSameDelayWeapons() && Hand == EQ::invslot::slotPrimary) {
|
||||
|
||||
if (GetDualWieldingSameDelayWeapons() == 3 && zone->random.Roll(50)) {
|
||||
type = animDualWield;
|
||||
SetDualWieldingSameDelayWeapons(2);//Don't roll again till you do another dw attack.
|
||||
}
|
||||
SetDualWieldingSameDelayWeapons(2);//Ensures first attack is always primary.
|
||||
}
|
||||
|
||||
DoAnim(type, 0, false);
|
||||
|
||||
return skillinuse;
|
||||
}
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h Tunecompute_tohit
|
||||
int Mob::compute_tohit(EQ::skills::SkillType skillinuse)
|
||||
{
|
||||
int tohit = GetSkill(EQ::skills::SkillOffense) + 7;
|
||||
@@ -168,6 +185,7 @@ int Mob::compute_tohit(EQ::skills::SkillType skillinuse)
|
||||
}
|
||||
|
||||
// return -1 in cases that always hit
|
||||
//SYNC WITH: tune.cpp, mob.h TuneGetTotalToHit
|
||||
int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
|
||||
{
|
||||
if (chance_mod >= 10000) // override for stuff like SE_SkillAttack
|
||||
@@ -231,6 +249,7 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
|
||||
|
||||
// based on dev quotes
|
||||
// the AGI bonus has actually drastically changed from classic
|
||||
//SYNC WITH: tune.cpp, mob.h Tunecompute_defense
|
||||
int Mob::compute_defense()
|
||||
{
|
||||
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
|
||||
@@ -259,6 +278,7 @@ int Mob::compute_defense()
|
||||
}
|
||||
|
||||
// return -1 in cases that always miss
|
||||
// SYNC WITH : tune.cpp, mob.h TuneGetTotalDefense()
|
||||
int Mob::GetTotalDefense()
|
||||
{
|
||||
auto avoidance = compute_defense() + 10; // add 10 in case the NPC's stats are fucked
|
||||
@@ -286,6 +306,7 @@ int Mob::GetTotalDefense()
|
||||
|
||||
// called when a mob is attacked, does the checks to see if it's a hit
|
||||
// and does other mitigation checks. 'this' is the mob being attacked.
|
||||
// SYNC WITH : tune.cpp, mob.h TuneCheckHitChance()
|
||||
bool Mob::CheckHitChance(Mob* other, DamageHitInfo &hit)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
@@ -382,18 +403,32 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
ultimately end up being more useful as fields in npc_types.
|
||||
*/
|
||||
|
||||
int counter_all = 0;
|
||||
int counter_all = 0;
|
||||
int counter_riposte = 0;
|
||||
int counter_block = 0;
|
||||
int counter_parry = 0;
|
||||
int counter_dodge = 0;
|
||||
int counter_block = 0;
|
||||
int counter_parry = 0;
|
||||
int counter_dodge = 0;
|
||||
|
||||
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)) {
|
||||
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
|
||||
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
|
||||
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 1);
|
||||
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
|
||||
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
|
||||
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
|
||||
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
|
||||
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
|
||||
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
|
||||
}
|
||||
|
||||
int modify_all = 0;
|
||||
int modify_riposte = 0;
|
||||
int modify_block = 0;
|
||||
int modify_parry = 0;
|
||||
int modify_dodge = 0;
|
||||
|
||||
if (GetSpecialAbility(MODIFY_AVOID_DAMAGE)) {
|
||||
modify_all = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 0);
|
||||
modify_riposte = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 1);
|
||||
modify_block = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 2);
|
||||
modify_parry = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 3);
|
||||
modify_dodge = GetSpecialAbilityParam(MODIFY_AVOID_DAMAGE, 4);
|
||||
}
|
||||
|
||||
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
|
||||
@@ -420,6 +455,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
float counter = (counter_riposte + counter_all) / 100.0f;
|
||||
chance -= chance * counter;
|
||||
}
|
||||
if (modify_riposte || modify_all) {
|
||||
float npc_modifier = (modify_riposte + modify_all) / 100.0f;
|
||||
chance += chance * npc_modifier;
|
||||
}
|
||||
// AA Slippery Attacks
|
||||
if (hit.hand == EQ::invslot::slotSecondary) {
|
||||
int slip = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail;
|
||||
@@ -459,6 +498,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
float counter = (counter_block + counter_all) / 100.0f;
|
||||
chance -= chance * counter;
|
||||
}
|
||||
if (modify_block || modify_all) {
|
||||
float npc_modifier = (modify_block + modify_all) / 100.0f;
|
||||
chance += chance * npc_modifier;
|
||||
}
|
||||
if (zone->random.Roll(chance)) {
|
||||
hit.damage_done = DMG_BLOCKED;
|
||||
return true;
|
||||
@@ -482,6 +525,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
float counter = (counter_parry + counter_all) / 100.0f;
|
||||
chance -= chance * counter;
|
||||
}
|
||||
if (modify_parry || modify_all) {
|
||||
float npc_modifier = (modify_parry + modify_all) / 100.0f;
|
||||
chance += chance * npc_modifier;
|
||||
}
|
||||
if (zone->random.Roll(chance)) {
|
||||
hit.damage_done = DMG_PARRIED;
|
||||
return true;
|
||||
@@ -505,6 +552,10 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
float counter = (counter_dodge + counter_all) / 100.0f;
|
||||
chance -= chance * counter;
|
||||
}
|
||||
if (modify_dodge || modify_all) {
|
||||
float npc_modifier = (modify_dodge + modify_all) / 100.0f;
|
||||
chance += chance * npc_modifier;
|
||||
}
|
||||
if (zone->random.Roll(chance)) {
|
||||
hit.damage_done = DMG_DODGED;
|
||||
return true;
|
||||
@@ -788,7 +839,7 @@ int Mob::GetClassRaceACBonus()
|
||||
|
||||
return ac_bonus;
|
||||
}
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h TuneACSum
|
||||
int Mob::ACSum(bool skip_caps)
|
||||
{
|
||||
int ac = 0; // this should be base AC whenever shrouds come around
|
||||
@@ -863,7 +914,7 @@ int Mob::ACSum(bool skip_caps)
|
||||
}
|
||||
|
||||
int Mob::GetBestMeleeSkill()
|
||||
{
|
||||
{
|
||||
int bestSkill=0;
|
||||
|
||||
EQ::skills::SkillType meleeSkills[]=
|
||||
@@ -885,8 +936,8 @@ int Mob::GetBestMeleeSkill()
|
||||
}
|
||||
|
||||
return bestSkill;
|
||||
}
|
||||
|
||||
}
|
||||
//SYNC WITH: tune.cpp, mob.h Tuneoffense
|
||||
int Mob::offense(EQ::skills::SkillType skill)
|
||||
{
|
||||
int offense = GetSkill(skill);
|
||||
@@ -941,7 +992,7 @@ double Mob::RollD20(int offense, int mitigation)
|
||||
|
||||
return mods[index];
|
||||
}
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h TuneMeleeMitigation
|
||||
void Mob::MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
@@ -990,32 +1041,35 @@ int Mob::GetWeaponDamage(Mob *against, const EQ::ItemData *weapon_item) {
|
||||
|
||||
//check to see if our weapons or fists are magical.
|
||||
if (against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)) {
|
||||
if (weapon_item) {
|
||||
if (GetSpecialAbility(SPECATK_MAGICAL)) {
|
||||
dmg = 1;
|
||||
}
|
||||
//On live this occurs for ALL NPC's >= 10
|
||||
else if (IsNPC() && GetLevel() >= RuleI(Combat, NPCAttackMagicLevel)) {
|
||||
dmg = 1;
|
||||
}
|
||||
else if (weapon_item) {
|
||||
if (weapon_item->Magic) {
|
||||
dmg = weapon_item->Damage;
|
||||
|
||||
//this is more for non weapon items, ex: boots for kick
|
||||
//they don't have a dmg but we should be able to hit magical
|
||||
dmg = dmg <= 0 ? 1 : dmg;
|
||||
if (weapon_item->Damage && (weapon_item->IsType1HWeapon() || weapon_item->IsType2HWeapon())) {
|
||||
dmg = weapon_item->Damage;
|
||||
}
|
||||
//Non weapon items, ie. boots for kick.
|
||||
else if (weapon_item->ItemType == EQ::item::ItemTypeArmor) {
|
||||
dmg = 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) {
|
||||
dmg = GetHandToHandDamage();
|
||||
}
|
||||
else {
|
||||
if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) {
|
||||
dmg = GetHandToHandDamage();
|
||||
}
|
||||
else if (GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)) {
|
||||
//pets wouldn't actually use this but...
|
||||
//it gives us an idea if we can hit due to the dual nature of this function
|
||||
dmg = 1;
|
||||
}
|
||||
else if (GetSpecialAbility(SPECATK_MAGICAL))
|
||||
{
|
||||
dmg = 1;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1314,6 +1368,7 @@ int Client::DoDamageCaps(int base_damage)
|
||||
}
|
||||
|
||||
// other is the defender, this is the attacker
|
||||
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
|
||||
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
||||
{
|
||||
if (!other)
|
||||
@@ -1371,6 +1426,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
||||
//note: throughout this method, setting `damage` to a negative is a way to
|
||||
//stop the attack calculations
|
||||
// IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.)
|
||||
//SYNC WITH: tune.cpp, mob.h TuneClientAttack
|
||||
bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
||||
{
|
||||
if (!other) {
|
||||
@@ -1552,7 +1608,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
float chance = aabonuses.SkillAttackProc[SBIndex::SKILLPROC_CHANCE] / 1000.0f;
|
||||
if (zone->random.Roll(chance))
|
||||
SpellFinished(aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID], other, EQ::spells::CastingSlot::Item, 0, -1,
|
||||
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].ResistDiff);
|
||||
spells[aabonuses.SkillAttackProc[SBIndex::SKILLPROC_SPELL_ID]].resist_difficulty);
|
||||
}
|
||||
other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks);
|
||||
|
||||
@@ -1628,9 +1684,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
|
||||
if (!spell)
|
||||
spell = SPELL_UNKNOWN;
|
||||
|
||||
char buffer[48] = { 0 };
|
||||
snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
|
||||
if (parse->EventPlayer(EVENT_DEATH, this, buffer, 0) != 0) {
|
||||
std::string export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
killerMob ? killerMob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
);
|
||||
if (parse->EventPlayer(EVENT_DEATH, this, export_string, 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
@@ -1685,11 +1746,17 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
|
||||
// #2: figure out things that affect the player dying and mark them dead
|
||||
|
||||
InterruptSpell();
|
||||
|
||||
Mob* m_pet = GetPet();
|
||||
SetPet(0);
|
||||
SetHorseId(0);
|
||||
ShieldAbilityClearVariables();
|
||||
dead = true;
|
||||
|
||||
if (m_pet && m_pet->IsCharmed()) {
|
||||
m_pet->BuffFadeByEffect(SE_Charm);
|
||||
}
|
||||
|
||||
if (GetMerc()) {
|
||||
GetMerc()->Suspend();
|
||||
}
|
||||
@@ -1926,10 +1993,10 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQ::skills::Skill
|
||||
QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc);
|
||||
}
|
||||
|
||||
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0);
|
||||
parse->EventPlayer(EVENT_DEATH_COMPLETE, this, export_string, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
|
||||
bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
||||
{
|
||||
if (!other) {
|
||||
@@ -2181,7 +2248,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQ::skills::SkillTyp
|
||||
if (IsLDoNTrapped())
|
||||
{
|
||||
MessageString(Chat::Red, LDON_ACCIDENT_SETOFF2);
|
||||
SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
|
||||
SpellFinished(GetLDoNTrapSpellID(), other, EQ::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].resist_difficulty, false);
|
||||
SetLDoNTrapSpellID(0);
|
||||
SetLDoNTrapped(false);
|
||||
SetLDoNTrapDetected(false);
|
||||
@@ -2206,11 +2273,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
|
||||
Mob *oos = nullptr;
|
||||
if (killer_mob) {
|
||||
oos = killer_mob->GetOwnerOrSelf();
|
||||
|
||||
char buffer[48] = { 0 };
|
||||
snprintf(buffer, 47, "%d %d %d %d", killer_mob->GetID(), damage, spell, static_cast<int>(attack_skill));
|
||||
|
||||
if (parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0) {
|
||||
std::string buffer = fmt::format("{} {} {} {}", killer_mob->GetID(), damage, spell, static_cast<int>(attack_skill));
|
||||
if (parse->EventNPC(EVENT_DEATH, this, oos, buffer.c_str(), 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
@@ -2233,10 +2297,8 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
}
|
||||
else {
|
||||
char buffer[48] = { 0 };
|
||||
snprintf(buffer, 47, "%d %d %d %d", 0, damage, spell, static_cast<int>(attack_skill));
|
||||
|
||||
if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0) {
|
||||
std::string buffer = fmt::format("{} {} {} {}", 0, damage, spell, static_cast<int>(attack_skill));
|
||||
if (parse->EventNPC(EVENT_DEATH, this, nullptr, buffer.c_str(), 0) != 0) {
|
||||
if (GetHP() < 0) {
|
||||
SetHP(0);
|
||||
}
|
||||
@@ -2357,9 +2419,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
|
||||
give_exp_client->GetCleanName(),
|
||||
GetNPCTypeID()
|
||||
);
|
||||
give_exp_client
|
||||
->GetTaskState()
|
||||
->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID());
|
||||
task_manager->HandleUpdateTasksOnKill(give_exp_client, GetNPCTypeID());
|
||||
}
|
||||
|
||||
if (kr) {
|
||||
@@ -2629,16 +2689,15 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
entity_list.UpdateFindableNPCState(this, true);
|
||||
|
||||
char buffer[48] = { 0 };
|
||||
snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0);
|
||||
std::string buffer = fmt::format("{} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill));
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer.c_str(), 0);
|
||||
|
||||
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
|
||||
if (RuleB(Zone, UseZoneController)) {
|
||||
if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && this->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) {
|
||||
char data_pass[100] = { 0 };
|
||||
snprintf(data_pass, 99, "%d %d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill), this->GetNPCTypeID());
|
||||
parse->EventNPC(EVENT_DEATH_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
|
||||
auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID);
|
||||
if (controller && GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID) {
|
||||
std::string data_pass = fmt::format("{} {} {} {} {}", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(attack_skill), GetNPCTypeID());
|
||||
parse->EventNPC(EVENT_DEATH_ZONE, controller, nullptr, data_pass.c_str(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2701,8 +2760,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
}
|
||||
}
|
||||
|
||||
if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0))
|
||||
TryTriggerOnValueAmount(false, false, false, true);
|
||||
if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) {
|
||||
TryTriggerOnCastRequirement();
|
||||
}
|
||||
|
||||
if (IsClient() && !IsAIControlled())
|
||||
return;
|
||||
@@ -2876,10 +2936,15 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
|
||||
spellid = spellbonuses.DamageShieldSpellID;
|
||||
}
|
||||
else {
|
||||
DS = spellbonuses.SpellDamageShield;
|
||||
DS = spellbonuses.SpellDamageShield + itembonuses.SpellDamageShield + aabonuses.SpellDamageShield;
|
||||
rev_ds = 0;
|
||||
// This ID returns "you are burned", seemed most appropriate for spell DS
|
||||
spellid = 2166;
|
||||
/*
|
||||
Live Message - not yet used on emu
|
||||
Feedback onto you "YOUR mind burns from TARGETS NAME's feedback for %i points of non-melee damage."
|
||||
Feedback onto other "TARGETS NAME's mind burns from YOUR feedback for %i points of non-melee damage."
|
||||
*/
|
||||
}
|
||||
|
||||
if (DS == 0 && rev_ds == 0)
|
||||
@@ -2913,6 +2978,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
|
||||
|
||||
DS -= DS * ds_mitigation / 100;
|
||||
}
|
||||
|
||||
attacker->Damage(this, -DS, spellid, EQ::skills::SkillAbjuration/*hackish*/, false);
|
||||
//we can assume there is a spell now
|
||||
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
|
||||
@@ -3128,7 +3194,7 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
if (spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) {
|
||||
slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT];
|
||||
if (slot >= 0) {
|
||||
if (--buffs[slot].numhits == 0) {
|
||||
if (--buffs[slot].hit_number == 0) {
|
||||
|
||||
if (!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot, true);
|
||||
@@ -3217,7 +3283,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
if (!iBuffTic && spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_EXISTS]) {
|
||||
slot = spellbonuses.NegateAttacks[SBIndex::NEGATE_ATK_BUFFSLOT];
|
||||
if (slot >= 0) {
|
||||
if (--buffs[slot].numhits == 0) {
|
||||
if (--buffs[slot].hit_number == 0) {
|
||||
|
||||
if (!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot, true);
|
||||
@@ -3343,7 +3409,7 @@ int32 Mob::ReduceAllDamage(int32 damage)
|
||||
if (GetMana() >= mana_reduced) {
|
||||
damage -= mana_reduced;
|
||||
SetMana(GetMana() - mana_reduced);
|
||||
TryTriggerOnValueAmount(false, true);
|
||||
TryTriggerOnCastRequirement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3356,7 +3422,7 @@ int32 Mob::ReduceAllDamage(int32 damage)
|
||||
if (IsClient() && CastToClient()->GetEndurance() >= endurance_drain) {
|
||||
damage -= damage_reduced;
|
||||
CastToClient()->SetEndurance(CastToClient()->GetEndurance() - endurance_drain);
|
||||
TryTriggerOnValueAmount(false, false, true);
|
||||
TryTriggerOnCastRequirement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3367,17 +3433,37 @@ int32 Mob::ReduceAllDamage(int32 damage)
|
||||
|
||||
bool Mob::HasProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++)
|
||||
if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN)
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.SpellProc[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::HasDefensiveProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++)
|
||||
if (DefensiveProcs[i].spellID != SPELL_UNKNOWN)
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.DefensiveProc[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3402,9 +3488,19 @@ bool Mob::HasSkillProcSuccess() const
|
||||
|
||||
bool Mob::HasRangedProcs() const
|
||||
{
|
||||
for (int i = 0; i < MAX_PROCS; i++)
|
||||
if (RangedProcs[i].spellID != SPELL_UNKNOWN)
|
||||
for (int i = 0; i < MAX_PROCS; i++){
|
||||
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
if (aabonuses.RangedProc[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3524,7 +3620,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
|
||||
if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) {
|
||||
int healed = damage;
|
||||
|
||||
healed = attacker->GetActSpellHealing(spell_id, healed);
|
||||
healed = RuleB(Spells, CompoundLifetapHeals) ? attacker->GetActSpellHealing(spell_id, healed) : healed;
|
||||
LogCombat("Applying lifetap heal of [{}] to [{}]", healed, attacker->GetName());
|
||||
attacker->HealDamage(healed);
|
||||
|
||||
@@ -3646,7 +3742,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
|
||||
TryDeathSave();
|
||||
}
|
||||
|
||||
TryTriggerOnValueAmount(true);
|
||||
TryTriggerOnCastRequirement();
|
||||
|
||||
//fade mez if we are mezzed
|
||||
if (IsMezzed() && attacker) {
|
||||
@@ -4038,33 +4134,61 @@ void Mob::TryDefensiveProc(Mob *on, uint16 hand) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasDefensiveProcs())
|
||||
if (!HasDefensiveProcs()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!on->HasDied() && on->GetHP() > 0) {
|
||||
|
||||
float ProcChance, ProcBonus;
|
||||
on->GetDefensiveProcChances(ProcBonus, ProcChance, hand, this);
|
||||
|
||||
if (hand != EQ::invslot::slotPrimary)
|
||||
if (hand == EQ::invslot::slotSecondary) {
|
||||
ProcChance /= 2;
|
||||
}
|
||||
|
||||
int level_penalty = 0;
|
||||
int level_diff = GetLevel() - on->GetLevel();
|
||||
if (level_diff > 6)//10% penalty per level if > 6 levels over target.
|
||||
if (level_diff > 6) {//10% penalty per level if > 6 levels over target.
|
||||
level_penalty = (level_diff - 6) * 10;
|
||||
}
|
||||
|
||||
ProcChance -= ProcChance*level_penalty / 100;
|
||||
|
||||
if (ProcChance < 0)
|
||||
if (ProcChance < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Spell Procs and Quest added procs
|
||||
for (int i = 0; i < MAX_PROCS; i++) {
|
||||
if (IsValidSpell(DefensiveProcs[i].spellID)) {
|
||||
float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID);
|
||||
if (!IsProcLimitTimerActive(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc)) {
|
||||
float chance = ProcChance * (static_cast<float>(DefensiveProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(NumHit::DefensiveSpellProcs, 0, DefensiveProcs[i].base_spellID);
|
||||
SetProcLimitTimer(DefensiveProcs[i].base_spellID, DefensiveProcs[i].proc_reuse_time, SE_DefensiveProc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AA Procs
|
||||
if (IsClient()){
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
int32 aa_rank_id = aabonuses.DefensiveProc[i];
|
||||
int32 aa_spell_id = aabonuses.DefensiveProc[i + 1];
|
||||
int32 aa_proc_chance = 100 + aabonuses.DefensiveProc[i + 2];
|
||||
uint32 aa_proc_reuse_timer = aabonuses.DefensiveProc[i + 3];
|
||||
|
||||
if (aa_rank_id) {
|
||||
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc)) {
|
||||
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
|
||||
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
|
||||
ExecWeaponProc(nullptr, aa_spell_id, on);
|
||||
SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, SE_DefensiveProc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4109,7 +4233,9 @@ void Mob::TryWeaponProc(const EQ::ItemInstance* weapon_g, Mob *on, uint16 hand)
|
||||
|
||||
void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand)
|
||||
{
|
||||
|
||||
if (!on) {
|
||||
return;
|
||||
}
|
||||
if (!weapon)
|
||||
return;
|
||||
uint16 skillinuse = 28;
|
||||
@@ -4119,7 +4245,7 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon
|
||||
ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
|
||||
float ProcChance = GetProcChances(ProcBonus, hand);
|
||||
|
||||
if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate?
|
||||
if (hand == EQ::invslot::slotSecondary)
|
||||
ProcChance /= 2;
|
||||
|
||||
// Try innate proc on weapon
|
||||
@@ -4193,12 +4319,16 @@ void Mob::TryWeaponProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon
|
||||
|
||||
void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon, Mob *on, uint16 hand)
|
||||
{
|
||||
if (!on) {
|
||||
return;
|
||||
}
|
||||
|
||||
float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
|
||||
itembonuses.SpellProcChance + aabonuses.SpellProcChance);
|
||||
float ProcChance = 0.0f;
|
||||
ProcChance = GetProcChances(ProcBonus, hand);
|
||||
|
||||
if (hand != EQ::invslot::slotPrimary) //Is Archery intened to proc at 50% rate?
|
||||
if (hand == EQ::invslot::slotSecondary)
|
||||
ProcChance /= 2;
|
||||
|
||||
bool rangedattk = false;
|
||||
@@ -4226,7 +4356,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
|
||||
|
||||
// Not ranged
|
||||
if (!rangedattk) {
|
||||
// Perma procs (AAs)
|
||||
// Perma procs (Not used for AA, they are handled below)
|
||||
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus?
|
||||
LogCombat("Permanent proc [{}] procing spell [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
|
||||
@@ -4243,32 +4373,79 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
|
||||
poison_slot=i;
|
||||
continue; // Process the poison proc last per @mackal
|
||||
}
|
||||
|
||||
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
SendBeginCast(SpellProcs[i].spellID, 0);
|
||||
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
|
||||
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
|
||||
SpellProcs[i].base_spellID);
|
||||
}
|
||||
else {
|
||||
LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
|
||||
if (!IsProcLimitTimerActive(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc)) {
|
||||
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
LogCombat("Spell proc [{}] procing spell [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
SendBeginCast(SpellProcs[i].spellID, 0);
|
||||
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
|
||||
SetProcLimitTimer(SpellProcs[i].base_spellID, SpellProcs[i].proc_reuse_time, SE_WeaponProc);
|
||||
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, SpellProcs[i].base_spellID);
|
||||
}
|
||||
else {
|
||||
LogCombat("Spell proc [{}] failed to proc [{}] ([{}] percent chance)", i, SpellProcs[i].spellID, chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rangedattk) { // ranged only
|
||||
// ranged spell procs (buffs)
|
||||
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
|
||||
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
|
||||
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
|
||||
RangedProcs[i].base_spellID);
|
||||
|
||||
if (!IsProcLimitTimerActive(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc)) {
|
||||
float chance = ProcChance * (static_cast<float>(RangedProcs[i].chance) / 100.0f);
|
||||
if (zone->random.Roll(chance)) {
|
||||
LogCombat("Ranged proc [{}] procing spell [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
|
||||
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
|
||||
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, RangedProcs[i].base_spellID);
|
||||
SetProcLimitTimer(RangedProcs[i].base_spellID, RangedProcs[i].proc_reuse_time, SE_RangedProc);
|
||||
}
|
||||
else {
|
||||
LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogCombat("Ranged proc [{}] failed to proc [{}] ([{}] percent chance)", i, RangedProcs[i].spellID, chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//AA Procs
|
||||
if (IsClient()) {
|
||||
for (int i = 0; i < MAX_AA_PROCS; i += 4) {
|
||||
|
||||
int32 aa_rank_id = 0;
|
||||
int32 aa_spell_id = SPELL_UNKNOWN;
|
||||
int32 aa_proc_chance = 100;
|
||||
uint32 aa_proc_reuse_timer = 0;
|
||||
int proc_type = 0; //used to deterimne which timer array is used.
|
||||
|
||||
if (!rangedattk) {
|
||||
|
||||
aa_rank_id = aabonuses.SpellProc[i];
|
||||
aa_spell_id = aabonuses.SpellProc[i + 1];
|
||||
aa_proc_chance += aabonuses.SpellProc[i + 2];
|
||||
aa_proc_reuse_timer = aabonuses.SpellProc[i + 3];
|
||||
proc_type = SE_WeaponProc;
|
||||
}
|
||||
else {
|
||||
aa_rank_id = aabonuses.RangedProc[i];
|
||||
aa_spell_id = aabonuses.RangedProc[i + 1];
|
||||
aa_proc_chance += aabonuses.RangedProc[i + 2];
|
||||
aa_proc_reuse_timer = aabonuses.RangedProc[i + 3];
|
||||
proc_type = SE_RangedProc;
|
||||
}
|
||||
|
||||
if (aa_rank_id) {
|
||||
if (!IsProcLimitTimerActive(-aa_rank_id, aa_proc_reuse_timer, proc_type)) {
|
||||
float chance = ProcChance * (static_cast<float>(aa_proc_chance) / 100.0f);
|
||||
if (zone->random.Roll(chance) && IsValidSpell(aa_spell_id)) {
|
||||
LogCombat("AA proc [{}] procing spell [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance);
|
||||
ExecWeaponProc(nullptr, aa_spell_id, on);
|
||||
SetProcLimitTimer(-aa_rank_id, aa_proc_reuse_timer, proc_type);
|
||||
}
|
||||
else {
|
||||
LogCombat("AA proc [{}] failed to proc [{}] ([{}] percent chance)", aa_rank_id, aa_spell_id, chance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4905,14 +5082,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
|
||||
if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) {
|
||||
proc_spell_id = spells[base_spell_id].base[i];
|
||||
ProcMod = static_cast<float>(spells[base_spell_id].base2[i]);
|
||||
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
|
||||
proc_spell_id = spells[base_spell_id].base_value[i];
|
||||
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
|
||||
}
|
||||
|
||||
else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) {
|
||||
else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) {
|
||||
|
||||
if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) {
|
||||
if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) {
|
||||
float final_chance = chance * (ProcMod / 100.0f);
|
||||
if (zone->random.Roll(final_chance)) {
|
||||
ExecWeaponProc(nullptr, proc_spell_id, on);
|
||||
@@ -4949,14 +5126,14 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
|
||||
ProcMod = 0;
|
||||
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (spells[base_spell_id].effectid[i] == SE_SkillProc || spells[base_spell_id].effectid[i] == SE_SkillProcSuccess) {
|
||||
proc_spell_id = spells[base_spell_id].base[i];
|
||||
ProcMod = static_cast<float>(spells[base_spell_id].base2[i]);
|
||||
if (spells[base_spell_id].effect_id[i] == SE_SkillProc || spells[base_spell_id].effect_id[i] == SE_SkillProcSuccess) {
|
||||
proc_spell_id = spells[base_spell_id].base_value[i];
|
||||
ProcMod = static_cast<float>(spells[base_spell_id].limit_value[i]);
|
||||
}
|
||||
|
||||
else if (spells[base_spell_id].effectid[i] == SE_LimitToSkill && spells[base_spell_id].base[i] <= EQ::skills::HIGHEST_SKILL) {
|
||||
else if (spells[base_spell_id].effect_id[i] == SE_LimitToSkill && spells[base_spell_id].base_value[i] <= EQ::skills::HIGHEST_SKILL) {
|
||||
|
||||
if (CanProc && spells[base_spell_id].base[i] == skill && IsValidSpell(proc_spell_id)) {
|
||||
if (CanProc && spells[base_spell_id].base_value[i] == skill && IsValidSpell(proc_spell_id)) {
|
||||
float final_chance = chance * (ProcMod / 100.0f);
|
||||
if (zone->random.Roll(final_chance)) {
|
||||
ExecWeaponProc(nullptr, proc_spell_id, on);
|
||||
@@ -4978,8 +5155,8 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
|
||||
|
||||
CanProc = true;
|
||||
uint32 effect_id = 0;
|
||||
int32 base1 = 0;
|
||||
int32 base2 = 0;
|
||||
int32 base_value = 0;
|
||||
int32 limit_value = 0;
|
||||
uint32 slot = 0;
|
||||
|
||||
for (int e = 0; e < MAX_SKILL_PROCS; e++) {
|
||||
@@ -5007,17 +5184,17 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
|
||||
|
||||
for (auto &effect : rank->effects) {
|
||||
effect_id = effect.effect_id;
|
||||
base1 = effect.base1;
|
||||
base2 = effect.base2;
|
||||
base_value = effect.base_value;
|
||||
limit_value = effect.limit_value;
|
||||
slot = effect.slot;
|
||||
|
||||
if (effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) {
|
||||
proc_spell_id = base1;
|
||||
ProcMod = static_cast<float>(base2);
|
||||
proc_spell_id = base_value;
|
||||
ProcMod = static_cast<float>(limit_value);
|
||||
}
|
||||
else if (effect_id == SE_LimitToSkill && base1 <= EQ::skills::HIGHEST_SKILL) {
|
||||
else if (effect_id == SE_LimitToSkill && base_value <= EQ::skills::HIGHEST_SKILL) {
|
||||
|
||||
if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) {
|
||||
if (CanProc && base_value == skill && IsValidSpell(proc_spell_id)) {
|
||||
float final_chance = chance * (ProcMod / 100.0f);
|
||||
|
||||
if (zone->random.Roll(final_chance)) {
|
||||
@@ -5046,7 +5223,7 @@ float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) {
|
||||
if (!ReuseTime && hand) {
|
||||
weapon_speed = GetWeaponSpeedbyHand(hand);
|
||||
ProcChance = static_cast<float>(weapon_speed) * (RuleR(Combat, AvgProcsPerMinute) / 60000.0f);
|
||||
if (hand != EQ::invslot::slotPrimary)
|
||||
if (hand == EQ::invslot::slotSecondary)
|
||||
ProcChance /= 2;
|
||||
}
|
||||
|
||||
@@ -5387,6 +5564,8 @@ void Mob::SetAttackTimer()
|
||||
void Client::SetAttackTimer()
|
||||
{
|
||||
float haste_mod = GetHaste() * 0.01f;
|
||||
int primary_speed = 0;
|
||||
int secondary_speed = 0;
|
||||
|
||||
//default value for attack timer in case they have
|
||||
//an invalid weapon equipped:
|
||||
@@ -5464,6 +5643,21 @@ void Client::SetAttackTimer()
|
||||
speed = static_cast<int>(speed + ((hhe / 100.0f) * delay));
|
||||
}
|
||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true);
|
||||
|
||||
if (i == EQ::invslot::slotPrimary) {
|
||||
primary_speed = speed;
|
||||
}
|
||||
else if (i == EQ::invslot::slotSecondary) {
|
||||
secondary_speed = speed;
|
||||
}
|
||||
}
|
||||
|
||||
//To allow for duel wield animation to display correctly if both weapons have same delay
|
||||
if (primary_speed == secondary_speed) {
|
||||
SetDualWieldingSameDelayWeapons(1);
|
||||
}
|
||||
else {
|
||||
SetDualWieldingSameDelayWeapons(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -98,7 +98,7 @@ bool Beacon::Process()
|
||||
{
|
||||
// NPCs should never be affected by an AE they cast. PB AEs shouldn't affect caster either
|
||||
// I don't think any other cases that get here matter
|
||||
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].targettype != ST_AECaster;
|
||||
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()) && spells[spell_id].target_type != ST_AECaster;
|
||||
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
|
||||
}
|
||||
else
|
||||
@@ -127,10 +127,10 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj
|
||||
caster_id = caster->GetID();
|
||||
spell_id = cast_spell_id;
|
||||
this->resist_adjust = resist_adjust;
|
||||
spell_iterations = spells[spell_id].AEDuration / 2500;
|
||||
spell_iterations = spells[spell_id].aoe_duration / 2500;
|
||||
spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1
|
||||
if (spells[spell_id].aemaxtargets)
|
||||
max_targets = spells[spell_id].aemaxtargets;
|
||||
if (spells[spell_id].aoe_max_targets)
|
||||
max_targets = spells[spell_id].aoe_max_targets;
|
||||
spell_timer.Start(2500);
|
||||
spell_timer.Trigger();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user