Compare commits

...

23 Commits

Author SHA1 Message Date
JJ 4120d94fc8 Change default login server 2026-06-22 17:28:56 -04:00
nytmyr 96c32e6189 [Bots] Make ^itemuse use BaseRace (#5102)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-06-17 21:23:08 -07:00
nytmyr 1a9ef6ea39 [Bots] Add bot owner to hatelist on bot pet social aggro (#5103) 2026-06-17 21:22:50 -07:00
nytmyr 36d6613a47 [Bots] Change how bots handle blind (#5104) 2026-06-17 21:22:33 -07:00
KimLS 8fd083b95c Update workflow for vs2026
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-06-09 19:30:33 -07:00
Nazwadi f66b8607d3 Fix CheckForCompatibleQuestPlugins to use config-driven directory paths (#5097)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-06-07 09:50:25 -07:00
nytmyr 584320202f [Combat] Monks no longer double kick (#5090)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-05-31 11:19:13 -07:00
nytmyr e089899e11 [Bots] Verify Tiger Claw on Monk Attacks (#5089) 2026-05-31 11:17:51 -07:00
nytmyr 854fd3eff9 [Bots] Fix saving of expansion settings for new bots (#5088) 2026-05-31 11:17:25 -07:00
Dan ca704c7f88 feat(Scripting): Created two perl/lua scripting hooks for merchants (#5083)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-05-13 00:18:36 -07:00
Knightly ef6dfe0469 Load openssl libs from executable directory and update logging (#5078)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-05-07 20:25:01 -07:00
Dan d7e010a3ec strings: refactor Money and add MoneyShort (#5075) 2026-05-07 20:24:15 -07:00
nytmyr 85c3255568 [Bots] Command add fix for spelltypeids/spelltypenames (#5074)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-04-28 23:59:19 -07:00
KimLS c6ddf300a4 Add back msvc step with a maintained fork
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run
2026-04-28 20:15:06 -07:00
KimLS 42d6004467 Remove step i think isn't required for windows, change linux path 2026-04-28 20:00:01 -07:00
KimLS cabe06ea92 Revert linux part for now 2026-04-28 19:42:26 -07:00
KimLS 4abd9c1b40 Some build changes, may or may not fix cache
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run
2026-04-28 18:01:20 -07:00
KimLS f72bbab0e9 Change paths to try to get this working
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run
2026-04-28 00:04:40 -07:00
KimLS 9647a5b82c Add develop branch 2026-04-27 23:20:29 -07:00
KimLS 84a90cef23 Linux build doesn't like the way we hash this 2026-04-27 22:22:54 -07:00
KimLS a8db057440 Add vcpkg cache, hopefully works. 2026-04-27 22:19:15 -07:00
Knightly 6d6cc8ca95 Update openssl calls to use EVP interface (#5072)
Build / Linux (push) Has been cancelled
Build / Windows (push) Has been cancelled
2026-04-25 16:49:03 -07:00
brainiac 6694281f22 Fix gcc warnings (#5071)
Build / Linux (push) Waiting to run
Build / Windows (push) Waiting to run
2026-04-24 23:05:32 -07:00
47 changed files with 552 additions and 288 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
"loginserver1": {
"account": "",
"password": "",
"legacy": 0,
"host": "login.projecteq.net",
"legacy": 1,
"host": "login.eqemulator.net",
"port": "5998"
},
"tcp": {
+27 -3
View File
@@ -3,8 +3,8 @@ on:
push:
branches:
- master
- develop
pull_request:
jobs:
linux:
name: Linux
@@ -25,6 +25,17 @@ jobs:
sudo apt-get update
sudo apt-get install -y build-essential ninja-build ccache uuid-dev
- name: Restore vcpkg Cache
uses: actions/cache@v5
with:
path: |
build/vcpkg_installed
submodules/vcpkg/downloads
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
restore-keys: |
${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}-
${{ runner.os }}-vcpkg-
- name: Configure
run: |
cmake -S . -B build -G Ninja \
@@ -47,6 +58,9 @@ jobs:
windows:
name: Windows
runs-on: windows-latest
env:
VCPKG_DOWNLOADS: ${{ github.workspace }}\submodules\vcpkg\downloads
VCPKG_BINARY_SOURCES: 'clear;files,${{ github.workspace }}\vcpkg_archives,readwrite'
steps:
- name: Checkout source
uses: actions/checkout@v5
@@ -57,14 +71,24 @@ jobs:
run: git config --global core.longpaths true
- name: Setup MSVC environment
uses: ilammy/msvc-dev-cmd@v1
uses: TheMrMilchmann/setup-msvc-dev@v4
with:
arch: x64
- name: Restore vcpkg Cache
uses: actions/cache@v5
with:
path: |
${{ env.VCPKG_DOWNLOADS }}
${{ github.workspace }}/vcpkg_archives
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
restore-keys: |
${{ runner.os }}-vcpkg-
- name: Configure
shell: pwsh
run: |
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 `
cmake -S . -B build -G "Visual Studio 18 2026" -A x64 `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
-DEQEMU_BUILD_TESTS=ON `
-DEQEMU_BUILD_LOGIN=ON `
+1 -1
View File
@@ -42,7 +42,7 @@ option(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
if(MSVC)
add_compile_options(/bigobj)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING _HAS_AUTO_PTR_ETC)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
option(EQEMU_DISABLE_MSVC_WARNINGS "Disable MSVC compile warnings." OFF)
+1
View File
@@ -6,6 +6,7 @@ set(common_sources
bodytypes.cpp
classes.cpp
cli/eqemu_command_handler.cpp
compiler_macros.h
compression.cpp
content/world_content_service.cpp
crash.cpp
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#if defined(_MSC_VER)
#define PUSH_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(push)) \
__pragma(warning(disable:4996))
#define POP_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(pop))
#elif defined(__GNUC__) || defined(__clang__)
#define PUSH_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
#define POP_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic pop")
#else
#define PUSH_DISABLE_DEPRECATED_WARNINGS()
#define POP_DISABLE_DEPRECATED_WARNINGS()
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#define UNREACHABLE() __assume(0)
#else
#define UNREACHABLE() __builtin_unreachable()
#endif
-8
View File
@@ -271,11 +271,7 @@ static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readVa
namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
typedef std::unique_ptr<CharReader> CharReaderPtr;
#else
typedef std::auto_ptr<CharReader> CharReaderPtr;
#endif
// Implementation of class Features
// ////////////////////////////////
@@ -4153,11 +4149,7 @@ Value& Path::make(Value& root) const {
namespace Json {
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
#else
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
#endif
static bool containsControlCharacter(const char* str) {
while (*str) {
+1
View File
@@ -908,6 +908,7 @@ RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spa
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
RULE_INT(Bots, BlindMoveChance, 5, "Chance for a bot to run to its target if it is blinded. Default 5.")
RULE_CATEGORY_END()
RULE_CATEGORY(Chat)
+47 -106
View File
@@ -380,118 +380,59 @@ std::string Strings::NumberToWords(unsigned long long int n)
return res;
}
std::string Strings::Money(uint64 platinum, uint64 gold, uint64 silver, uint64 copper)
{
std::string money_string = "Unknown";
if (copper && silver && gold && platinum) { // CSGP
money_string = fmt::format(
"{} platinum, {} gold, {} silver, and {} copper",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(silver)),
Strings::Commify(std::to_string(copper))
);
std::string Strings::Money(uint64 platinum, uint64 gold, uint64 silver, uint64 copper, bool commify) {
uint64 values[] = { platinum, gold, silver, copper };
const char* names[] = { " platinum", " gold", " silver", " copper" };
std::vector<std::string> parts;
for (int i = 0; i < 4; ++i) {
if (values[i] > 0) {
std::string s = std::to_string(values[i]);
parts.push_back((commify ? Strings::Commify(s) : s) + names[i]);
}
else if (copper && silver && !gold && platinum) { // CSP
money_string = fmt::format(
"{} platinum, {} silver, and {} copper",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(silver)),
Strings::Commify(std::to_string(copper))
);
}
else if (copper && silver && gold && !platinum) { // CSG
money_string = fmt::format(
"{} gold, {} silver, and {} copper",
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(silver)),
Strings::Commify(std::to_string(copper))
);
if (parts.empty()) return "0 copper";
if (parts.size() == 1) return parts[0];
std::string result;
for (size_t i = 0; i < parts.size(); ++i) {
result += parts[i];
if (i < parts.size() - 2) {
result += ", ";
}
else if (copper && !silver && !gold && platinum) { // CP
money_string = fmt::format(
"{} platinum and {} copper",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(copper))
);
else if (i == parts.size() - 2) {
// Oxford comma logic: ", and " for 3+ items, " and " for 2
result += (parts.size() > 2) ? ", and " : " and ";
}
else if (copper && silver && !gold && !platinum) { // CS
money_string = fmt::format(
"{} silver and {} copper",
Strings::Commify(std::to_string(silver)),
Strings::Commify(std::to_string(copper))
);
}
else if (!copper && silver && gold && platinum) { // SGP
money_string = fmt::format(
"{} platinum, {} gold, and {} silver",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(silver))
);
}
else if (!copper && silver && !gold && platinum) { // SP
money_string = fmt::format(
"{} platinum and {} silver",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(silver))
);
}
else if (!copper && silver && gold && !platinum) { // SG
money_string = fmt::format(
"{} gold and {} silver",
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(silver))
);
}
else if (copper && !silver && gold && platinum) { // CGP
money_string = fmt::format(
"{} platinum, {} gold, and {} copper",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(copper))
);
}
else if (copper && !silver && gold && !platinum) { // CG
money_string = fmt::format(
"{} gold and {} copper",
Strings::Commify(std::to_string(gold)),
Strings::Commify(std::to_string(copper))
);
}
else if (!copper && !silver && gold && platinum) { // GP
money_string = fmt::format(
"{} platinum and {} gold",
Strings::Commify(std::to_string(platinum)),
Strings::Commify(std::to_string(gold))
);
}
else if (!copper && !silver && !gold && platinum) { // P
money_string = fmt::format(
"{} platinum",
Strings::Commify(std::to_string(platinum))
);
}
else if (!copper && !silver && gold && !platinum) { // G
money_string = fmt::format(
"{} gold",
Strings::Commify(std::to_string(gold))
);
}
else if (!copper && silver && !gold && !platinum) { // S
money_string = fmt::format(
"{} silver",
Strings::Commify(std::to_string(silver))
);
}
else if (copper && !silver && !gold && !platinum) { // C
money_string = fmt::format(
"{} copper",
Strings::Commify(std::to_string(copper))
);
}
return money_string;
return result;
}
std::string Strings::MoneyShort(uint64 copper, bool commify) {
// Matches merchant format
uint64 values[] = {
copper / 1000,
(copper / 100) % 10,
(copper / 10) % 10,
copper % 10
};
const char* names[] = { " platinum", " gold", " silver", " copper" };
std::string result;
for (int i = 0; i < 4; ++i) {
if (values[i] > 0) {
if (!result.empty()) result += " ";
std::string s = std::to_string(values[i]);
result += (commify ? Strings::Commify(s) : s) + names[i];
}
}
return result.empty() ? "0 copper" : result;
}
std::string Strings::SecondsToTime(int duration, bool is_milliseconds)
{
if (duration <= 0) {
+2 -1
View File
@@ -62,7 +62,8 @@ public:
static std::string Join(const std::vector<std::string> &ar, const std::string &delim);
static std::string Join(const std::vector<uint32_t> &ar, const std::string &delim);
static std::string MillisecondsToTime(int duration);
static std::string Money(uint64 platinum, uint64 gold = 0, uint64 silver = 0, uint64 copper = 0);
static std::string Money(uint64 platinum, uint64 gold = 0, uint64 silver = 0, uint64 copper = 0, bool commify = true);
static std::string MoneyShort(uint64 copper = 0, bool commify = true); // Matches merchant format when commify is false
static std::string NumberToWords(unsigned long long int n);
static std::string Repeat(std::string s, int n);
static std::string Replace(std::string subject, const std::string &search, const std::string &replace);
+1 -1
View File
@@ -103,7 +103,7 @@ namespace luabind { namespace detail
if (luabind::move_back_reference(L, ptr))
return;
make_instance(L, std::auto_ptr<T>(ptr));
make_instance(L, std::unique_ptr<T>(ptr));
}
};
+1 -1
View File
@@ -335,7 +335,7 @@ namespace luabind
template <class T>
struct default_pointer<null_type, T>
{
typedef std::auto_ptr<T> type;
typedef std::unique_ptr<T> type;
};
template <class Class, class Pointer, class Signature, class Policies>
+4 -4
View File
@@ -46,7 +46,7 @@ struct construct_aux<0, T, Pointer, Signature>
object_rep* self = touserdata<object_rep>(self_);
class_rep* cls = self->crep();
std::auto_ptr<T> instance(new T);
std::unique_ptr<T> instance(new T);
inject_backref(self_.interpreter(), instance.get(), instance.get());
void* naked_ptr = instance.get();
@@ -55,7 +55,7 @@ struct construct_aux<0, T, Pointer, Signature>
void* storage = self->allocate(sizeof(holder_type));
self->set_instance(new (storage) holder_type(
ptr, registered_class<T>::id, naked_ptr, cls));
std::move(ptr), registered_class<T>::id, naked_ptr, cls));
}
};
@@ -92,7 +92,7 @@ struct construct_aux<N, T, Pointer, Signature>
object_rep* self = touserdata<object_rep>(self_);
class_rep* cls = self->crep();
std::auto_ptr<T> instance(new T(BOOST_PP_ENUM_PARAMS(N,_)));
std::unique_ptr<T> instance(new T(BOOST_PP_ENUM_PARAMS(N,_)));
inject_backref(self_.interpreter(), instance.get(), instance.get());
void* naked_ptr = instance.get();
@@ -101,7 +101,7 @@ struct construct_aux<N, T, Pointer, Signature>
void* storage = self->allocate(sizeof(holder_type));
self->set_instance(new (storage) holder_type(
ptr, registered_class<T>::id, naked_ptr, cls));
std::move(ptr), registered_class<T>::id, naked_ptr, cls));
}
};
@@ -58,7 +58,7 @@ namespace has_get_pointer_
T* get_pointer(T const volatile*);
template<class T>
T* get_pointer(std::auto_ptr<T> const&);
T* get_pointer(std::unique_ptr<T> const&);
# endif
@@ -57,7 +57,7 @@ inline mpl::true_ check_const_pointer(void const*)
}
template <class T>
void release_ownership(std::auto_ptr<T>& p)
void release_ownership(std::unique_ptr<T>& p)
{
p.release();
}
@@ -83,7 +83,7 @@ public:
P p, class_id dynamic_id, void* dynamic_ptr, class_rep* cls
)
: instance_holder(cls, check_const_pointer(false ? get_pointer(p) : 0))
, p(p)
, p(std::move(p))
, weak(0)
, dynamic_id(dynamic_id)
, dynamic_ptr(dynamic_ptr)
@@ -88,7 +88,7 @@ void make_instance(lua_State* L, P p)
try
{
new (storage) holder_type(p, dynamic.first, dynamic.second, cls);
new (storage) holder_type(std::move(p), dynamic.first, dynamic.second, cls);
}
catch (...)
{
+3 -3
View File
@@ -169,7 +169,7 @@ namespace luabind { namespace detail
{
if (get_pointer(x))
{
make_instance(L, x);
make_instance(L, std::move(x));
}
else
{
@@ -180,8 +180,8 @@ namespace luabind { namespace detail
template <class T>
void make_pointee_instance(lua_State* L, T& x, mpl::false_, mpl::true_)
{
std::auto_ptr<T> ptr(new T(x));
make_instance(L, ptr);
std::unique_ptr<T> ptr(new T(x));
make_instance(L, std::move(ptr));
}
template <class T>
+1 -1
View File
@@ -46,7 +46,7 @@ namespace detail
template <class F, class Policies>
scope def(char const* name, F f, Policies const& policies)
{
return scope(std::auto_ptr<detail::registration>(
return scope(std::unique_ptr<detail::registration>(
new detail::function_registration<F, Policies>(name, f, policies)));
}
+1 -1
View File
@@ -56,7 +56,7 @@ namespace luabind {
struct LUABIND_API scope
{
scope();
explicit scope(std::auto_ptr<detail::registration> reg);
explicit scope(std::unique_ptr<detail::registration> reg);
scope(scope const& other_);
~scope();
+5 -5
View File
@@ -235,7 +235,7 @@ namespace luabind { namespace detail {
// -- interface ---------------------------------------------------------
class_base::class_base(char const* name)
: scope(std::auto_ptr<registration>(
: scope(std::unique_ptr<registration>(
m_registration = new class_registration(name))
)
{
@@ -258,14 +258,14 @@ namespace luabind { namespace detail {
void class_base::add_member(registration* member)
{
std::auto_ptr<registration> ptr(member);
m_registration->m_members.operator,(scope(ptr));
std::unique_ptr<registration> ptr(member);
m_registration->m_members.operator,(scope(std::move(ptr)));
}
void class_base::add_default_member(registration* member)
{
std::auto_ptr<registration> ptr(member);
m_registration->m_default_members.operator,(scope(ptr));
std::unique_ptr<registration> ptr(member);
m_registration->m_default_members.operator,(scope(std::move(ptr)));
}
const char* class_base::name() const
+3 -2
View File
@@ -49,7 +49,8 @@ namespace luabind { namespace detail {
{
}
scope::scope(std::auto_ptr<detail::registration> reg)
scope::scope(std::unique_ptr<detail::registration> reg)
: m_chain(reg.release())
{
}
@@ -193,7 +194,7 @@ namespace luabind {
};
namespace_::namespace_(char const* name)
: scope(std::auto_ptr<detail::registration>(
: scope(std::unique_ptr<detail::registration>(
m_registration = new registration_(name)))
{
}
+10
View File
@@ -37,3 +37,13 @@ target_include_directories(loginserver PRIVATE ..)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set_property(TARGET loginserver PROPERTY FOLDER executables/servers)
# vcpkg doesn't copy legacy.dll automatically because it is loaded at runtime, not via the import table.
if(WIN32 AND DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET)
add_custom_command(TARGET loginserver POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<IF:$<CONFIG:Debug>,${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/bin/legacy.dll,${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/legacy.dll>"
"$<TARGET_FILE_DIR:loginserver>/legacy.dll"
VERBATIM
)
endif()
+102 -16
View File
@@ -16,11 +16,12 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "encryption.h"
#include "common/compiler_macros.h"
#ifdef EQEMU_USE_OPENSSL
#include <openssl/des.h>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/provider.h>
#endif
#ifdef EQEMU_USE_MBEDTLS
#include <mbedtls/des.h>
@@ -32,6 +33,8 @@
#include <cstring>
#include <string>
#include <memory>
#ifdef ENABLE_SECURITY
#include <sodium.h>
@@ -127,21 +130,104 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff
#endif
#ifdef EQEMU_USE_OPENSSL
DES_key_schedule k;
DES_cblock v;
memset(&k, 0, sizeof(DES_key_schedule));
memset(&v, 0, sizeof(DES_cblock));
// Decrypt requires block-aligned input; encrypt zero-pads a trailing
// partial block to match the legacy DES_ncbc_encrypt semantics the
// game protocol expects.
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
return nullptr;
}
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
unsigned char key[8] = {0};
unsigned char iv[8] = {0};
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
if (!ctx) {
return nullptr;
}
bool result = EVP_CipherInit_ex2(ctx, EVP_des_cbc(), key, iv, enc, nullptr) == 1;
if (result) {
EVP_CIPHER_CTX_set_padding(ctx, 0);
const unsigned char* src = reinterpret_cast<const unsigned char*>(buffer_in);
size_t src_len = buffer_in_sz;
std::unique_ptr<unsigned char[]> padded;
if (enc && buffer_in_sz % 8 != 0) {
src_len = ((buffer_in_sz / 8) + 1) * 8;
padded.reset(new unsigned char[src_len]());
memcpy(padded.get(), buffer_in, buffer_in_sz);
src = padded.get();
}
int outl = 0;
int final_len = 0;
result = EVP_CipherUpdate(ctx, reinterpret_cast<unsigned char*>(buffer_out), &outl, src, static_cast<int>(src_len)) == 1
&& EVP_CipherFinal_ex(ctx, reinterpret_cast<unsigned char*>(buffer_out) + outl, &final_len) == 1;
}
EVP_CIPHER_CTX_free(ctx);
if (!result) {
return nullptr;
}
#endif
return buffer_out;
}
#ifdef EQEMU_USE_OPENSSL
static OSSL_PROVIDER *s_legacy_provider = nullptr;
static OSSL_PROVIDER *s_default_provider = nullptr;
#endif
bool eqcrypt_init()
{
#ifdef EQEMU_USE_OPENSSL
#ifdef _WIN32
// Set OpenSSL default provider search path to the executable directory.
char* exe_path = nullptr;
if (_get_pgmptr(&exe_path) == 0 && exe_path != nullptr && *exe_path != '\0') {
std::string exe_dir{exe_path};
if (auto sep = exe_dir.find_last_of("\\/"); sep != std::string::npos) {
exe_dir.resize(sep);
OSSL_PROVIDER_set_default_search_path(nullptr, exe_dir.c_str());
}
}
#endif
if (!s_default_provider) {
s_default_provider = OSSL_PROVIDER_load(nullptr, "default");
}
if (!s_legacy_provider) {
s_legacy_provider = OSSL_PROVIDER_load(nullptr, "legacy");
}
if (!s_default_provider || !s_legacy_provider) {
char buf[256];
while (auto err = ERR_get_error()) {
ERR_error_string_n(err, buf, sizeof(buf));
LogError("OpenSSL provider load failure: {}", buf);
}
return false;
}
#endif
return true;
}
void eqcrypt_shutdown()
{
#ifdef EQEMU_USE_OPENSSL
if (s_legacy_provider) {
OSSL_PROVIDER_unload(s_legacy_provider);
s_legacy_provider = nullptr;
}
if (s_default_provider) {
OSSL_PROVIDER_unload(s_default_provider);
s_default_provider = nullptr;
}
#endif
}
std::string eqcrypt_md5(const std::string &msg)
{
std::string ret;
@@ -164,13 +250,13 @@ std::string eqcrypt_md5(const std::string &msg)
unsigned char md5_digest[16];
char tmp[4];
MD5((const unsigned char*)msg.c_str(), msg.length(), md5_digest);
if (EVP_Digest(msg.data(), msg.length(), md5_digest, nullptr, EVP_md5(), nullptr) == 1) {
for (int i = 0; i < 16; ++i) {
sprintf(&tmp[0], "%02x", md5_digest[i]);
ret.push_back(tmp[0]);
ret.push_back(tmp[1]);
}
}
#endif
return ret;
@@ -198,13 +284,13 @@ std::string eqcrypt_sha1(const std::string &msg)
unsigned char sha_digest[20];
char tmp[4];
SHA1((const unsigned char*)msg.c_str(), msg.length(), sha_digest);
if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha1(), nullptr) == 1) {
for (int i = 0; i < 20; ++i) {
sprintf(&tmp[0], "%02x", sha_digest[i]);
ret.push_back(tmp[0]);
ret.push_back(tmp[1]);
}
}
#endif
return ret;
@@ -232,13 +318,13 @@ std::string eqcrypt_sha512(const std::string &msg)
unsigned char sha_digest[64];
char tmp[4];
SHA512((const unsigned char*)msg.c_str(), msg.length(), sha_digest);
if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha512(), nullptr) == 1) {
for (int i = 0; i < 64; ++i) {
sprintf(&tmp[0], "%02x", sha_digest[i]);
ret.push_back(tmp[0]);
ret.push_back(tmp[1]);
}
}
#endif
return ret;
+10
View File
@@ -48,10 +48,20 @@ namespace CryptoHash {
}
std::string GetEncryptionByModeId(uint32 mode);
// DES-CBC with an all-zero key and IV (EQ login protocol obfuscation, not security).
// On encrypt, a trailing partial block is zero-padded to the next 8-byte boundary, so
// buffer_out must be at least ((buffer_in_sz + 7) / 8) * 8 bytes. On decrypt, buffer_in_sz
// must already be a multiple of 8 or the call returns nullptr.
const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc);
std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode);
bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode);
// OpenSSL 3.0 moved DES behind the "legacy" provider; these load/unload it
// for the lifetime of the process. No-op when built against mbedtls.
bool eqcrypt_init();
void eqcrypt_shutdown();
struct EncryptionResult {
std::string password;
int mode = 0;
+7 -4
View File
@@ -26,6 +26,7 @@
#include "common/platform.h"
#include "common/timer.h"
#include "common/types.h"
#include "loginserver/encryption.h"
#include "loginserver/login_server.h"
#include "loginserver/loginserver_command_handler.h"
#include "loginserver/loginserver_webserver.h"
@@ -158,12 +159,12 @@ void start_web_server()
int main(int argc, char **argv)
{
RegisterExecutablePlatform(ExePlatformLogin);
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
set_exception_handler();
LogInfo("Logging System Init");
if (argc == 1) {
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
if (!eqcrypt_init()) {
LogError("Failed to initialize crypto providers");
return 1;
}
PathManager::Instance()->Init();
@@ -280,5 +281,7 @@ int main(int argc, char **argv)
LogInfo("Server Manager Shutdown");
delete server.server_manager;
eqcrypt_shutdown();
return 0;
}
+4
View File
@@ -3186,6 +3186,10 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
// if other is a bot, add the bots client to the hate list
if (RuleB(Bots, Enabled)) {
if (other->GetOwner() && other->GetOwner()->IsBot()) {
other = other->GetOwner();
}
if (other->IsBot()) {
auto other_ = other->CastToBot();
+27
View File
@@ -3432,6 +3432,10 @@ bool Bot::CheckIfIncapacitated() {
}
if (currently_fleeing) {
if (!FindType(SpellEffect::Fear)) { // Blinded
return BotProcessBlind();
}
if (RuleB(Combat, EnableFearPathing) && AI_movement_timer->Check()) {
// Check if we have reached the last fear point
if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) {
@@ -3453,6 +3457,23 @@ bool Bot::CheckIfIncapacitated() {
return false;
}
bool Bot::BotProcessBlind() {
if (m_combat_jitter_timer.Check() && zone->random.Int(1, 100) <= RuleI(Bots, BlindMoveChance)) {
Mob* tar = GetTarget() ? GetTarget() : GetBotOwner();
if (tar) {
glm::vec3 Goal = tar->GetPosition();
RunTo(Goal.x, Goal.y, Goal.z);
SetCombatJitter();
}
return true;
}
return false;
}
void Bot::SetBerserkState() {// Berserk updates should occur if primary AI criteria are met
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
if (!berserk && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) {
@@ -5335,6 +5356,12 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
if (ma_time) {
switch (GetClass()) {
case Class::Monk: {
if (!GetSkill(EQ::skills::SkillTigerClaw)) {
monkattack_timer.Disable();
return;
}
int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1);
// Live AA - Technique of Master Wu
+1
View File
@@ -1055,6 +1055,7 @@ public:
bool BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
bool CheckIfIncapacitated();
bool BotProcessBlind();
bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid);
Client* SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const;
+3 -3
View File
@@ -775,7 +775,7 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type)
bot_owner->Message(Chat::Green, "%s", description.c_str());
}
void SendSpellTypeWindow(Client* c, const Seperator* sep) {
void SendSpellTypeWindow(Client* c, const Seperator* sep, bool short_names) {
std::string arg0 = sep->arg[0];
std::string arg1 = sep->arg[1];
@@ -828,7 +828,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) {
std::string popup_text = DialogueWindow::TableRow(
DialogueWindow::TableCell(DialogueWindow::ColorMessage(goldenrod, spell_type_field))
+
DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, id_field) : DialogueWindow::ColorMessage(goldenrod, shortname_field)))
DialogueWindow::TableCell((!short_names ? DialogueWindow::ColorMessage(goldenrod, id_field) : DialogueWindow::ColorMessage(goldenrod, shortname_field)))
);
popup_text += DialogueWindow::TableRow(
@@ -845,7 +845,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) {
popup_text += DialogueWindow::TableRow(
DialogueWindow::TableCell(DialogueWindow::ColorMessage(forest_green, Bot::GetSpellTypeNameByID(i)))
+
DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, Bot::GetSpellTypeShortNameByID(i))))
DialogueWindow::TableCell((!short_names ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, Bot::GetSpellTypeShortNameByID(i))))
);
}
+1 -1
View File
@@ -1182,4 +1182,4 @@ bool helper_is_help_or_usage(const char* arg);
bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr);
void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, std::vector<const char*> subcommand_list);
void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type);
void SendSpellTypeWindow(Client* c, const Seperator* sep);
void SendSpellTypeWindow(Client* c, const Seperator* sep, bool short_names = false);
+2 -2
View File
@@ -206,8 +206,8 @@ void bot_command_item_use(Client* c, const Seperator* sep)
}
if (
(!RuleB(Bots, AllowBotEquipAnyRaceGear) && ((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace()))) ||
(!RuleB(Bots, AllowBotEquipAnyClassGear) && ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass())))
(!RuleB(Bots, AllowBotEquipAnyRaceGear) && !(item_data->Races & GetPlayerRaceBit(bot_iter->GetBaseRace()))) ||
(!RuleB(Bots, AllowBotEquipAnyClassGear) && !(item_data->Classes & GetPlayerClassBit(bot_iter->GetClass())))
) {
continue;
}
+1 -1
View File
@@ -24,5 +24,5 @@ void bot_command_spelltype_ids(Client* c, const Seperator* sep)
void bot_command_spelltype_names(Client* c, const Seperator* sep)
{
SendSpellTypeWindow(c, sep);
SendSpellTypeWindow(c, sep, true);
}
+1 -1
View File
@@ -512,7 +512,7 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id)
e.poison = b->GetBasePR();
e.disease = b->GetBaseDR();
e.corruption = b->GetBaseCorrup();
e.expansion_bitmask = b->GetExpansionBitmask();
e.expansion_bitmask = RuleI(Bots, BotExpansionSettings);
e = BotDataRepository::InsertOne(database, e);
+39 -16
View File
@@ -14378,9 +14378,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
sizeof(Merchant_Purchase_Struct), app->size);
return;
}
RDTSC_Timer t1(true);
Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer;
Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer;
Mob* vendor = entity_list.GetMob(mp->npcid);
if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != Class::Merchant)
@@ -14390,35 +14389,51 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
if (DistanceSquared(m_Position, vendor->GetPosition()) > USE_NPC_RANGE2)
return;
uint32 price = 0;
uint32 itemid = GetItemIDAt(mp->itemslot);
if (itemid == 0)
return;
const EQ::ItemData* item = database.GetItem(itemid);
EQ::ItemInstance* inst = GetInv().GetItem(mp->itemslot);
if (!item || !inst) {
Message(Chat::Red, "You seemed to have misplaced that item..");
return;
}
if (mp->quantity > 1)
{
if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges()))
Message(Chat::Red, "You seem to have misplaced that item..");
return;
}
if (!item->NoDrop) {
//Message(Chat::Red,"%s tells you, 'LOL NOPE'", vendor->GetName());
return;
}
uint32 cost_quantity = mp->quantity;
if (inst->IsCharged())
uint32 cost_quantity = 1;
if (mp->quantity > 1) {
if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges()))
return;
}
uint32 i;
// Check for veto from script
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_PRESELL)) {
std::string export_string = fmt::format("{} {} {}", mp->itemslot, itemid, inst->GetItemType());
std::vector<std::any> extra_pointers = { vendor, inst };
int result = parse->EventPlayer(EVENT_MERCHANT_PRESELL, this, export_string, 0, &extra_pointers);
// CANCEL: If a script returns -1 for this event, the sale wil be cancelled. Sends a dummy packet sent to satisfy the client
if (result == -1) {
auto outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer;
mco->npcid = vendor->GetID();
mco->itemslot = -1; // Critical or the client will remove the item visually
mco->quantity = 0;
mco->price = 0;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
uint32 cost_quantity = inst->IsCharged() ? 1 : mp->quantity;
uint32 price = 0;
if (RuleB(Merchant, UsePriceMod)) {
for (i = 1; i <= cost_quantity; i++) {
for (uint32 i = 1; i <= cost_quantity; i++) {
price = (uint32)(item->Price * i) * Client::CalcPriceMod(vendor, true);
// Don't use SellCostMod if using UseClassicPriceMod
@@ -14436,7 +14451,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
}
}
else {
for (i = 1; i <= cost_quantity; i++) {
for (uint32 i = 1; i <= cost_quantity; i++) {
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price
if (price > 4000000000) {
cost_quantity = i;
@@ -14448,6 +14463,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
AddMoneyToPP(price);
// Update merchant stock and refresh client
if (inst->IsStackable() || inst->IsCharged())
{
unsigned int i_quan = inst->GetCharges();
@@ -14561,6 +14577,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
QueuePacket(outapp);
safe_delete(outapp);
SendMoneyUpdate();
RDTSC_Timer t1(true);
t1.start();
Save(1);
t1.stop();
@@ -14679,6 +14697,11 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
if ((tabs_to_display & Parcel) == Parcel) {
SendBulkParcels();
}
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_OPEN)) {
std::vector<std::any> extra_pointers = { tmp };
parse->EventPlayer(EVENT_MERCHANT_OPEN, this, "", 0, &extra_pointers);
}
}
return;
+39 -4
View File
@@ -17,11 +17,13 @@
*/
#ifdef EMBPERL
#include "zone/embparser.h"
#include "common/compiler_macros.h"
#include "common/features.h"
#include "common/misc_functions.h"
#include "common/seperator.h"
#include "common/strings.h"
#include "zone/embparser.h"
#include "zone/masterentity.h"
#include "zone/qglobals.h"
#include "zone/questmgr.h"
@@ -161,8 +163,10 @@ const char* QuestEventSubroutines[_LargestEventID] = {
"EVENT_LANGUAGE_SKILL_UP",
"EVENT_ALT_CURRENCY_MERCHANT_BUY",
"EVENT_ALT_CURRENCY_MERCHANT_SELL",
"EVENT_MERCHANT_OPEN",
"EVENT_MERCHANT_BUY",
"EVENT_MERCHANT_SELL",
"EVENT_MERCHANT_PRESELL",
"EVENT_INSPECT",
"EVENT_TASK_BEFORE_UPDATE",
"EVENT_AA_BUY",
@@ -398,6 +402,8 @@ int PerlembParser::EventCommon(
zone
);
}
return 0;
}
int PerlembParser::EventNPC(
@@ -1211,7 +1217,8 @@ QuestType PerlembParser::GetQuestTypes(
event_id == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE
) {
return is_global ? QuestType::SpellGlobal : QuestType::Spell;
} else {
}
if (npc_mob) {
if (!inst) {
if (npc_mob->IsBot()) {
@@ -1224,7 +1231,7 @@ QuestType PerlembParser::GetQuestTypes(
} else {
return is_global ? QuestType::ItemGlobal : QuestType::Item;
}
} else if (!npc_mob && mob) {
} else if (mob) {
if (!inst) {
if (mob->IsClient()) {
return is_global ? QuestType::PlayerGlobal : QuestType::Player;
@@ -1235,7 +1242,8 @@ QuestType PerlembParser::GetQuestTypes(
} else if (zone) {
return is_global ? QuestType::ZoneGlobal : QuestType::Zone;
}
}
UNREACHABLE();
}
std::string PerlembParser::GetQuestPackageName(
@@ -2283,6 +2291,33 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_MERCHANT_OPEN: {
if (!extra_pointers || extra_pointers->size() < 1) break;
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
if (!mob_ptr) break;
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
break;
}
case EVENT_MERCHANT_PRESELL: {
Seperator sep(data);
ExportVar(package_name.c_str(), "slot_id", sep.arg[0]);
ExportVar(package_name.c_str(), "item_id", sep.arg[1]);
ExportVar(package_name.c_str(), "item_type", sep.arg[2]);
if (!extra_pointers || extra_pointers->size() < 2) break;
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
if (!mob_ptr || !inst_ptr) break;
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
ExportVar(package_name.c_str(), "item", "ItemInstance", inst_ptr);
break;
}
case EVENT_AA_BUY: {
Seperator sep(data);
ExportVar(package_name.c_str(), "aa_cost", sep.arg[0]);
+2
View File
@@ -116,8 +116,10 @@ enum QuestEventID {
EVENT_LANGUAGE_SKILL_UP,
EVENT_ALT_CURRENCY_MERCHANT_BUY,
EVENT_ALT_CURRENCY_MERCHANT_SELL,
EVENT_MERCHANT_OPEN,
EVENT_MERCHANT_BUY,
EVENT_MERCHANT_SELL,
EVENT_MERCHANT_PRESELL,
EVENT_INSPECT,
EVENT_TASK_BEFORE_UPDATE,
EVENT_AA_BUY,
+2
View File
@@ -6968,8 +6968,10 @@ luabind::scope lua_register_events() {
luabind::value("language_skill_up", static_cast<int>(EVENT_LANGUAGE_SKILL_UP)),
luabind::value("alt_currency_merchant_buy", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_BUY)),
luabind::value("alt_currency_merchant_sell", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_SELL)),
luabind::value("merchant_open", static_cast<int>(EVENT_MERCHANT_OPEN)),
luabind::value("merchant_buy", static_cast<int>(EVENT_MERCHANT_BUY)),
luabind::value("merchant_sell", static_cast<int>(EVENT_MERCHANT_SELL)),
luabind::value("merchant_presell", static_cast<int>(EVENT_MERCHANT_PRESELL)),
luabind::value("inspect", static_cast<int>(EVENT_INSPECT)),
luabind::value("task_before_update", static_cast<int>(EVENT_TASK_BEFORE_UPDATE)),
luabind::value("aa_buy", static_cast<int>(EVENT_AA_BUY)),
+6
View File
@@ -158,6 +158,11 @@ uint32 Lua_ItemInst::GetItemScriptID() {
return self->GetItemScriptID();
}
uint8 Lua_ItemInst::GetItemType() {
Lua_Safe_Call_Int();
return self->GetItemType();
}
int Lua_ItemInst::GetCharges() {
Lua_Safe_Call_Int();
return self->GetCharges();
@@ -497,6 +502,7 @@ luabind::scope lua_register_iteminst() {
.def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID)
.def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink)
.def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID)
.def("GetItemType", (uint8(Lua_ItemInst::*)(void)) & Lua_ItemInst::GetItemType)
.def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl)
.def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName)
.def("GetSerialNumber", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetSerialNumber)
+1
View File
@@ -71,6 +71,7 @@ public:
bool IsAmmo();
uint32 GetID();
uint32 GetItemScriptID();
uint8 GetItemType();
int GetCharges();
void SetCharges(int charges);
uint32 GetPrice();
+10
View File
@@ -82,6 +82,16 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) {
}
}
Lua_Packet::~Lua_Packet()
{
if (owned_) {
EQApplicationPacket* ptr = GetLuaPtrData();
if (ptr) {
delete ptr;
}
}
}
int Lua_Packet::GetSize() {
Lua_Safe_Call_Int();
return static_cast<int>(self->size);
+1 -1
View File
@@ -41,7 +41,7 @@ public:
Lua_Packet(int opcode, int size, bool raw);
Lua_Packet& operator=(const Lua_Packet& o);
Lua_Packet(const Lua_Packet& o);
virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } }
virtual ~Lua_Packet();
int GetSize();
int GetOpcode();
+4
View File
@@ -160,8 +160,10 @@ const char *LuaEvents[_LargestEventID] = {
"event_language_skill_up",
"event_alt_currency_merchant_buy",
"event_alt_currency_merchant_sell",
"event_merchant_open",
"event_merchant_buy",
"event_merchant_sell",
"event_merchant_presell",
"event_inspect",
"event_task_before_update",
"event_aa_buy",
@@ -335,8 +337,10 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_LANGUAGE_SKILL_UP] = handle_player_language_skill_up;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_BUY] = handle_player_alt_currency_merchant;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_SELL] = handle_player_alt_currency_merchant;
PlayerArgumentDispatch[EVENT_MERCHANT_OPEN] = handle_player_merchant_open;
PlayerArgumentDispatch[EVENT_MERCHANT_BUY] = handle_player_merchant;
PlayerArgumentDispatch[EVENT_MERCHANT_SELL] = handle_player_merchant;
PlayerArgumentDispatch[EVENT_MERCHANT_PRESELL] = handle_player_merchant_presell;
PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect;
PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy;
PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain;
+47
View File
@@ -2340,6 +2340,53 @@ void handle_player_merchant(
lua_setfield(L, -2, "item_cost");
}
void handle_player_merchant_open(
QuestInterface* parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
) {
if (!extra_pointers || extra_pointers->size() < 1) return;
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
if (!mob_ptr) return;
Lua_Mob l_mob(mob_ptr);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
lua_setfield(L, -2, "other");
}
void handle_player_merchant_presell(
QuestInterface* parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
) {
Seperator sep(data.c_str());
lua_pushinteger(L, Strings::ToInt(sep.arg[0])); lua_setfield(L, -2, "slot_id");
lua_pushinteger(L, Strings::ToInt(sep.arg[1])); lua_setfield(L, -2, "item_id");
lua_pushinteger(L, Strings::ToInt(sep.arg[2])); lua_setfield(L, -2, "item_type");
if (!extra_pointers || extra_pointers->size() < 2) return;
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
if (!mob_ptr || !inst_ptr) return;
Lua_Mob l_mob(mob_ptr);
luabind::adl::object(L, l_mob).push(L);
lua_setfield(L, -2, "other");
Lua_ItemInst l_iteminst(inst_ptr);
luabind::adl::object(L, l_iteminst).push(L);
lua_setfield(L, -2, "item");
}
void handle_player_augment_insert(
QuestInterface *parse,
lua_State* L,
+18
View File
@@ -671,6 +671,24 @@ void handle_player_merchant(
std::vector<std::any> *extra_pointers
);
void handle_player_merchant_open(
QuestInterface* parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
void handle_player_merchant_presell(
QuestInterface* parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
void handle_player_inspect(
QuestInterface *parse,
lua_State* L,
+2 -1
View File
@@ -48,7 +48,8 @@ public:
Lua_Ptr(T *d) : d_(d) {
}
~Lua_Ptr() {
virtual ~Lua_Ptr() {
}
T *GetLuaPtrData() {
+18 -30
View File
@@ -711,48 +711,36 @@ void UpdateWindowTitle(char *iNewTitle)
bool CheckForCompatibleQuestPlugins()
{
const std::vector<std::pair<std::string, bool *>> directories = {
{"lua_modules", nullptr},
{"plugins", nullptr}
};
bool lua_found = false;
bool perl_found = false;
auto check_dir = [&](const std::string& dir_path, bool& found) {
if (!File::Exists(dir_path)) { return; }
try {
for (const auto &[directory, flag]: directories) {
std::string dir_path = PathManager::Instance()->GetServerPath() + "/" + directory;
if (!File::Exists(dir_path)) { continue; }
for (const auto &file: fs::directory_iterator(dir_path)) {
for (const auto& file : fs::directory_iterator(dir_path)) {
if (!file.is_regular_file()) { continue; }
std::string file_path = file.path().string();
if (!File::Exists(file_path)) { continue; }
auto r = File::GetContents(file_path);
if (!Strings::Contains(r.contents, "CheckHandin")) { continue; }
if (directory == "lua_modules") {
lua_found = true;
}
else {
perl_found = true;
}
if (lua_found && perl_found) { return true; }
auto r = File::GetContents(file.path().string());
if (Strings::Contains(r.contents, "CheckHandin")) {
found = true;
return;
}
}
} catch (const fs::filesystem_error &ex) {
}
catch (const fs::filesystem_error& ex) {
LogError("Failed to check for compatible quest plugins: {}", ex.what());
}
};
if (!lua_found) {
LogError("Failed to find CheckHandin in lua_modules");
for (const auto& path : PathManager::Instance()->GetLuaModulePaths()) {
check_dir(path, lua_found);
}
if (!perl_found) {
LogError("Failed to find CheckHandin in plugins");
for (const auto& path : PathManager::Instance()->GetPluginPaths()) {
check_dir(path, perl_found);
}
if (!lua_found) { LogError("Failed to find CheckHandin in the Lua module quest directories"); }
if (!perl_found) { LogError("Failed to find CheckHandin in the Perl plugins quest directories");}
return lua_found && perl_found;
}
+24 -23
View File
@@ -512,29 +512,6 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
bool found_skill = false;
if (
ca_atk->m_atk == 100 &&
ca_atk->m_skill == EQ::skills::SkillKick &&
can_use_kick
) {
if (GetTarget() != this) {
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
DoAnim(animKick, 0, false);
int hate_override = 0;
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
damage = -5;
} else {
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
}
reuse_time = KickReuseTime - 1 - skill_reduction;
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
found_skill = true;
}
}
if (class_id == Class::Monk) {
reuse_time = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction;
@@ -596,6 +573,30 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
found_skill = true;
}
else {
if (
ca_atk->m_atk == 100 &&
ca_atk->m_skill == EQ::skills::SkillKick &&
can_use_kick
) {
if (GetTarget() != this) {
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
DoAnim(animKick, 0, false);
int hate_override = 0;
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
damage = -5;
} else {
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
}
reuse_time = KickReuseTime - 1 - skill_reduction;
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
found_skill = true;
}
}
}
if (
ca_atk->m_atk == 100 &&
+4
View File
@@ -1362,6 +1362,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
}
}
}
else if (IsBot()) {
currently_fleeing = true;
CastToBot()->BotProcessBlind();
}
else if (!IsClient()) {
CalculateNewFearpoint();
}