diff --git a/Client/Client.cpp b/Client/Client.cpp new file mode 100644 index 0000000..ebe1b87 --- /dev/null +++ b/Client/Client.cpp @@ -0,0 +1,94 @@ +// Client.cpp : This file contains the 'main' function. Program execution begins and ends there. +// + +#include +#include + +HANDLE hPipe; +DWORD dwWritten; +BOOL fSuccess = FALSE; +DWORD cbRead, cbToWrite, cbWritten, dwMode; + +int CreatePipe(std::string name) +{ + while (1) + { + hPipe = CreateFileA( + name.c_str(), // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + 0, // default attributes + NULL); // no template file + + // Break if the pipe handle is valid. + + if (hPipe != INVALID_HANDLE_VALUE) + break; + + // Exit if an error other than ERROR_PIPE_BUSY occurs. + + if (GetLastError() != ERROR_PIPE_BUSY) + { + return -1; + } + } + + dwMode = PIPE_READMODE_MESSAGE; + fSuccess = SetNamedPipeHandleState( + hPipe, // pipe handle + &dwMode, // new pipe mode + NULL, // don't set maximum bytes + NULL); // don't set maximum time + if (!fSuccess) + { + return -1; + } + + return 0; +} + +std::string ReadMessage() +{ + char chBuf[10240]; + do + { + // Read from the pipe. + + fSuccess = ReadFile( + hPipe, // pipe handle + chBuf, // buffer to receive reply + 10240 * sizeof(char), // size of buffer + &cbRead, // number of bytes read + NULL); // not overlapped + + if (!fSuccess && GetLastError() != ERROR_MORE_DATA) + break; + } while (!fSuccess); + + return std::string(chBuf); +} + +int main() +{ + CreatePipe("\\\\.\\pipe\\PipeL2Bot"); + std::cout << "Connected to the connection pipe" << std::endl; + + auto name = ReadMessage(); + CloseHandle(hPipe); + std::cout << "Received main pipe name: " << name << std::endl; + + std::cin.get(); + CreatePipe(name); + + const std::string message = "invalidate"; + DWORD written; + WriteFile(hPipe, message.c_str(), message.size() + 1, &written, NULL); + + while (true) { + std::cout << ReadMessage() << std::endl; + } + std::cin.get(); +} \ No newline at end of file diff --git a/Client/Client.vcxproj b/Client/Client.vcxproj new file mode 100644 index 0000000..8f0a3d1 --- /dev/null +++ b/Client/Client.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {d5e220d4-cba9-465c-8d1a-b97cc5e5e825} + Client + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + false + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/Client/Client.vcxproj.filters b/Client/Client.vcxproj.filters new file mode 100644 index 0000000..5e23c2e --- /dev/null +++ b/Client/Client.vcxproj.filters @@ -0,0 +1,22 @@ +п»ї + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/InjectionLibrary/InjectionLibrary.vcxproj b/InjectionLibrary/InjectionLibrary.vcxproj new file mode 100644 index 0000000..f88ead9 --- /dev/null +++ b/InjectionLibrary/InjectionLibrary.vcxproj @@ -0,0 +1,182 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {54fbe631-3f9b-458c-9db2-43a868cdb806} + InjectionLibrary + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + false + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + + + + + true + true + true + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + true + true + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + \ No newline at end of file diff --git a/InjectionLibrary/InjectionLibrary.vcxproj.filters b/InjectionLibrary/InjectionLibrary.vcxproj.filters new file mode 100644 index 0000000..09745ec --- /dev/null +++ b/InjectionLibrary/InjectionLibrary.vcxproj.filters @@ -0,0 +1,48 @@ +п»ї + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/InjectionLibrary/Injector.cpp b/InjectionLibrary/Injector.cpp new file mode 100644 index 0000000..c1ed523 --- /dev/null +++ b/InjectionLibrary/Injector.cpp @@ -0,0 +1,35 @@ +#include "pch.h" +#include "Injector.h" + +namespace InjectLibrary +{ + HHOOK Injector::_hookHandle = nullptr; + + Injector::Injector(const std::string& mutexName, int windowsMessage) : _mutexName(mutexName), _windowsMessage(windowsMessage) + { + } + + void Injector::SetHook(const HINSTANCE moduleHandle) + { + if (moduleHandle) { + // С помощью мютекса проверяем, что еще не было хука + HANDLE mutexHandle = CreateMutexA(nullptr, false, _mutexName.c_str()); + if (GetLastError() != ERROR_ALREADY_EXISTS) { + _hookHandle = SetWindowsHookExA(_windowsMessage, (HOOKPROC)HookMessageProcedure, moduleHandle, 0); + _mutexHandle = mutexHandle; + } + else if (mutexHandle) { + CloseHandle(mutexHandle); + } + } + else { + UnhookWindowsHookEx(_hookHandle); + CloseHandle(_mutexHandle); + } + } + + const LRESULT Injector::HookMessageProcedure(const DWORD code, const DWORD wParam, const DWORD lParam) + { + return CallNextHookEx(_hookHandle, code, wParam, lParam); + } +} \ No newline at end of file diff --git a/InjectionLibrary/Injector.h b/InjectionLibrary/Injector.h new file mode 100644 index 0000000..5c39abe --- /dev/null +++ b/InjectionLibrary/Injector.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace InjectLibrary +{ + class Injector + { + public: + Injector(const std::string& mutexName, int windowsMessage); + virtual ~Injector() = default; + void CALLBACK SetHook(const HINSTANCE moduleHandle = nullptr); + private: + static const LRESULT CALLBACK HookMessageProcedure(const DWORD code, const DWORD wParam, const DWORD lParam); + + private: + static HHOOK _hookHandle; + HANDLE _mutexHandle = nullptr; + int _windowsMessage = 0; + const std::string _mutexName; + }; +}; diff --git a/InjectionLibrary/ProcessManipulation.cpp b/InjectionLibrary/ProcessManipulation.cpp new file mode 100644 index 0000000..c58b168 --- /dev/null +++ b/InjectionLibrary/ProcessManipulation.cpp @@ -0,0 +1,89 @@ +#include "pch.h" +#include +#include "ProcessManipulation.h" + +namespace InjectLibrary +{ + const std::string GetProcessName(const DWORD processId) + { + HANDLE handle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId); + if (handle) { + char path[MAX_PATH]; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + GetModuleFileNameExA(handle, 0, path, sizeof(path)); + _splitpath_s(path, drive, dir, fname, ext); + CloseHandle(handle); + + std::string result = std::string(fname) + std::string(ext); + std::transform(result.begin(), result.end(), result.begin(), ::towlower); + return result; + } + return ""; + } + + const std::string GetCurrentProcessName() + { + return GetProcessName(GetCurrentProcessId()); + } + + void StartProcess(const DWORD processId) + { + DWORD currThread = GetCurrentThreadId(); + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + THREADENTRY32 thread; + HANDLE threadHandle; + if (snap != INVALID_HANDLE_VALUE) { + thread.dwSize = sizeof(LPTHREADENTRY32); + if (Thread32First(snap, &thread)) { + do { + if (thread.th32ThreadID != currThread && thread.th32OwnerProcessID != processId) { + threadHandle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, thread.th32ThreadID); + if (threadHandle == 0) { + break; + } + ResumeThread(threadHandle); + CloseHandle(threadHandle); + } + } while (Thread32Next(snap, &thread)); + } + CloseHandle(snap); + } + } + + void StartCurrentProcess() + { + StartProcess(GetCurrentProcessId()); + } + + void StopProcess(const DWORD processId) + { + DWORD currThread = GetCurrentThreadId(); + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + THREADENTRY32 thread; + HANDLE threadHandle; + if (snap != INVALID_HANDLE_VALUE) { + thread.dwSize = sizeof(LPTHREADENTRY32); + if (Thread32First(snap, &thread)) { + do { + if (thread.th32ThreadID != currThread && thread.th32OwnerProcessID != processId) { + threadHandle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, thread.th32ThreadID); + if (threadHandle == 0) { + break; + } + SuspendThread(threadHandle); + CloseHandle(threadHandle); + } + } while (Thread32Next(snap, &thread)); + } + CloseHandle(snap); + } + } + + void StopCurrentProcess() + { + StopProcess(GetCurrentProcessId()); + } +} \ No newline at end of file diff --git a/InjectionLibrary/ProcessManipulation.h b/InjectionLibrary/ProcessManipulation.h new file mode 100644 index 0000000..70073e6 --- /dev/null +++ b/InjectionLibrary/ProcessManipulation.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +namespace InjectLibrary +{ + void StartProcess(const DWORD processId); + void StartCurrentProcess(); + void StopProcess(const DWORD processId); + void StopCurrentProcess(); + const std::string GetProcessName(const DWORD processId); + const std::string GetCurrentProcessName(); +}; diff --git a/InjectionLibrary/Trampoline.cpp b/InjectionLibrary/Trampoline.cpp new file mode 100644 index 0000000..3624ecd --- /dev/null +++ b/InjectionLibrary/Trampoline.cpp @@ -0,0 +1,66 @@ +#include "pch.h" +#include "Trampoline.h" + +namespace InjectLibrary +{ + Trampoline::Trampoline(void* hookedFunctionAddress, void* hookPayloadFunctionAddress, const BYTE oldCodeSize) : + _hookedFunctionAddress(hookedFunctionAddress), _hookPayloadFunctionAddress(hookPayloadFunctionAddress) + { + auto size = oldCodeSize; + if (size <= 0) { + size = SIZE_OF_JUMP; + } + _trampolineLayout = new TrampolineLayout(size); + // Код, который будет сгенерирован в будущем должен иметь разрешение на выполнение + VirtualProtect(_trampolineLayout->code, _trampolineLayout->GetFullSize(), PAGE_EXECUTE_READWRITE, &_protect); + } + + Trampoline::~Trampoline() + { + VirtualProtect(_trampolineLayout->code, _trampolineLayout->GetFullSize(), _protect, &_protect); + delete _trampolineLayout; + } + + const FARPROC Trampoline::Install() + { + FillLayout(); + InstallHook(); + return GetAddress(); + } + + void Trampoline::Uninstall() + { + DWORD oldProtect; + VirtualProtect(_hookedFunctionAddress, SIZE_OF_JUMP, PAGE_EXECUTE_READWRITE, &oldProtect); + // При удалении хука вернем на место затертые инструкции в перехватываемой фукнции + CopyMemory(_hookedFunctionAddress, _trampolineLayout->code, SIZE_OF_JUMP); + VirtualProtect(_hookedFunctionAddress, SIZE_OF_JUMP, oldProtect, &oldProtect); + } + + const FARPROC Trampoline::GetAddress() const + { + return (FARPROC)(void*)_trampolineLayout->code; + } + + void Trampoline::FillLayout() + { + const auto oldCodeSize = _trampolineLayout->GetOldCodeSize(); + // Скопируем первые oldCodeSize байт кода из перехватываемой функции в наш трамплин + CopyMemory(_trampolineLayout->code, _hookedFunctionAddress, oldCodeSize); + // Подсчитаем 32битное смещение адреса и запишем в наш трамплин после кода фукнции, скопировнного выше + _trampolineLayout->jumpInstruction->rel32 = (DWORD)_hookedFunctionAddress - ((DWORD)_trampolineLayout->code + oldCodeSize); + } + + void Trampoline::InstallHook() const + { + DWORD oldProtect; + // Что бы изменить код перехватываемой функции, область памяти должна иметь разрешение на запись + VirtualProtect(_hookedFunctionAddress, SIZE_OF_JUMP, PAGE_EXECUTE_READWRITE, &oldProtect); + RelativeJumpLayout* instr = (RelativeJumpLayout*)((BYTE*)_hookedFunctionAddress); + // Подсчитаем 32битное смещение адреса и запишем его вместе с опкодом инструкции джампа в начало перехватываемой функции + // Джамп будет выполнен в нашу функцию, где выполняется реальная работа после перехвата и осуществляется переход на инструкцию трамплина + instr->opcode = 0xe9; + instr->rel32 = (DWORD)_hookPayloadFunctionAddress - ((DWORD)_hookedFunctionAddress + SIZE_OF_JUMP); + VirtualProtect(_hookedFunctionAddress, SIZE_OF_JUMP, oldProtect, &oldProtect); + } +} diff --git a/InjectionLibrary/Trampoline.h b/InjectionLibrary/Trampoline.h new file mode 100644 index 0000000..c684563 --- /dev/null +++ b/InjectionLibrary/Trampoline.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include + +namespace InjectLibrary +{ + // Вынесем инструкцию джампа в отдельную структуру, и запретим компилятору вырванивание адресов +#pragma pack(push, 1) + struct RelativeJumpLayout + { + BYTE opcode = 0; + DWORD rel32 = 0; + }; +#pragma pack(pop) + + const BYTE SIZE_OF_JUMP = sizeof(RelativeJumpLayout); + + struct TrampolineLayout + { + static const BYTE CODE_MAX_SIZE = 100; + // В этой структуре запрет на выравнивание адресов важен только для генерируемого кода +#pragma pack(push, 1) + BYTE code[CODE_MAX_SIZE] = { 0 }; +#pragma pack(pop) + RelativeJumpLayout* jumpInstruction = nullptr; + + TrampolineLayout(const BYTE oldCodeSize) : _oldCodeSize(oldCodeSize) + { + if (oldCodeSize < SIZE_OF_JUMP) { + throw std::overflow_error("oldCodeSize lesser than SIZE_OF_JUMP"); + } + if (oldCodeSize > CODE_MAX_SIZE - SIZE_OF_JUMP) { + throw std::overflow_error("oldCodeSize greater than CODE_MAX_SIZE - SIZE_OF_JUMP"); + } + // Настроим адрес инструкции джампа на адрес, находящийся сразу после реальных инструкций, которые будут скопированы из перехватываемой функции + jumpInstruction = (RelativeJumpLayout*)((BYTE*)code + _oldCodeSize); + jumpInstruction->opcode = 0xe9; + } + + const BYTE GetFullSize() const + { + return GetOldCodeSize() + SIZE_OF_JUMP; + } + + const BYTE GetOldCodeSize() const + { + return _oldCodeSize; + } + private: + const BYTE _oldCodeSize = 0; + }; + + class Trampoline + { + public: + Trampoline(void* hookedFunctionAddress, void* hookPayloadFunctionAddress, const BYTE oldCodeSize = 0); + virtual ~Trampoline(); + const FARPROC Install(); + void Uninstall(); + const FARPROC GetAddress() const; + + Trampoline(const Trampoline&) = delete; + Trampoline& operator=(const Trampoline&) = delete; + Trampoline(const Trampoline&&) = delete; + Trampoline& operator=(const Trampoline&&) = delete; + private: + void FillLayout(); + void InstallHook() const; + + private: + TrampolineLayout* _trampolineLayout = nullptr; + void* _hookedFunctionAddress = nullptr; + void* _hookPayloadFunctionAddress = nullptr; + DWORD _protect = 0; + + }; +}; \ No newline at end of file diff --git a/InjectionLibrary/framework.h b/InjectionLibrary/framework.h new file mode 100644 index 0000000..9072e35 --- /dev/null +++ b/InjectionLibrary/framework.h @@ -0,0 +1,9 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/InjectionLibrary/pch.cpp b/InjectionLibrary/pch.cpp new file mode 100644 index 0000000..64b7eef --- /dev/null +++ b/InjectionLibrary/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/InjectionLibrary/pch.h b/InjectionLibrary/pch.h new file mode 100644 index 0000000..885d5d6 --- /dev/null +++ b/InjectionLibrary/pch.h @@ -0,0 +1,13 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here +#include "framework.h" + +#endif //PCH_H diff --git a/L2Bot.sln b/L2Bot.sln new file mode 100644 index 0000000..9b86642 --- /dev/null +++ b/L2Bot.sln @@ -0,0 +1,77 @@ +п»ї +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32126.317 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "L2BotCore", "L2BotCore\L2BotCore.vcxproj", "{504A5403-BA08-46DF-AA8A-B79993B56BCA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sandbox", "Sandbox\Sandbox.vcxproj", "{28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}" + ProjectSection(ProjectDependencies) = postProject + {504A5403-BA08-46DF-AA8A-B79993B56BCA} = {504A5403-BA08-46DF-AA8A-B79993B56BCA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "L2BotDll", "L2BotDll\L2BotDll.vcxproj", "{F077B130-780F-4C72-AF56-E98B104A2A7D}" + ProjectSection(ProjectDependencies) = postProject + {54FBE631-3F9B-458C-9DB2-43A868CDB806} = {54FBE631-3F9B-458C-9DB2-43A868CDB806} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Client", "Client\Client.vcxproj", "{D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InjectionLibrary", "InjectionLibrary\InjectionLibrary.vcxproj", "{54FBE631-3F9B-458C-9DB2-43A868CDB806}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Debug|x64.ActiveCfg = Debug|x64 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Debug|x64.Build.0 = Debug|x64 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Debug|x86.ActiveCfg = Debug|Win32 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Debug|x86.Build.0 = Debug|Win32 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Release|x64.ActiveCfg = Release|x64 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Release|x64.Build.0 = Release|x64 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Release|x86.ActiveCfg = Release|Win32 + {504A5403-BA08-46DF-AA8A-B79993B56BCA}.Release|x86.Build.0 = Release|Win32 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Debug|x64.ActiveCfg = Debug|x64 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Debug|x64.Build.0 = Debug|x64 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Debug|x86.ActiveCfg = Debug|Win32 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Debug|x86.Build.0 = Debug|Win32 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Release|x64.ActiveCfg = Release|x64 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Release|x64.Build.0 = Release|x64 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Release|x86.ActiveCfg = Release|Win32 + {28B1B2BC-BB6C-483E-B18D-E532A29ED8EA}.Release|x86.Build.0 = Release|Win32 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Debug|x64.ActiveCfg = Debug|x64 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Debug|x64.Build.0 = Debug|x64 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Debug|x86.ActiveCfg = Debug|Win32 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Debug|x86.Build.0 = Debug|Win32 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Release|x64.ActiveCfg = Release|x64 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Release|x64.Build.0 = Release|x64 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Release|x86.ActiveCfg = Release|Win32 + {F077B130-780F-4C72-AF56-E98B104A2A7D}.Release|x86.Build.0 = Release|Win32 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Debug|x64.ActiveCfg = Debug|x64 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Debug|x64.Build.0 = Debug|x64 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Debug|x86.ActiveCfg = Debug|Win32 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Debug|x86.Build.0 = Debug|Win32 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Release|x64.ActiveCfg = Release|x64 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Release|x64.Build.0 = Release|x64 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Release|x86.ActiveCfg = Release|Win32 + {D5E220D4-CBA9-465C-8D1A-B97CC5E5E825}.Release|x86.Build.0 = Release|Win32 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Debug|x64.ActiveCfg = Debug|x64 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Debug|x64.Build.0 = Debug|x64 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Debug|x86.ActiveCfg = Debug|Win32 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Debug|x86.Build.0 = Debug|Win32 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Release|x64.ActiveCfg = Release|x64 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Release|x64.Build.0 = Release|x64 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Release|x86.ActiveCfg = Release|Win32 + {54FBE631-3F9B-458C-9DB2-43A868CDB806}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F47CE936-74D5-4FF2-AABA-0AA02AB71BA4} + EndGlobalSection +EndGlobal diff --git a/L2BotCore/Domain/DTO/BaseItem.h b/L2BotCore/Domain/DTO/BaseItem.h new file mode 100644 index 0000000..f54b6a4 --- /dev/null +++ b/L2BotCore/Domain/DTO/BaseItem.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include "WorldObject.h" + +namespace L2Bot::Domain::DTO +{ + struct BaseItem + { + public: + const uint32_t itemId = 0; + const uint32_t amount = 0; + const bool isEquipped = 0; + const uint16_t enchantLevel = 0; + const int32_t mana = -1; + const std::string name = ""; + const std::string iconName = ""; + const std::string description = ""; + const uint16_t weight = 0; + }; +} diff --git a/L2BotCore/Domain/DTO/Drop.h b/L2BotCore/Domain/DTO/Drop.h new file mode 100644 index 0000000..2fbf851 --- /dev/null +++ b/L2BotCore/Domain/DTO/Drop.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include "WorldObject.h" + +namespace L2Bot::Domain::DTO +{ + struct Drop : public WorldObject + { + public: + const uint32_t itemId = 0; + const uint32_t amount = 0; + const std::string name = ""; + const std::string iconName = ""; + }; +} diff --git a/L2BotCore/Domain/DTO/Hero.h b/L2BotCore/Domain/DTO/Hero.h new file mode 100644 index 0000000..65556c4 --- /dev/null +++ b/L2BotCore/Domain/DTO/Hero.h @@ -0,0 +1,29 @@ +#pragma once +#include "WorldObject.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../ValueObjects/Phenotype.h" +#include "../ValueObjects/ExperienceInfo.h" +#include "../ValueObjects/PermanentStats.h" +#include "../ValueObjects/VariableStats.h" +#include "../ValueObjects/Reputation.h" +#include "../ValueObjects/InventoryInfo.h" +#include + +namespace L2Bot::Domain::DTO +{ + struct Hero : public WorldObject + { + public: + const ValueObjects::FullName fullName = ValueObjects::FullName(); + const ValueObjects::VitalStats vitalStats = ValueObjects::VitalStats(); + const ValueObjects::Phenotype phenotype = ValueObjects::Phenotype(); + const ValueObjects::ExperienceInfo experienceInfo = ValueObjects::ExperienceInfo(); + const ValueObjects::PermanentStats permanentStats = ValueObjects::PermanentStats(); + const ValueObjects::VariableStats variableStats = ValueObjects::VariableStats(); + const ValueObjects::Reputation reputation = ValueObjects::Reputation(); + const ValueObjects::InventoryInfo inventoryInfo = ValueObjects::InventoryInfo(); + const uint32_t targetId = 0; + const bool isStanding = true; + }; +} diff --git a/L2BotCore/Domain/DTO/NPC.h b/L2BotCore/Domain/DTO/NPC.h new file mode 100644 index 0000000..8e2f345 --- /dev/null +++ b/L2BotCore/Domain/DTO/NPC.h @@ -0,0 +1,19 @@ +#pragma once +#include +#include "WorldObject.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../Enums/SpoilStateEnum.h" + +namespace L2Bot::Domain::DTO +{ + struct NPC : public WorldObject + { + public: + const bool isHostile = false; + const uint32_t npcId = 0; + const Enums::SpoilStateEnum spoilState = Enums::SpoilStateEnum::none; + const ValueObjects::FullName fullName = ValueObjects::FullName(); + const ValueObjects::VitalStats vitalStats = ValueObjects::VitalStats(); + }; +} diff --git a/L2BotCore/Domain/DTO/ObjectState.h b/L2BotCore/Domain/DTO/ObjectState.h new file mode 100644 index 0000000..8df7e76 --- /dev/null +++ b/L2BotCore/Domain/DTO/ObjectState.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Enums/ObjectStateEnum.h" + +namespace L2Bot::Domain::DTO +{ + template + struct ObjectState + { + public: + T object; + Enums::ObjectStateEnum state = Enums::ObjectStateEnum::none; + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/DTO/Player.h b/L2BotCore/Domain/DTO/Player.h new file mode 100644 index 0000000..e07bb6d --- /dev/null +++ b/L2BotCore/Domain/DTO/Player.h @@ -0,0 +1,14 @@ +#pragma once +#include "WorldObject.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../ValueObjects/Phenotype.h" +namespace L2Bot::Domain::DTO +{ + struct Player : public WorldObject + { + public: + const ValueObjects::FullName fullName = ValueObjects::FullName(); + const ValueObjects::Phenotype phenotype = ValueObjects::Phenotype(); + }; +} diff --git a/L2BotCore/Domain/DTO/Skill.h b/L2BotCore/Domain/DTO/Skill.h new file mode 100644 index 0000000..261112e --- /dev/null +++ b/L2BotCore/Domain/DTO/Skill.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +namespace L2Bot::Domain::DTO +{ + struct Skill + { + public: + const uint32_t skillId = 0; + const uint8_t level = 0; + const bool isActive = false; + const uint8_t cost = 0; + const int16_t range = 0; + const std::string name = ""; + const std::string description = ""; + const std::string iconName = ""; + const bool isToggled = false; + const bool isCasting = false; + const bool isReloading = false; + }; +} diff --git a/L2BotCore/Domain/DTO/WorldObject.h b/L2BotCore/Domain/DTO/WorldObject.h new file mode 100644 index 0000000..f7bbbbe --- /dev/null +++ b/L2BotCore/Domain/DTO/WorldObject.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include "../ValueObjects/Transform.h" + +namespace L2Bot::Domain::DTO +{ + struct WorldObject + { + public: + const uint32_t id = 0; + const ValueObjects::Transform transform = ValueObjects::Transform(); + }; +} diff --git a/L2BotCore/Domain/Entities/Drop.h b/L2BotCore/Domain/Entities/Drop.h new file mode 100644 index 0000000..65188a5 --- /dev/null +++ b/L2BotCore/Domain/Entities/Drop.h @@ -0,0 +1,97 @@ +#pragma once +#include +#include +#include "WorldObject.h" +#include "../DTO/Drop.h" + +namespace L2Bot::Domain::Entities +{ + class Drop : public WorldObject + { + public: + const uint32_t GetItemId() const + { + return m_ItemId; + } + const uint32_t GetAmount() const + { + return m_Amount; + } + const std::string GetName() const + { + return m_Name; + } + const std::string GetIconName() const + { + return m_IconName; + } + void UpdateFromDTO(const DTO::WorldObject* dto) override + { + const DTO::Drop* castedDto = static_cast(dto); + WorldObject::UpdateFromDTO(dto); + m_ItemId = castedDto->itemId; + m_Amount = castedDto->amount; + m_Name = castedDto->name; + m_IconName = castedDto->iconName; + } + void SaveState() override + { + WorldObject::SaveState(); + m_IsNewState = false; + } + const static Drop CreateFromDTO(const DTO::Drop& dto) + { + return Drop(dto.id, dto.transform, dto.itemId, dto.amount, dto.name, dto.iconName); + } + const bool IsEqual(const DTO::WorldObject* dto) const override + { + const DTO::Drop* castedDto = static_cast(dto); + return WorldObject::IsEqual(dto) && + m_ItemId == castedDto->itemId && + m_Amount == castedDto->amount && + m_Name == castedDto->name && + m_IconName == castedDto->iconName; + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result = WorldObject::BuildSerializationNodes(); + + if (m_IsNewState) + { + result.push_back({ "itemId", std::to_string(m_ItemId) }); + result.push_back({ "amount", std::to_string(m_Amount) }); + result.push_back({ "name", m_Name }); + result.push_back({ "iconName", m_IconName }); + } + + return result; + } + + Drop( + const uint32_t id, + const ValueObjects::Transform transform, + const uint32_t itemId, + const uint32_t amount, + const std::string name, + const std::string iconName + ) : + WorldObject(id, transform), + m_ItemId(itemId), + m_Amount(amount), + m_Name(name), + m_IconName(iconName) + { + + } + + Drop() = default; + virtual ~Drop() = default; + private: + uint32_t m_ItemId = 0; + uint32_t m_Amount = 0; + std::string m_Name = ""; + std::string m_IconName = ""; + bool m_IsNewState = true; + }; +} diff --git a/L2BotCore/Domain/Entities/Hero.h b/L2BotCore/Domain/Entities/Hero.h new file mode 100644 index 0000000..ad06e00 --- /dev/null +++ b/L2BotCore/Domain/Entities/Hero.h @@ -0,0 +1,235 @@ +#pragma once +#include "WorldObject.h" +#include "../DTO/Hero.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../ValueObjects/Phenotype.h" +#include "../ValueObjects/ExperienceInfo.h" +#include "../ValueObjects/PermanentStats.h" +#include "../ValueObjects/VariableStats.h" +#include "../ValueObjects/Reputation.h" +#include "../ValueObjects/InventoryInfo.h" +#include + +namespace L2Bot::Domain::Entities +{ + class Hero : public WorldObject + { + public: + const ValueObjects::FullName& GetFullName() const + { + return m_FullName; + } + const ValueObjects::VitalStats& GetVitalStats() const + { + return m_VitalStats; + } + const ValueObjects::Phenotype& GetPhenotype() const + { + return m_Phenotype; + } + const ValueObjects::ExperienceInfo& GetExperienceInfo() const + { + return m_ExperienceInfo; + } + const ValueObjects::PermanentStats& GetPermanentStats() const + { + return m_PermanentStats; + } + const ValueObjects::VariableStats& GetVariableStats() const + { + return m_VariableStats; + } + const ValueObjects::Reputation& GetReputation() const + { + return m_Reputation; + } + const ValueObjects::InventoryInfo& GetInventoryInfo() const + { + return m_InventoryInfo; + } + const uint32_t GetTargetId() const + { + return m_TargetId; + } + const bool IsStanding() const + { + return m_IsStanding; + } + void UpdateFromDTO(const DTO::WorldObject* dto) override + { + const DTO::Hero* castedDto = static_cast(dto); + WorldObject::UpdateFromDTO(dto); + m_FullName = castedDto->fullName; + m_VitalStats = castedDto->vitalStats; + m_Phenotype = castedDto->phenotype; + m_ExperienceInfo = castedDto->experienceInfo; + m_PermanentStats = castedDto->permanentStats; + m_VariableStats = castedDto->variableStats; + m_Reputation = castedDto->reputation; + m_InventoryInfo = castedDto->inventoryInfo; + m_TargetId = castedDto->targetId; + m_IsStanding = castedDto->isStanding; + } + void SaveState() override + { + WorldObject::SaveState(); + m_PrevState = + { + m_FullName, + m_VitalStats, + m_Phenotype, + m_ExperienceInfo, + m_PermanentStats, + m_VariableStats, + m_Reputation, + m_InventoryInfo, + m_TargetId, + m_IsStanding, + false + }; + } + const static Hero CreateFromDTO(const DTO::Hero& dto) + { + return Hero( + dto.id, + dto.transform, + dto.fullName, + dto.vitalStats, + dto.phenotype, + dto.experienceInfo, + dto.permanentStats, + dto.variableStats, + dto.reputation, + dto.inventoryInfo, + dto.targetId, + dto.isStanding + ); + } + const bool IsEqual(const DTO::WorldObject* dto) const override + { + const DTO::Hero* castedDto = static_cast(dto); + return WorldObject::IsEqual(dto) && + m_FullName.IsEqual(&castedDto->fullName) && + m_VitalStats.IsEqual(&castedDto->vitalStats) && + m_Phenotype.IsEqual(&castedDto->phenotype) && + m_ExperienceInfo.IsEqual(&castedDto->experienceInfo) && + m_PermanentStats.IsEqual(&castedDto->permanentStats) && + m_VariableStats.IsEqual(&castedDto->variableStats) && + m_Reputation.IsEqual(&castedDto->reputation) && + m_InventoryInfo.IsEqual(&castedDto->inventoryInfo) && + m_TargetId == castedDto->targetId && + m_IsStanding == castedDto->isStanding; + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result = WorldObject::BuildSerializationNodes(); + + if (m_PrevState.isNewState || !m_FullName.IsEqual(&m_PrevState.fullName)) + { + result.push_back({ "fullName", m_FullName.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_VitalStats.IsEqual(&m_PrevState.vitalStats)) + { + result.push_back({ "vitalStats", m_VitalStats.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_Phenotype.IsEqual(&m_PrevState.phenotype)) + { + result.push_back({ "phenotype", m_Phenotype.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_ExperienceInfo.IsEqual(&m_PrevState.experienceInfo)) + { + result.push_back({ "experienceInfo", m_ExperienceInfo.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_PermanentStats.IsEqual(&m_PrevState.permanentStats)) + { + result.push_back({ "permanentStats", m_PermanentStats.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_VariableStats.IsEqual(&m_PrevState.variableStats)) + { + result.push_back({ "variableStats", m_VariableStats.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_Reputation.IsEqual(&m_PrevState.reputation)) + { + result.push_back({ "reputation", m_Reputation.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_InventoryInfo.IsEqual(&m_PrevState.inventoryInfo)) + { + result.push_back({ "inventoryInfo", m_InventoryInfo.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || m_TargetId != m_PrevState.targetId) + { + result.push_back({ "targetId", std::to_string(m_TargetId) }); + } + if (m_PrevState.isNewState || m_IsStanding != m_PrevState.isStanding) + { + result.push_back({ "isStanding", std::to_string(m_IsStanding) }); + } + + return result; + } + + Hero( + const uint32_t id, + const ValueObjects::Transform transform, + const ValueObjects::FullName fullName, + const ValueObjects::VitalStats vitalStats, + const ValueObjects::Phenotype phenotype, + const ValueObjects::ExperienceInfo experienceInfo, + const ValueObjects::PermanentStats permanentStats, + const ValueObjects::VariableStats variableStats, + const ValueObjects::Reputation reputation, + const ValueObjects::InventoryInfo inventoryInfo, + const uint32_t targetId, + const bool isStanding + ) : + WorldObject(id, transform), + m_FullName(fullName), + m_VitalStats(vitalStats), + m_Phenotype(phenotype), + m_ExperienceInfo(experienceInfo), + m_PermanentStats(permanentStats), + m_VariableStats(variableStats), + m_Reputation(reputation), + m_InventoryInfo(inventoryInfo), + m_TargetId(targetId), + m_IsStanding(isStanding) + { + + } + + Hero() = default; + virtual ~Hero() = default; + + private: + struct State + { + ValueObjects::FullName fullName = ValueObjects::FullName(); + ValueObjects::VitalStats vitalStats = ValueObjects::VitalStats(); + ValueObjects::Phenotype phenotype = ValueObjects::Phenotype(); + ValueObjects::ExperienceInfo experienceInfo = ValueObjects::ExperienceInfo(); + ValueObjects::PermanentStats permanentStats = ValueObjects::PermanentStats(); + ValueObjects::VariableStats variableStats = ValueObjects::VariableStats(); + ValueObjects::Reputation reputation = ValueObjects::Reputation(); + ValueObjects::InventoryInfo inventoryInfo = ValueObjects::InventoryInfo(); + uint32_t targetId = 0; + bool isStanding = true; + + bool isNewState = true; + }; + + private: + ValueObjects::FullName m_FullName = ValueObjects::FullName(); + ValueObjects::VitalStats m_VitalStats = ValueObjects::VitalStats(); + ValueObjects::Phenotype m_Phenotype = ValueObjects::Phenotype(); + ValueObjects::ExperienceInfo m_ExperienceInfo = ValueObjects::ExperienceInfo(); + ValueObjects::PermanentStats m_PermanentStats = ValueObjects::PermanentStats(); + ValueObjects::VariableStats m_VariableStats = ValueObjects::VariableStats(); + ValueObjects::Reputation m_Reputation = ValueObjects::Reputation(); + ValueObjects::InventoryInfo m_InventoryInfo = ValueObjects::InventoryInfo(); + uint32_t m_TargetId = 0; + bool m_IsStanding = true; + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/Entities/NPC.h b/L2BotCore/Domain/Entities/NPC.h new file mode 100644 index 0000000..31801a8 --- /dev/null +++ b/L2BotCore/Domain/Entities/NPC.h @@ -0,0 +1,149 @@ +#pragma once +#include +#include "WorldObject.h" +#include "../DTO/NPC.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../Serializers/Serializable.h" +#include "../Enums/SpoilStateEnum.h" + +namespace L2Bot::Domain::Entities +{ + class NPC : public WorldObject + { + public: + const bool IsHostile() const + { + return m_IsHostile; + } + const uint32_t GetNpcId() const + { + return m_NpcId; + } + const bool IsSpoiled() const + { + return m_SpoilState == Enums::SpoilStateEnum::spoiled; + } + const bool CanBeSweeped() const + { + return !m_VitalStats.IsAlive() && m_SpoilState == Enums::SpoilStateEnum::sweepable; + } + const ValueObjects::FullName& GetFullName() const + { + return m_FullName; + } + const ValueObjects::VitalStats& GetVitalStats() const + { + return m_VitalStats; + } + + void UpdateFromDTO(const DTO::WorldObject* dto) override + { + const DTO::NPC* castedDto = static_cast(dto); + WorldObject::UpdateFromDTO(dto); + m_IsHostile = castedDto->isHostile; + m_NpcId = castedDto->npcId; + m_SpoilState = castedDto->spoilState; + m_FullName = castedDto->fullName; + m_VitalStats = castedDto->vitalStats; + } + void SaveState() override + { + WorldObject::SaveState(); + m_PrevState = + { + m_FullName, + m_SpoilState, + m_VitalStats, + false + }; + } + const static NPC CreateFromDTO(const DTO::NPC& dto) + { + return NPC( + dto.id, + dto.transform, + dto.isHostile, + dto.npcId, + dto.spoilState, + dto.fullName, + dto.vitalStats + ); + } + const bool IsEqual(const DTO::WorldObject* dto) const override + { + const DTO::NPC* castedDto = static_cast(dto); + return WorldObject::IsEqual(dto) && + m_IsHostile == castedDto->isHostile && + m_NpcId == castedDto->npcId && + m_SpoilState == castedDto->spoilState && + m_FullName.IsEqual(&castedDto->fullName) && + m_VitalStats.IsEqual(&castedDto->vitalStats); + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result = WorldObject::BuildSerializationNodes(); + + if (m_PrevState.isNewState || !m_FullName.IsEqual(&m_PrevState.fullName)) + { + result.push_back({ "fullName", m_FullName.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState) + { + result.push_back({ "isHostile", std::to_string(m_IsHostile) }); + result.push_back({ "npcId", std::to_string(m_NpcId) }); + } + if (m_PrevState.isNewState || m_SpoilState != m_PrevState.spoilState) + { + result.push_back({ "spoilState", std::to_string(static_cast(m_SpoilState)) }); + } + if (m_PrevState.isNewState || !m_VitalStats.IsEqual(&m_PrevState.vitalStats)) + { + result.push_back({ "vitalStats", m_VitalStats.BuildSerializationNodes() }); + } + + return result; + } + + NPC( + const uint32_t id, + const ValueObjects::Transform transform, + const bool isHostile, + const uint32_t npcId, + const Enums::SpoilStateEnum spoilState, + const ValueObjects::FullName fullName, + const ValueObjects::VitalStats vitalStats + ) : + WorldObject(id, transform), + m_IsHostile(isHostile), + m_NpcId(npcId), + m_SpoilState(spoilState), + m_FullName(fullName), + m_VitalStats(vitalStats) + { + + } + + NPC() = default; + virtual ~NPC() = default; + + private: + struct State + { + ValueObjects::FullName fullName = ValueObjects::FullName(); + Enums::SpoilStateEnum spoilState = Enums::SpoilStateEnum::none; + ValueObjects::VitalStats vitalStats = ValueObjects::VitalStats(); + + bool isNewState = true; + }; + + private: + bool m_IsHostile = false; + uint32_t m_NpcId = 0; + Enums::SpoilStateEnum m_SpoilState = Enums::SpoilStateEnum::none; + ValueObjects::FullName m_FullName = ValueObjects::FullName(); + ValueObjects::VitalStats m_VitalStats = ValueObjects::VitalStats(); + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/Entities/Player.h b/L2BotCore/Domain/Entities/Player.h new file mode 100644 index 0000000..bad21c6 --- /dev/null +++ b/L2BotCore/Domain/Entities/Player.h @@ -0,0 +1,101 @@ +#pragma once +#include "WorldObject.h" +#include "../DTO/Player.h" +#include "../ValueObjects/FullName.h" +#include "../ValueObjects/VitalStats.h" +#include "../ValueObjects/Phenotype.h" + +namespace L2Bot::Domain::Entities +{ + class Player : public WorldObject + { + public: + const ValueObjects::FullName& GetFullName() const + { + return m_FullName; + } + const ValueObjects::Phenotype& GetPhenotype() const + { + return m_Phenotype; + } + + void UpdateFromDTO(const DTO::WorldObject* dto) override + { + const DTO::Player* castedDto = static_cast(dto); + WorldObject::UpdateFromDTO(dto); + m_FullName = castedDto->fullName; + m_Phenotype = castedDto->phenotype; + } + void SaveState() override + { + WorldObject::SaveState(); + m_PrevState = + { + m_FullName, + m_Phenotype + }; + } + const static Player CreateFromDTO(const DTO::Player& dto) + { + return Player( + dto.id, + dto.transform, + dto.fullName, + dto.phenotype + ); + } + const bool IsEqual(const DTO::WorldObject* dto) const override + { + const DTO::Player* castedDto = static_cast(dto); + return WorldObject::IsEqual(dto) && + m_FullName.IsEqual(&castedDto->fullName) && + m_Phenotype.IsEqual(&castedDto->phenotype); + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result = WorldObject::BuildSerializationNodes(); + + if (m_PrevState.isNewState || !m_FullName.IsEqual(&m_PrevState.fullName)) + { + result.push_back({ "fullName", m_FullName.BuildSerializationNodes() }); + } + if (m_PrevState.isNewState || !m_Phenotype.IsEqual(&m_PrevState.phenotype)) + { + result.push_back({ "phenotype", m_Phenotype.BuildSerializationNodes() }); + } + + return result; + } + + Player( + const uint32_t id, + const ValueObjects::Transform transform, + const ValueObjects::FullName fullName, + const ValueObjects::Phenotype phenotype + ) : + WorldObject(id, transform), + m_FullName(fullName), + m_Phenotype(phenotype) + { + SaveState(); + } + + Player() = default; + virtual ~Player() = default; + + private: + struct State + { + ValueObjects::FullName fullName = ValueObjects::FullName(); + ValueObjects::Phenotype phenotype = ValueObjects::Phenotype(); + + bool isNewState = true; + }; + + private: + ValueObjects::FullName m_FullName = ValueObjects::FullName(); + ValueObjects::Phenotype m_Phenotype = ValueObjects::Phenotype(); + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/Entities/WorldObject.h b/L2BotCore/Domain/Entities/WorldObject.h new file mode 100644 index 0000000..7fb11a2 --- /dev/null +++ b/L2BotCore/Domain/Entities/WorldObject.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include "../ValueObjects/Transform.h" +#include "../DTO/WorldObject.h" +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::Entities +{ + class WorldObject : public Serializers::Serializable + { + public: + const uint32_t GetId() const + { + return m_Id; + } + const ValueObjects::Transform& GetTransform() const + { + return m_Transform; + } + virtual void UpdateFromDTO(const DTO::WorldObject* dto) + { + SaveState(); + + m_Id = dto->id; + m_Transform = dto->transform; + } + virtual void SaveState() + { + m_PrevState = { m_Transform, false }; + } + virtual const bool IsEqual(const DTO::WorldObject* dto) const + { + return m_Id == dto->id && m_Transform.IsEqual(&dto->transform); + } + const float_t GetSqrDistance(const WorldObject& other) const + { + return m_Transform.GetSqrDistance(other.m_Transform); + } + const float_t GetHorizontalSqrDistance(const WorldObject& other) const + { + return m_Transform.GetHorizontalSqrDistance(other.m_Transform); + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result; + + result.push_back({ "id", std::to_string(GetId()) }); + if (m_PrevState.isNewState || !GetTransform().IsEqual(&m_PrevState.transform)) + { + result.push_back({ "transform", GetTransform().BuildSerializationNodes() }); + } + + return result; + } + + WorldObject(const uint32_t id, const ValueObjects::Transform transform) : + m_Id(id), m_Transform(transform) + { + + } + + WorldObject() = default; + virtual ~WorldObject() = default; + private: + private: + struct State + { + ValueObjects::Transform transform = ValueObjects::Transform(); + + bool isNewState = true; + }; + + private: + uint32_t m_Id = 0; + ValueObjects::Transform m_Transform = ValueObjects::Transform(); + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/Enums/ClassEnum.h b/L2BotCore/Domain/Enums/ClassEnum.h new file mode 100644 index 0000000..cb3b180 --- /dev/null +++ b/L2BotCore/Domain/Enums/ClassEnum.h @@ -0,0 +1,99 @@ +#pragma once +#include + +namespace L2Bot::Domain::Enums +{ + enum class ClassEnum : uint8_t + { + none = 255, + humanFighter = 0, + warrior, + gladiator, + warlord, + humanKnight, + paladin, + darkAvenger, + rogue, + treasureHunter, + hawkeye, + humanMystic, + humanWizard, + sorceror, + necromancer, + warlock, + cleric, + bishop, + prophet, + elvenFighter, + elvenKnight, + templeKnight, + swordsinger, + elvenScout, + plainsWalker, + silverRanger, + elvenMystic, + elvenWizard, + spellsinger, + elementalSummoner, + elvenOracle, + elvenElder, + darkElvenFighter, + palusKnight, + shillienKnight, + bladedancer, + assassin, + abyssWalker, + phantomRanger, + darkElvenMystic, + darkElvenWizard, + spellhowler, + phantomSummoner, + shillienOracle, + shillienElder, + orcFighter, + orcRaider, + destroyer, + orcMonk, + tyrant, + orcMystic, + orcShaman, + overlord, + warcryer, + dwarvenFighter, + dwarvenScavenger, + bountyHunter, + dwarvenArtisan, + warsmith, + duelist = 88, + dreadnought, + phoenixKnight, + hellKnight, + sagittarius, + adventurer, + archmage, + soultaker, + arcanaLord, + cardinal, + hierophant, + evaTemplar, + swordMuse, + windRider, + moonlightSentinel, + mysticMuse, + elementalMaster, + evaSaint, + shillienTemplar, + spectralDancer, + ghostHunter, + ghostSentinel, + stormScreamer, + spectralMaster, + shillienSaint, + titan, + grandKhauatari, + dominator, + doomcryer, + fortuneSeeker, + maestro + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Enums/ObjectStateEnum.h b/L2BotCore/Domain/Enums/ObjectStateEnum.h new file mode 100644 index 0000000..1dc8aa3 --- /dev/null +++ b/L2BotCore/Domain/Enums/ObjectStateEnum.h @@ -0,0 +1,12 @@ +#pragma once + +namespace L2Bot::Domain::Enums +{ + enum class ObjectStateEnum + { + none, + created, + updated, + deleted + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Enums/RaceEnum.h b/L2BotCore/Domain/Enums/RaceEnum.h new file mode 100644 index 0000000..6795643 --- /dev/null +++ b/L2BotCore/Domain/Enums/RaceEnum.h @@ -0,0 +1,15 @@ +#pragma once +#include + +namespace L2Bot::Domain::Enums +{ + enum class RaceEnum : uint8_t + { + none = 255, + darkElf = 2, + dwarf = 4, + elf = 1, + human = 0, + orc = 3 + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Enums/SpoilStateEnum.h b/L2BotCore/Domain/Enums/SpoilStateEnum.h new file mode 100644 index 0000000..9674776 --- /dev/null +++ b/L2BotCore/Domain/Enums/SpoilStateEnum.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace L2Bot::Domain::Enums +{ + enum class SpoilStateEnum : uint32_t + { + none = 0, + spoiled, + sweepable + }; +}; \ No newline at end of file diff --git a/L2BotCore/Domain/Repositories/DropRepositoryInterface.h b/L2BotCore/Domain/Repositories/DropRepositoryInterface.h new file mode 100644 index 0000000..d1aaa11 --- /dev/null +++ b/L2BotCore/Domain/Repositories/DropRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/Drop.h" + +namespace L2Bot::Domain::Repositories +{ + class DropRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/HeroRepositoryInterface.h b/L2BotCore/Domain/Repositories/HeroRepositoryInterface.h new file mode 100644 index 0000000..20a467b --- /dev/null +++ b/L2BotCore/Domain/Repositories/HeroRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/Hero.h" + +namespace L2Bot::Domain::Repositories +{ + class HeroRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/ItemRepositoryInterface.h b/L2BotCore/Domain/Repositories/ItemRepositoryInterface.h new file mode 100644 index 0000000..080a4f4 --- /dev/null +++ b/L2BotCore/Domain/Repositories/ItemRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/BaseItem.h" + +namespace L2Bot::Domain::Repositories +{ + class ItemRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/NPCRepositoryInterface.h b/L2BotCore/Domain/Repositories/NPCRepositoryInterface.h new file mode 100644 index 0000000..ea17502 --- /dev/null +++ b/L2BotCore/Domain/Repositories/NPCRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/NPC.h" + +namespace L2Bot::Domain::Repositories +{ + class NPCRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/ObjectRepositoryInterface.h b/L2BotCore/Domain/Repositories/ObjectRepositoryInterface.h new file mode 100644 index 0000000..ede148c --- /dev/null +++ b/L2BotCore/Domain/Repositories/ObjectRepositoryInterface.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace L2Bot::Domain::Repositories +{ + template + class ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/PlayerRepositoryInterface.h b/L2BotCore/Domain/Repositories/PlayerRepositoryInterface.h new file mode 100644 index 0000000..7d8811a --- /dev/null +++ b/L2BotCore/Domain/Repositories/PlayerRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/Player.h" + +namespace L2Bot::Domain::Repositories +{ + class PlayerRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Repositories/SkillRepositoryInterface.h b/L2BotCore/Domain/Repositories/SkillRepositoryInterface.h new file mode 100644 index 0000000..9377bd9 --- /dev/null +++ b/L2BotCore/Domain/Repositories/SkillRepositoryInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include "ObjectRepositoryInterface.h" +#include "../DTO/Skill.h" + +namespace L2Bot::Domain::Repositories +{ + class SkillRepositoryInterface : public ObjectRepositoryInterface + { + public: + virtual const std::map GetObjects() override = 0; + }; +} diff --git a/L2BotCore/Domain/Serializers/Node.h b/L2BotCore/Domain/Serializers/Node.h new file mode 100644 index 0000000..292a0ea --- /dev/null +++ b/L2BotCore/Domain/Serializers/Node.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include +#include + +namespace L2Bot::Domain::Serializers +{ + struct Node + { + const std::string name = ""; + const std::string value = ""; + const std::vector children; + const bool isArray = false; + const bool isContainer = false; + + Node() = delete; + Node(const std::string name, const std::string value) : + name(name), value(value) + { + } + Node(const std::string name, const std::vector children, const bool isArray = false) : + name(name), children(children), isArray(isArray), isContainer(true) + { + } + }; +} diff --git a/L2BotCore/Domain/Serializers/Serializable.h b/L2BotCore/Domain/Serializers/Serializable.h new file mode 100644 index 0000000..b38d5a1 --- /dev/null +++ b/L2BotCore/Domain/Serializers/Serializable.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include "Node.h" + +namespace L2Bot::Domain::Serializers +{ + class Serializable + { + public: + virtual const std::vector BuildSerializationNodes() const = 0; + }; +} diff --git a/L2BotCore/Domain/Serializers/SerializableStateContainer.h b/L2BotCore/Domain/Serializers/SerializableStateContainer.h new file mode 100644 index 0000000..c8eb67f --- /dev/null +++ b/L2BotCore/Domain/Serializers/SerializableStateContainer.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include "../DTO/ObjectState.h" +#include "Serializable.h" + +namespace L2Bot::Domain::Serializers +{ + template + class SerializableStateContainer : public Serializers::Serializable + { + public: + const std::vector BuildSerializationNodes() const override + { + std::vector result; + + for (const auto& kvp : m_Objects) + { + std::string operationName = ""; + switch (kvp.state) + { + case Enums::ObjectStateEnum::created: + operationName = "created"; + break; + case Enums::ObjectStateEnum::updated: + operationName = "updated"; + break; + case Enums::ObjectStateEnum::deleted: + operationName = "deleted"; + break; + } + + if (operationName != "") + { + result.push_back( + { + m_ContainerName, + std::vector{ { operationName, kvp.object.BuildSerializationNodes() } } + } + ); + } + } + + return result; + } + + SerializableStateContainer(const std::vector> objects, const std::string containerName) : + m_Objects(objects), m_ContainerName(containerName) + { + + } + SerializableStateContainer() = delete; + virtual ~SerializableStateContainer() = default; + private: + const std::vector> m_Objects; + const std::string m_ContainerName; + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Serializers/SerializableStateContainerPtr.h b/L2BotCore/Domain/Serializers/SerializableStateContainerPtr.h new file mode 100644 index 0000000..1e7e03a --- /dev/null +++ b/L2BotCore/Domain/Serializers/SerializableStateContainerPtr.h @@ -0,0 +1,57 @@ +#pragma once +#include +#include "../DTO/ObjectState.h" +#include "Serializable.h" + +namespace L2Bot::Domain::Serializers +{ + template + class SerializableStateContainerPtr : public Serializers::Serializable + { + public: + const std::vector BuildSerializationNodes() const override + { + std::vector result; + + for (const auto& kvp : m_Objects) + { + std::string operationName = ""; + switch (kvp.state) + { + case Enums::ObjectStateEnum::created: + operationName = "created"; + break; + case Enums::ObjectStateEnum::updated: + operationName = "updated"; + break; + case Enums::ObjectStateEnum::deleted: + operationName = "deleted"; + break; + } + + if (operationName != "") + { + result.push_back( + { + m_ContainerName, + std::vector{ { operationName, kvp.object->BuildSerializationNodes() } } + } + ); + } + } + + return result; + } + + SerializableStateContainerPtr(const std::vector> objects, const std::string containerName) : + m_Objects(objects), m_ContainerName(containerName) + { + + } + SerializableStateContainerPtr() = delete; + virtual ~SerializableStateContainerPtr() = default; + private: + const std::vector> m_Objects; + const std::string m_ContainerName; + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/Serializers/SerializerInterface.h b/L2BotCore/Domain/Serializers/SerializerInterface.h new file mode 100644 index 0000000..76d1710 --- /dev/null +++ b/L2BotCore/Domain/Serializers/SerializerInterface.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include "Serializable.h" + +namespace L2Bot::Domain::Serializers +{ + class SerializerInterface + { + public: + virtual const std::string Serialize(std::vector nodes, const bool isArray = false) const = 0; + }; +} diff --git a/L2BotCore/Domain/Services/DropService.h b/L2BotCore/Domain/Services/DropService.h new file mode 100644 index 0000000..c30f628 --- /dev/null +++ b/L2BotCore/Domain/Services/DropService.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/Drop.h" +#include "../Entities/Drop.h" +#include "../Repositories/DropRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class DropService : public ObjectService + { + public: + DropService(Repositories::DropRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~DropService() override = default; + }; +} diff --git a/L2BotCore/Domain/Services/HeroService.h b/L2BotCore/Domain/Services/HeroService.h new file mode 100644 index 0000000..1f985f3 --- /dev/null +++ b/L2BotCore/Domain/Services/HeroService.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/Hero.h" +#include "../Entities/Hero.h" +#include "../Repositories/HeroRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class HeroService : public ObjectService + { + public: + const DTO::ObjectState GetHero() + { + const auto map = GetObjects(); + if (map.size() == 0) + { + return DTO::ObjectState {}; + } + + return map[0]; + } + + HeroService(Repositories::HeroRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~HeroService() override = default; + }; +} diff --git a/L2BotCore/Domain/Services/ItemService.h b/L2BotCore/Domain/Services/ItemService.h new file mode 100644 index 0000000..8f2f4f5 --- /dev/null +++ b/L2BotCore/Domain/Services/ItemService.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/BaseItem.h" +#include "../ValueObjects/BaseItem.h" +#include "../Repositories/ItemRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class ItemService : public ObjectService + { + public: + ItemService(Repositories::ItemRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~ItemService() override = default; + }; +} diff --git a/L2BotCore/Domain/Services/NPCService.h b/L2BotCore/Domain/Services/NPCService.h new file mode 100644 index 0000000..fda28cc --- /dev/null +++ b/L2BotCore/Domain/Services/NPCService.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/NPC.h" +#include "../Entities/NPC.h" +#include "../Repositories/NPCRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class NPCService : public ObjectService + { + public: + NPCService(Repositories::NPCRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~NPCService() override = default; + }; +} diff --git a/L2BotCore/Domain/Services/ObjectService.h b/L2BotCore/Domain/Services/ObjectService.h new file mode 100644 index 0000000..2e6e19c --- /dev/null +++ b/L2BotCore/Domain/Services/ObjectService.h @@ -0,0 +1,100 @@ +#pragma once +#include +#include +#include +#include +#include "../DTO/ObjectState.h" +#include "../Entities/Hero.h" +#include "../Repositories/ObjectRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + template + class ObjectService + { + public: + ObjectService(Repositories::ObjectRepositoryInterface& repository) : m_Repository(repository) + { + + } + ObjectService() = delete; + virtual ~ObjectService() = default; + + virtual const std::vector> GetObjects() + { + UpdateObjectsFromRepository(); + + std::vector> objects; + + for (const auto& kvp : m_ObjectStates) { + objects.push_back(kvp.second); + } + + return objects; + } + + void Invalidate() + { + m_ObjectStates.clear(); + } + + protected: + virtual void UpdateObjectsFromRepository() + { + auto objects = m_Repository.GetObjects(); + + RemoveOutdatedStates(); + + + for (const auto& kvp : objects) + { + const auto& dto = kvp.second; + if (m_ObjectStates.contains(kvp.first)) + { + if (!m_ObjectStates[kvp.first].object.IsEqual(&dto)) { + m_ObjectStates[kvp.first].object.UpdateFromDTO(&dto); + m_ObjectStates[kvp.first].state = Enums::ObjectStateEnum::updated; + } + else + { + m_ObjectStates[kvp.first].state = Enums::ObjectStateEnum::none; + } + } + else + { + m_ObjectStates.emplace(kvp.first, DTO::ObjectState{ T::CreateFromDTO(dto), Enums::ObjectStateEnum::created }); + } + } + + for (auto& kvp : m_ObjectStates) + { + if (!objects.contains(kvp.second.object.GetId())) + { + m_ObjectStates[kvp.first].object.SaveState(); + kvp.second.state = Enums::ObjectStateEnum::deleted; + } + } + } + + private: + void RemoveOutdatedStates() + { + auto it = m_ObjectStates.begin(); + while (it != m_ObjectStates.end()) + { + if (it->second.state == Enums::ObjectStateEnum::deleted) + { + m_ObjectStates.erase(it++); + } + else + { + it++; + } + } + } + + private: + Repositories::ObjectRepositoryInterface& m_Repository; + std::map> m_ObjectStates; + }; +} diff --git a/L2BotCore/Domain/Services/ObjectServicePtr.h b/L2BotCore/Domain/Services/ObjectServicePtr.h new file mode 100644 index 0000000..a18c69c --- /dev/null +++ b/L2BotCore/Domain/Services/ObjectServicePtr.h @@ -0,0 +1,99 @@ +#pragma once +#include +#include +#include +#include +#include "../DTO/ObjectState.h" +#include "../Entities/Hero.h" +#include "../Repositories/ObjectRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + template + class ObjectServicePtr + { + public: + ObjectServicePtr(Repositories::ObjectRepositoryInterface& repository) : m_Repository(repository) + { + + }ObjectServicePtr() = delete; + virtual ~ObjectServicePtr() = default; + + virtual const std::vector> GetObjects() + { + UpdateObjectsFromRepository(); + + std::vector> objects; + + for (const auto& kvp : m_ObjectStates) { + objects.push_back(kvp.second); + } + + return objects; + } + + void Invalidate() + { + m_ObjectStates.clear(); + } + + protected: + virtual void UpdateObjectsFromRepository() + { + auto objects = m_Repository.GetObjects(); + + RemoveOutdatedStates(); + + + for (const auto& kvp : objects) + { + const auto& dto = kvp.second; + if (m_ObjectStates.contains(kvp.first)) + { + if (!m_ObjectStates[kvp.first].object->IsEqual(dto.get())) { + m_ObjectStates[kvp.first].object->UpdateFromDTO(dto.get()); + m_ObjectStates[kvp.first].state = Enums::ObjectStateEnum::updated; + } + else + { + m_ObjectStates[kvp.first].state = Enums::ObjectStateEnum::none; + } + } + else + { + //m_ObjectStates.emplace(kvp.first, DTO::ObjectState{ T::CreateFromDTO(dto), Enums::ObjectStateEnum::created }); + } + } + + for (auto& kvp : m_ObjectStates) + { + if (!objects.contains(kvp.second.object->GetId())) + { + m_ObjectStates[kvp.first].object->SaveState(); + kvp.second.state = Enums::ObjectStateEnum::deleted; + } + } + } + + private: + void RemoveOutdatedStates() + { + auto it = m_ObjectStates.begin(); + while (it != m_ObjectStates.end()) + { + if (it->second.state == Enums::ObjectStateEnum::deleted) + { + m_ObjectStates.erase(it++); + } + else + { + it++; + } + } + } + + private: + Repositories::ObjectRepositoryInterface& m_Repository; + std::map> m_ObjectStates; + }; +} diff --git a/L2BotCore/Domain/Services/PlayerService.h b/L2BotCore/Domain/Services/PlayerService.h new file mode 100644 index 0000000..601446b --- /dev/null +++ b/L2BotCore/Domain/Services/PlayerService.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/Player.h" +#include "../Entities/Player.h" +#include "../Repositories/PlayerRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class PlayerService : public ObjectService + { + public: + PlayerService(Repositories::PlayerRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~PlayerService() override = default; + }; +} diff --git a/L2BotCore/Domain/Services/SkillService.h b/L2BotCore/Domain/Services/SkillService.h new file mode 100644 index 0000000..e0a7912 --- /dev/null +++ b/L2BotCore/Domain/Services/SkillService.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "ObjectService.h" +#include "../DTO/Skill.h" +#include "../ValueObjects/Skill.h" +#include "../Repositories/SkillRepositoryInterface.h" + +namespace L2Bot::Domain::Services +{ + class SkillService : public ObjectService + { + public: + SkillService(Repositories::SkillRepositoryInterface& repository) : ObjectService(repository) + { + + } + virtual ~SkillService() override = default; + }; +} diff --git a/L2BotCore/Domain/Transports/TransportInterface.h b/L2BotCore/Domain/Transports/TransportInterface.h new file mode 100644 index 0000000..1f4ee79 --- /dev/null +++ b/L2BotCore/Domain/Transports/TransportInterface.h @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace L2Bot::Domain::Transports +{ + class TransportInterface + { + public: + virtual const bool Connect() = 0; + virtual const bool IsConnected() const = 0; + virtual const void Send(std::string data) = 0; + virtual const std::string Receive() = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/BaseItem.h b/L2BotCore/Domain/ValueObjects/BaseItem.h new file mode 100644 index 0000000..3f673a4 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/BaseItem.h @@ -0,0 +1,158 @@ +#pragma once +#include +#include +#include +#include "../DTO/BaseItem.h" +#include "../Serializers/Serializable.h" +#include "../Serializers/Node.h" + +namespace L2Bot::Domain::ValueObjects +{ + class BaseItem : public Serializers::Serializable + { + public: + const uint32_t GetId() const + { + return m_ItemId; + } + void UpdateFromDTO(const DTO::BaseItem* dto) + { + SaveState(); + + m_ItemId = dto->itemId; + m_Amount = dto->amount; + m_IsEquipped = dto->isEquipped; + m_EnchantLevel = dto->enchantLevel; + m_Mana = dto->mana; + m_Name = dto->name; + m_IconName = dto->iconName; + m_Description = dto->description; + m_Weight = dto->weight; + } + void SaveState() + { + m_PrevState = + { + m_Amount, + m_IsEquipped, + m_EnchantLevel, + m_Mana, + m_Weight, + false + }; + } + const static BaseItem CreateFromDTO(const DTO::BaseItem& dto) + { + return BaseItem( + dto.itemId, + dto.amount, + dto.isEquipped, + dto.enchantLevel, + dto.mana, + dto.name, + dto.iconName, + dto.description, + dto.weight + ); + } + const bool IsEqual(const DTO::BaseItem* dto) const + { + return m_ItemId == dto->itemId && + m_Amount == dto->amount && + m_IsEquipped == dto->isEquipped && + m_EnchantLevel == dto->enchantLevel && + m_Mana == dto->mana && + m_Name == dto->name && + m_IconName == dto->iconName && + m_Description == dto->description && + m_Weight == dto->weight; + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result; + + result.push_back({ "itemId", std::to_string(m_ItemId) }); + + if (m_PrevState.isNewState) + { + result.push_back({ "name", m_Name }); + result.push_back({ "iconName", m_IconName }); + result.push_back({ "description", m_Description }); + } + + if (m_PrevState.isNewState || m_Amount != m_PrevState.amount) + { + result.push_back({ "amount", std::to_string(m_Amount) }); + } + if (m_PrevState.isNewState || m_IsEquipped != m_PrevState.isEquipped) + { + result.push_back({ "isEquipped", std::to_string(m_IsEquipped) }); + } + if (m_PrevState.isNewState || m_EnchantLevel != m_PrevState.enchantLevel) + { + result.push_back({ "enchantLevel", std::to_string(m_EnchantLevel) }); + } + if (m_PrevState.isNewState || m_Mana != m_PrevState.mana) + { + result.push_back({ "mana", std::to_string(m_Mana) }); + } + if (m_PrevState.isNewState || m_Weight != m_PrevState.weight) + { + result.push_back({ "weight", std::to_string(m_Weight) }); + } + + return result; + } + + BaseItem( + const uint32_t itemId, + const uint32_t amount, + const bool isEquipped, + const uint16_t enchantLevel, + const int32_t mana, + const std::string name, + const std::string iconName, + const std::string description, + const uint16_t weight + ) : + m_ItemId(itemId), + m_Amount(amount), + m_IsEquipped(isEquipped), + m_EnchantLevel(enchantLevel), + m_Mana(mana), + m_Name(name), + m_IconName(iconName), + m_Description(description), + m_Weight(weight) + { + } + + BaseItem() = default; + virtual ~BaseItem() = default; + + private: + struct State + { + uint32_t amount = 0; + bool isEquipped = 0; + uint16_t enchantLevel = 0; + int32_t mana = -1; + uint16_t weight = 0; + + bool isNewState = true; + }; + + private: + uint32_t m_ItemId = 0; + uint32_t m_Amount = 0; + bool m_IsEquipped = 0; + uint16_t m_EnchantLevel = 0; + int32_t m_Mana = -1; + std::string m_Name = ""; + std::string m_IconName = ""; + std::string m_Description = ""; + uint16_t m_Weight = 0; + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/ValueObjects/ExperienceInfo.h b/L2BotCore/Domain/ValueObjects/ExperienceInfo.h new file mode 100644 index 0000000..43fa434 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/ExperienceInfo.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class ExperienceInfo : public Serializers::Serializable + { + public: + const uint8_t GetLevel() const + { + return m_Level; + } + const uint8_t GetExp() const + { + return m_Exp; + } + const uint8_t GetSp() const + { + return m_Sp; + } + const bool IsEqual(const ExperienceInfo* other) const + { + return m_Level == other->m_Level && m_Exp == other->m_Exp && m_Sp == other->m_Sp; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "level", std::to_string(m_Level) }, + { "exp", std::to_string(m_Exp) }, + { "sp", std::to_string(m_Sp) } + }; + } + + ExperienceInfo( + const uint8_t level, + const uint32_t exp, + const uint32_t sp + ) : + m_Level(level), + m_Exp(exp), + m_Sp(sp) + { + } + + ExperienceInfo() = default; + virtual ~ExperienceInfo() = default; + private: + uint8_t m_Level = 0; + uint32_t m_Exp = 0; + uint32_t m_Sp = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/FullName.h b/L2BotCore/Domain/ValueObjects/FullName.h new file mode 100644 index 0000000..4c727fb --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/FullName.h @@ -0,0 +1,47 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class FullName : public Serializers::Serializable + { + public: + const std::string& GetNickname() const + { + return m_Nickname; + } + const std::string& GetTitle() const + { + return m_Title; + } + const bool IsEqual(const FullName* other) const + { + return m_Nickname == other->m_Nickname && m_Title == other->m_Title; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "nickname", m_Nickname }, + { "title", m_Title } + }; + } + + FullName( + const std::string nickname, + const std::string title + ) : + m_Nickname(nickname), + m_Title(title) + { + } + + FullName() = default; + virtual ~FullName() = default; + private: + std::string m_Nickname = ""; + std::string m_Title = ""; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/InventoryInfo.h b/L2BotCore/Domain/ValueObjects/InventoryInfo.h new file mode 100644 index 0000000..420b82b --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/InventoryInfo.h @@ -0,0 +1,61 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class InventoryInfo : public Serializers::Serializable + { + public: + const bool IsOverloaded() const + { + return m_Weight >= m_MaxWeight; + } + const uint16_t GetMaxWeight() const + { + return m_MaxWeight; + } + const uint16_t GetWeight() const + { + return m_Weight; + } + const uint16_t GetSlots() const + { + return m_Slots; + } + const bool IsEqual(const InventoryInfo* other) const + { + return m_MaxWeight == other->m_MaxWeight && + m_Weight == other->m_Weight && + m_Slots == other->m_Slots; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "maxWeight", std::to_string(m_MaxWeight) }, + { "weight", std::to_string(m_Weight) }, + { "slots", std::to_string(m_Slots) } + }; + } + + InventoryInfo( + const uint16_t maxWeight, + const uint16_t weight, + const uint16_t slots + ) : + m_MaxWeight(maxWeight), + m_Weight(weight), + m_Slots(slots) + { + } + + InventoryInfo() = default; + virtual ~InventoryInfo() = default; + private: + uint16_t m_MaxWeight = 0; + uint16_t m_Weight = 0; + uint16_t m_Slots = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/PermanentStats.h b/L2BotCore/Domain/ValueObjects/PermanentStats.h new file mode 100644 index 0000000..c81ae9a --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/PermanentStats.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class PermanentStats : public Serializers::Serializable + { + public: + const uint16_t GetStr() const + { + return m_Str; + } + const uint16_t GetDex() const + { + return m_Dex; + } + const uint16_t GetCon() const + { + return m_Con; + } + const uint16_t GetInt() const + { + return m_Int; + } + const uint16_t GetMen() const + { + return m_Men; + } + const uint16_t GetWit() const + { + return m_Wit; + } + const bool IsEqual(const PermanentStats* other) const + { + return m_Str == other->m_Str && + m_Dex == other->m_Dex && + m_Con == other->m_Con && + m_Int == other->m_Int && + m_Men == other->m_Men && + m_Wit == other->m_Wit; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "str", std::to_string(m_Str) }, + { "dex", std::to_string(m_Dex) }, + { "con", std::to_string(m_Con) }, + { "int", std::to_string(m_Int) }, + { "men", std::to_string(m_Men) }, + { "wit", std::to_string(m_Wit) } + }; + } + + PermanentStats( + uint16_t str, + uint16_t dex, + uint16_t con, + uint16_t int_, + uint16_t men, + uint16_t wit + ) : + m_Str(str), + m_Dex(dex), + m_Con(con), + m_Int(int_), + m_Men(men), + m_Wit(wit) + { + } + + PermanentStats() = default; + virtual ~PermanentStats() = default; + + private: + uint16_t m_Str = 0; + uint16_t m_Dex = 0; + uint16_t m_Con = 0; + uint16_t m_Int = 0; + uint16_t m_Men = 0; + uint16_t m_Wit = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/Phenotype.h b/L2BotCore/Domain/ValueObjects/Phenotype.h new file mode 100644 index 0000000..15e2bce --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/Phenotype.h @@ -0,0 +1,72 @@ +#pragma once +#include "../Enums/RaceEnum.h" +#include "../Enums/ClassEnum.h" +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class Phenotype : public Serializers::Serializable + { + public: + const bool IsSubClass() const + { + return m_ActiveClass != Enums::ClassEnum::none && m_Class != m_ActiveClass; + } + const Enums::RaceEnum GetRace() const + { + return m_Race; + } + const bool IsMale() const + { + return m_IsMale; + } + const Enums::ClassEnum GetClass() const + { + return m_Class; + } + const Enums::ClassEnum GetActiveClass() const + { + return m_ActiveClass; + } + const bool IsEqual(const Phenotype* other) const + { + return m_Race == other->m_Race && + m_IsMale == other->m_IsMale && + m_Class == other->m_Class && + m_ActiveClass == other->m_ActiveClass; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "race", std::to_string(static_cast(m_Race)) }, + { "isMale", std::to_string(m_IsMale) }, + { "class", std::to_string(static_cast(m_Class)) }, + { "activeClass", std::to_string(static_cast(m_ActiveClass)) } + }; + } + + Phenotype( + Enums::RaceEnum race, + bool isMale, + Enums::ClassEnum class_, + Enums::ClassEnum activeClass + ) : + m_Race(race), + m_IsMale(isMale), + m_Class(class_), + m_ActiveClass(activeClass) + { + } + + Phenotype() = default; + virtual ~Phenotype() = default; + + private: + Enums::RaceEnum m_Race = Enums::RaceEnum::none; + bool m_IsMale = true; + Enums::ClassEnum m_Class = Enums::ClassEnum::none; + Enums::ClassEnum m_ActiveClass = Enums::ClassEnum::none; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/Reputation.h b/L2BotCore/Domain/ValueObjects/Reputation.h new file mode 100644 index 0000000..d6f0d65 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/Reputation.h @@ -0,0 +1,80 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class Reputation : public Serializers::Serializable + { + public: + const bool IsPlayerKiller() const + { + return m_Karma > 0; + } + const uint16_t GetKarma() const + { + return m_Karma; + } + const uint16_t GetPkKills() const + { + return m_PkKills; + } + const uint16_t GetPvpKills() const + { + return m_PvpKills; + } + const uint8_t GetRecRemaining() const + { + return m_RecRemaining; + } + const uint8_t GetEvalScore() const + { + return m_EvalScore; + } + const bool IsEqual(const Reputation* other) const + { + return m_Karma == other->m_Karma && + m_PkKills == other->m_PkKills && + m_PvpKills == other->m_PvpKills && + m_RecRemaining == other->m_RecRemaining && + m_EvalScore == other->m_EvalScore; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "karma", std::to_string(m_Karma) }, + { "pkKills", std::to_string(m_PkKills) }, + { "pvpKills", std::to_string(m_PvpKills) }, + { "recRemaining", std::to_string(m_RecRemaining) }, + { "evalScore", std::to_string(m_EvalScore) } + }; + } + + Reputation( + uint16_t karma, + uint16_t pkKills, + uint16_t pvpKills, + uint8_t recRemaining, + uint8_t evalScore + ) : + m_Karma(karma), + m_PkKills(pkKills), + m_PvpKills(pvpKills), + m_RecRemaining(recRemaining), + m_EvalScore(evalScore) + { + } + + Reputation() = default; + virtual ~Reputation() = default; + + private: + uint16_t m_Karma = 0; + uint16_t m_PkKills = 0; + uint16_t m_PvpKills = 0; + uint8_t m_RecRemaining = 0; + uint8_t m_EvalScore = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/Skill.h b/L2BotCore/Domain/ValueObjects/Skill.h new file mode 100644 index 0000000..bcedb22 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/Skill.h @@ -0,0 +1,188 @@ +#pragma once +#include +#include +#include +#include "../DTO/Skill.h" +#include "../Serializers/Serializable.h" +#include "../Serializers/Node.h" + +namespace L2Bot::Domain::ValueObjects +{ + class Skill : public Serializers::Serializable + { + public: + const uint32_t GetId() const + { + return m_SkillId; + } + const bool IsReadyToUse() const + { + return !m_IsCasting && !m_IsReloading; + } + void UpdateFromDTO(const DTO::Skill* dto) + { + SaveState(); + + m_SkillId = dto->skillId; + m_Level = dto->level; + m_IsActive = dto->isActive; + m_Cost = dto->cost; + m_Range = dto->range; + m_Name = dto->name; + m_Description = dto->description; + m_IconName = dto->iconName; + m_IsToggled = dto->isToggled; + m_IsCasting = dto->isCasting; + m_IsReloading = dto->isReloading; + } + void SaveState() + { + m_PrevState = + { + m_Cost, + m_Range, + m_Description, + m_IsToggled, + m_IsCasting, + m_IsReloading, + IsReadyToUse(), + false + }; + } + const static Skill CreateFromDTO(const DTO::Skill& dto) + { + return Skill( + dto.skillId, + dto.level, + dto.isActive, + dto.cost, + dto.range, + dto.name, + dto.description, + dto.iconName, + dto.isToggled, + dto.isCasting, + dto.isReloading + ); + } + const bool IsEqual(const DTO::Skill* dto) const + { + return m_SkillId == dto->skillId && + m_Level == dto->level && + m_IsActive == dto->isActive && + m_Cost == dto->cost && + m_Range == dto->range && + m_Name == dto->name && + m_Description == dto->description && + m_IconName == dto->iconName && + m_IsToggled == dto->isToggled && + m_IsCasting == dto->isCasting && + m_IsReloading == dto->isReloading; + } + + const std::vector BuildSerializationNodes() const override + { + std::vector result; + + result.push_back({ "skillId", std::to_string(m_SkillId) }); + result.push_back({ "level", std::to_string(m_Level) }); + + if (m_PrevState.isNewState) + { + result.push_back({ "isActive", std::to_string(m_IsActive) }); + result.push_back({ "name", m_Name }); + result.push_back({ "iconName", m_IconName }); + } + + if (m_PrevState.isNewState || m_Description != m_PrevState.description) + { + result.push_back({ "description", m_Description }); + } + if (m_PrevState.isNewState || m_Cost != m_PrevState.cost) + { + result.push_back({ "cost", std::to_string(m_Cost) }); + } + if (m_PrevState.isNewState || m_Range != m_PrevState.range) + { + result.push_back({ "range", std::to_string(m_Range) }); + } + if (m_PrevState.isNewState || m_IsToggled != m_PrevState.isToggled) + { + result.push_back({ "isToggled", std::to_string(m_IsToggled) }); + } + if (m_PrevState.isNewState || m_IsCasting != m_PrevState.isCasting) + { + result.push_back({ "isCasting", std::to_string(m_IsCasting) }); + } + if (m_PrevState.isNewState || m_IsReloading != m_PrevState.isReloading) + { + result.push_back({ "isReloading", std::to_string(m_IsReloading) }); + } + if (m_PrevState.isNewState || IsReadyToUse() != m_PrevState.isReadyToUse) + { + result.push_back({ "isReadyToUse", std::to_string(IsReadyToUse()) }); + } + + return result; + } + + Skill( + const uint32_t skillId, + const uint8_t level, + const bool isActive, + const uint8_t cost, + const int16_t range, + const std::string& name, + const std::string& description, + const std::string& iconName, + const bool isToggled, + const bool isCasting, + const bool isReloading + ) : + m_SkillId(skillId), + m_Level(level), + m_IsActive(isActive), + m_Cost(cost), + m_Range(range), + m_Name(name), + m_Description(description), + m_IconName(iconName), + m_IsToggled(isToggled), + m_IsCasting(isCasting), + m_IsReloading(isReloading) + { + + } + + Skill() = default; + virtual ~Skill() = default; + + private: + struct State + { + uint8_t cost = 0; + int16_t range = 0; + std::string description = ""; + bool isToggled = false; + bool isCasting = false; + bool isReloading = false; + bool isReadyToUse = true; + + bool isNewState = true; + }; + + private: + uint32_t m_SkillId = 0; + uint8_t m_Level = 0; + bool m_IsActive = false; + uint8_t m_Cost = 0; + int16_t m_Range = 0; + std::string m_Name = ""; + std::string m_Description = ""; + std::string m_IconName = ""; + bool m_IsToggled = false; + bool m_IsCasting = false; + bool m_IsReloading = false; + State m_PrevState = State(); + }; +} diff --git a/L2BotCore/Domain/ValueObjects/Transform.h b/L2BotCore/Domain/ValueObjects/Transform.h new file mode 100644 index 0000000..5bb7a6c --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/Transform.h @@ -0,0 +1,74 @@ +#pragma once +#include "../ValueObjects/Vector3.h" +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class Transform : public Serializers::Serializable + { + public: + const Vector3& GetPosition() const + { + return m_Position; + } + const Vector3& GetRotation() const + { + return m_Rotation; + } + const Vector3& GetVelocity() const + { + return m_Velocity; + } + const Vector3& GetAcceleration() const + { + return m_Acceleration; + } + const bool IsEqual(const Transform* other) const + { + return m_Position.IsEqual(&other->m_Position) && + m_Rotation.IsEqual(&other->m_Rotation) && + m_Velocity.IsEqual(&other->m_Velocity) && + m_Acceleration.IsEqual(&other->m_Acceleration); + } + const float_t GetSqrDistance(const Transform& other) const + { + return m_Position.GetSqrDistance(other.m_Position); + } + const float_t GetHorizontalSqrDistance(const Transform& other) const + { + return m_Position.GetHorizontalSqrDistance(other.m_Position); + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "position", m_Position.BuildSerializationNodes() }, + { "rotation", m_Rotation.BuildSerializationNodes() }, + { "velocity", m_Velocity.BuildSerializationNodes() }, + { "acceleration", m_Acceleration.BuildSerializationNodes() } + }; + } + + Transform( + const Vector3 position, + const Vector3 rotation, + const Vector3 velocity, + const Vector3 acceleration + ) : + m_Position(position), + m_Rotation(rotation), + m_Velocity(velocity), + m_Acceleration(acceleration) + { + } + + Transform() = default; + virtual ~Transform() = default; + private: + Vector3 m_Position = Vector3(); + Vector3 m_Rotation = Vector3(); + Vector3 m_Velocity = Vector3(); + Vector3 m_Acceleration = Vector3(); + }; +} diff --git a/L2BotCore/Domain/ValueObjects/VariableStats.h b/L2BotCore/Domain/ValueObjects/VariableStats.h new file mode 100644 index 0000000..cbd0601 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/VariableStats.h @@ -0,0 +1,112 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class VariableStats : public Serializers::Serializable + { + public: + const uint16_t GetAccuracy() const + { + return m_Accuracy; + } + const uint16_t GetCritRate() const + { + return m_CritRate; + } + const uint16_t GetPAttack() const + { + return m_PAttack; + } + const uint16_t GetAttackSpeed() const + { + return m_AttackSpeed; + } + const uint16_t GetPDefense() const + { + return m_PDefense; + } + const uint16_t GetEvasion() const + { + return m_Evasion; + } + const uint16_t GetMAttack() const + { + return m_MAttack; + } + const uint16_t GetMDefense() const + { + return m_MDefense; + } + const uint16_t GetCastingSpeed() const + { + return m_CastingSpeed; + } + const bool IsEqual(const VariableStats* other) const + { + return m_Accuracy == other->m_Accuracy && + m_CritRate == other->m_CritRate && + m_PAttack == other->m_PAttack && + m_AttackSpeed == other->m_AttackSpeed && + m_PDefense == other->m_PDefense && + m_Evasion == other->m_Evasion && + m_MAttack == other->m_MAttack && + m_MDefense == other->m_MDefense && + m_CastingSpeed == other->m_CastingSpeed; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "accuracy", std::to_string(m_Accuracy) }, + { "critRate", std::to_string(m_CritRate) }, + { "pAttack", std::to_string(m_PAttack) }, + { "attackSpeed", std::to_string(m_AttackSpeed) }, + { "pDefense", std::to_string(m_PDefense) }, + { "evasion", std::to_string(m_Evasion) }, + { "mAttack", std::to_string(m_MAttack) }, + { "mDefense", std::to_string(m_MDefense) }, + { "castingSpeed", std::to_string(m_CastingSpeed) } + }; + } + + VariableStats( + uint16_t accuracy, + uint16_t critRate, + uint16_t pAttack, + uint16_t attackSpeed, + uint16_t pDefense, + uint16_t evasion, + uint16_t mAttack, + uint16_t mDefense, + uint16_t castingSpeed + ) : + m_Accuracy(accuracy), + m_CritRate(critRate), + m_PAttack(pAttack), + m_AttackSpeed(attackSpeed), + m_PDefense(pDefense), + m_Evasion(evasion), + m_MAttack(mAttack), + m_MDefense(mDefense), + m_CastingSpeed(castingSpeed) + { + } + + VariableStats() = default; + virtual ~VariableStats() = default; + + private: + uint16_t m_Accuracy = 0; + uint16_t m_CritRate = 0; + uint16_t m_PAttack = 0; + uint16_t m_AttackSpeed = 0; + uint16_t m_PDefense = 0; + uint16_t m_Evasion = 0; + uint16_t m_MAttack = 0; + uint16_t m_MDefense = 0; + uint16_t m_CastingSpeed = 0; + }; +} diff --git a/L2BotCore/Domain/ValueObjects/Vector3.h b/L2BotCore/Domain/ValueObjects/Vector3.h new file mode 100644 index 0000000..fbf5871 --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/Vector3.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class Vector3 : public Serializers::Serializable + { + public: + const float_t GetX() const + { + return m_X; + } + const float_t GetY() const + { + return m_Y; + } + const float_t GetZ() const + { + return m_Z; + } + const bool IsEqual(const Vector3* other) const + { + float_t epsilon = 0.0001f; + return fabsf(m_X - other->m_X) < epsilon && + fabsf(m_Y - other->m_Y) < epsilon && + fabsf(m_Z - other->m_Z) < epsilon; + } + const float_t GetSqrDistance(const Vector3& other) const + { + return (m_X - other.m_X) * (m_X - other.m_X) + + (m_Y - other.m_Y) * (m_Y - other.m_Y) + + (m_Z - other.m_Z) * (m_Z - other.m_Z); + } + const float_t GetHorizontalSqrDistance(const Vector3& other) const + { + return (m_X - other.m_X) * (m_X - other.m_X) + + (m_Y - other.m_Y) * (m_Y - other.m_Y); + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "x", std::to_string(m_X) }, + { "y", std::to_string(m_Y) }, + { "z", std::to_string(m_Z) } + }; + } + + Vector3(const float_t x, const float_t y, const float_t z) : + m_X(x), m_Y(y), m_Z(z) + { + } + + Vector3() = default; + virtual ~Vector3() = default; + + private: + float_t m_X = 0; + float_t m_Y = 0; + float_t m_Z = 0; + }; +} \ No newline at end of file diff --git a/L2BotCore/Domain/ValueObjects/VitalStats.h b/L2BotCore/Domain/ValueObjects/VitalStats.h new file mode 100644 index 0000000..6b4319d --- /dev/null +++ b/L2BotCore/Domain/ValueObjects/VitalStats.h @@ -0,0 +1,89 @@ +#pragma once +#include +#include "../Serializers/Serializable.h" + +namespace L2Bot::Domain::ValueObjects +{ + class VitalStats : public Serializers::Serializable + { + public: + const bool IsAlive() const + { + return m_MaxHp <= 0 || m_Hp > 0; + } + const uint32_t GetMaxHp() const + { + return m_MaxHp; + } + const uint32_t GetHp() const + { + return m_Hp; + } + const uint32_t GetMaxMp() const + { + return m_MaxMp; + } + const uint32_t GetMp() const + { + return m_Mp; + } + const uint32_t GetMaxCp() const + { + return m_MaxCp; + } + const uint32_t GetCp() const + { + return m_Cp; + } + const bool IsEqual(const VitalStats* other) const + { + return m_MaxHp == other->m_MaxHp && + m_Hp == other->m_Hp && + m_MaxMp == other->m_MaxMp && + m_Mp == other->m_Mp && + m_MaxCp == other->m_MaxCp && + m_Cp == other->m_Cp; + } + + const std::vector BuildSerializationNodes() const override + { + return std::vector + { + { "maxHp", std::to_string(m_MaxHp) }, + { "hp", std::to_string(m_Hp) }, + { "maxMp", std::to_string(m_MaxMp) }, + { "mp", std::to_string(m_Mp) }, + { "maxCp", std::to_string(m_MaxCp) }, + { "cp", std::to_string(m_Cp) } + }; + } + + VitalStats( + uint32_t maxHp, + uint32_t hp, + uint32_t maxMp, + uint32_t mp, + uint32_t maxCp, + uint32_t cp + ) : + m_MaxHp(maxHp), + m_Hp(hp), + m_MaxMp(maxMp), + m_Mp(mp), + m_MaxCp(maxCp), + m_Cp(cp) + { + } + + VitalStats() = default; + virtual ~VitalStats() = default; + + private: + uint32_t m_MaxHp = 0; + uint32_t m_Hp = 0; + uint32_t m_MaxMp = 0; + uint32_t m_Mp = 0; + uint32_t m_MaxCp = 0; + uint32_t m_Cp = 0; + }; +} diff --git a/L2BotCore/L2BotCore.vcxproj b/L2BotCore/L2BotCore.vcxproj new file mode 100644 index 0000000..39afd5a --- /dev/null +++ b/L2BotCore/L2BotCore.vcxproj @@ -0,0 +1,230 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {504a5403-ba08-46df-aa8a-b79993b56bca} + L2BotCore + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + false + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + + + + + true + true + true + + + + + Level3 + true + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + + + + + Level3 + true + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + stdcpp20 + stdcpp20 + stdcpp20 + stdcpp20 + + + + + + \ No newline at end of file diff --git a/L2BotCore/L2BotCore.vcxproj.filters b/L2BotCore/L2BotCore.vcxproj.filters new file mode 100644 index 0000000..7830a20 --- /dev/null +++ b/L2BotCore/L2BotCore.vcxproj.filters @@ -0,0 +1,180 @@ +п»ї + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/L2BotCore/framework.h b/L2BotCore/framework.h new file mode 100644 index 0000000..42b95f1 --- /dev/null +++ b/L2BotCore/framework.h @@ -0,0 +1,7 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +#include +#include diff --git a/L2BotCore/pch.cpp b/L2BotCore/pch.cpp new file mode 100644 index 0000000..64b7eef --- /dev/null +++ b/L2BotCore/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/L2BotCore/pch.h b/L2BotCore/pch.h new file mode 100644 index 0000000..885d5d6 --- /dev/null +++ b/L2BotCore/pch.h @@ -0,0 +1,13 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here +#include "framework.h" + +#endif //PCH_H diff --git a/L2BotDll/Application.h b/L2BotDll/Application.h new file mode 100644 index 0000000..0d701c5 --- /dev/null +++ b/L2BotDll/Application.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +#include "Services/WorldHandler.h" +#include "Domain/Repositories/SkillRepositoryInterface.h" +#include "Serializers/JsonSerializer.h" +#include "Transports/NamedPipeTransport.h" + +#include "Versions/VersionAbstractFactory.h" + +using namespace L2Bot::Domain; + +class Application +{ +public: + Application(const VersionAbstractFactory::Version version) : + m_AbstractFactory(VersionAbstractFactory::GetFactory(version, Application::RADIUS)), + m_Transport(Application::PIPE_NAME), + m_WorldHandler + ( + m_AbstractFactory.GetHeroRepository(), + m_AbstractFactory.GetDropRepository(), + m_AbstractFactory.GetNPCRepository(), + m_AbstractFactory.GetPlayerRepository(), + m_AbstractFactory.GetSkillRepository(), + m_Serializer, + m_Transport + ) + { + + } + Application() = delete; + virtual ~Application() = default; + + void Start() + { + HMODULE hEngine = GetModuleHandleA("Engine.dll"); + HMODULE hCore = GetModuleHandleA("Core.dll"); + + m_AbstractFactory.GetNetworkHandler().Init(hEngine); + m_AbstractFactory.GetGameEngine().Init(hEngine); + m_AbstractFactory.GetL2GameData().Init(hEngine); + m_AbstractFactory.GetFName().Init(hCore); + m_WorldHandler.Start(); + } + + void Stop() + { + m_WorldHandler.Stop(); + m_AbstractFactory.GetL2GameData().Restore(); + m_AbstractFactory.GetGameEngine().Restore(); + m_AbstractFactory.GetNetworkHandler().Restore(); + } + +private: + const VersionAbstractFactory& m_AbstractFactory; + WorldHandler m_WorldHandler; + JsonSerializer m_Serializer; + NamedPipeTransport m_Transport; + + static const std::string PIPE_NAME; + static const uint16_t RADIUS; +}; + +const std::string Application::PIPE_NAME = std::string("PipeL2Bot"); +const uint16_t Application::RADIUS = 2000; \ No newline at end of file diff --git a/L2BotDll/Common/Common.cpp b/L2BotDll/Common/Common.cpp new file mode 100644 index 0000000..c61ed45 --- /dev/null +++ b/L2BotDll/Common/Common.cpp @@ -0,0 +1,34 @@ +#include "pch.h" +#include "Common.h" +#include +#pragma comment(lib, "Rpcrt4.lib") + +std::string ConvertFromWideChar(const wchar_t* str) +{ + std::wstring ws(str); + std::string result(ws.begin(), ws.end()); + return result; +} + +std::string GenerateUUID() +{ + UUID uuid; + ::ZeroMemory(&uuid, sizeof(UUID)); + + ::UuidCreate(&uuid); + + WCHAR* wszUuid = NULL; + ::UuidToStringW(&uuid, (RPC_WSTR*)&wszUuid); + + if (wszUuid == NULL) + { + return ""; + } + + std::wstring ws = wszUuid; + + ::RpcStringFree((RPC_WSTR*)&wszUuid); + wszUuid = NULL; + + return std::string(ws.begin(), ws.end()); +} \ No newline at end of file diff --git a/L2BotDll/Common/Common.h b/L2BotDll/Common/Common.h new file mode 100644 index 0000000..05d7856 --- /dev/null +++ b/L2BotDll/Common/Common.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +std::string ConvertFromWideChar(const wchar_t* str); +std::string GenerateUUID(); \ No newline at end of file diff --git a/L2BotDll/Common/TimerMap.h b/L2BotDll/Common/TimerMap.h new file mode 100644 index 0000000..7e7fa1c --- /dev/null +++ b/L2BotDll/Common/TimerMap.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class TimerMap +{ +public: + TimerMap() = default; + virtual ~TimerMap() + { + StopAll(); + } + + void StartTimer(const uint32_t key, const uint32_t milliseconds, const std::function callback) + { + StopTimer(key); + + m_Timers[key].Start(milliseconds, callback, key); + } + + void StopTimer(uint32_t key) + { + if (m_Timers.find(key) != m_Timers.end()) + { + m_Timers[key].Stop(); + } + } + + void StopAll() + { + m_Timers.clear(); + } + +private: + class Timer + { + public: + void Start(const uint32_t milliseconds, const std::function callback, const uint32_t data) + { + m_Terminate = false; + m_Thread = std::thread([this, milliseconds, callback, data] { + std::unique_lock lk(m_Mutex); + + if (!m_Condition.wait_for(lk, std::chrono::milliseconds(milliseconds), [this]() { return m_Terminate == true; })) + { + callback(data); + } + }); + } + + void Stop() + { + m_Terminate = true; + m_Condition.notify_all(); + if (m_Thread.joinable()) + { + m_Thread.join(); + } + } + + Timer() = default; + virtual ~Timer() + { + Stop(); + + } + private: + std::condition_variable m_Condition; + std::mutex m_Mutex; + std::atomic_bool m_Terminate = false; + std::thread m_Thread; + }; + + std::map m_Timers; +}; \ No newline at end of file diff --git a/L2BotDll/Common/apihook.cpp b/L2BotDll/Common/apihook.cpp new file mode 100644 index 0000000..fad5716 --- /dev/null +++ b/L2BotDll/Common/apihook.cpp @@ -0,0 +1,75 @@ +#include "pch.h" +#include "apihook.h" +#include "Trampoline.h" + +#pragma pack(push, 1) +struct CallJmpInstr +{ + BYTE opcode; + DWORD rel32; +}; +#pragma pack(pop) +#pragma pack(push, 1) +struct SavedFunction +{ + DWORD originalAddress; + BYTE size; + BYTE oldCode[5]; + CallJmpInstr* jumpInstruction; +}; +#pragma pack(pop) + +/* +* Если в начале функции для сплайсинг стоит инструкция jump (0xe9), то обычный сплайсинг не будет работать +* Необходимо пересчитать смещение джампа из оригинальной функции как при сохранении, так и при восстановлении оригинального кода +*/ +void recalculateRel32IfIsJump(void* dest, void* source) +{ + CallJmpInstr* mayBeJump = (CallJmpInstr*)dest; + if (mayBeJump->opcode == 0xe9) + { + mayBeJump->rel32 = (DWORD)source - (DWORD)dest + mayBeJump->rel32; + } +} + +BYTE saveOldFunction(void* proc, void* old) +{ + CopyMemory(old, proc, 5); + recalculateRel32IfIsJump(old, proc); + CallJmpInstr* instr = (CallJmpInstr*)((BYTE*)old + 5); + instr->opcode = 0xe9; + instr->rel32 = (DWORD)((BYTE*)proc - (BYTE*)old - 5); + return 5; +} + +void* splice(void* splicedFunctionAddress, void* hookFunction) +{ + DWORD oldProtect; + VirtualProtect((DWORD*)splicedFunctionAddress, 5, PAGE_EXECUTE_READWRITE, &oldProtect); + void* oldFunction = malloc(255); + *(DWORD*)oldFunction = (DWORD)splicedFunctionAddress; + *((BYTE*)oldFunction + 4) = saveOldFunction((DWORD*)((BYTE*)splicedFunctionAddress), (DWORD*)((BYTE*)oldFunction + 5)); + CallJmpInstr* instr = (CallJmpInstr*)((BYTE*)splicedFunctionAddress); + instr->opcode = 0xe9; + instr->rel32 = (DWORD)hookFunction - (DWORD)splicedFunctionAddress - 5; + VirtualProtect((DWORD*)splicedFunctionAddress, 5, oldProtect, &oldProtect); + + return (DWORD*)((BYTE*)oldFunction + 5); +} + +BOOL restore(void*& oldProc) +{ + if (oldProc != 0 && *((BYTE*)(*(DWORD*)((BYTE*)oldProc - 5))) == 0xe9) { + void* proc = (DWORD*)(*(DWORD*)((BYTE*)oldProc - 5)); + DWORD size = (BYTE)(*(DWORD*)((BYTE*)oldProc - 1)); + DWORD oldProtect; + VirtualProtect(proc, size, PAGE_EXECUTE_READWRITE, &oldProtect); + CopyMemory(proc, oldProc, size); + recalculateRel32IfIsJump(proc, oldProc); + VirtualProtect(proc, size, oldProtect, &oldProtect); + free((DWORD*)((BYTE*)oldProc - 5)); + oldProc = 0; + return TRUE; + } + return FALSE; +} diff --git a/L2BotDll/Common/apihook.h b/L2BotDll/Common/apihook.h new file mode 100644 index 0000000..d85ab89 --- /dev/null +++ b/L2BotDll/Common/apihook.h @@ -0,0 +1,5 @@ +#pragma once +#include + +void* splice(void* splicedFunctionAddress, void* hookFunction); +BOOL restore(void*& oldProc); \ No newline at end of file diff --git a/L2BotDll/Events/AbnormalEffectChangedEvent.h b/L2BotDll/Events/AbnormalEffectChangedEvent.h new file mode 100644 index 0000000..6ca64ff --- /dev/null +++ b/L2BotDll/Events/AbnormalEffectChangedEvent.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "Event.h" + +class AbnormalEffectChangedEvent : public Event +{ +public: + static constexpr const char* name = "abnormalEffectChanged"; + + const std::string GetName() const + { + return std::string(name); + } + + const std::vector GetSkillInfo() const + { + return m_SkillInfo; + } + + AbnormalEffectChangedEvent(const std::vector skillInfo) : + m_SkillInfo(skillInfo) + { + + } + + AbnormalEffectChangedEvent() = delete; + virtual ~AbnormalEffectChangedEvent() = default; + +private: + const std::vector m_SkillInfo; +}; \ No newline at end of file diff --git a/L2BotDll/Events/CreatureDiedEvent.h b/L2BotDll/Events/CreatureDiedEvent.h new file mode 100644 index 0000000..0553dfd --- /dev/null +++ b/L2BotDll/Events/CreatureDiedEvent.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Event.h" + +class CreatureDiedEvent : public Event +{ +public: + static constexpr const char* name = "creatureDied"; + + const std::string GetName() const + { + return std::string(name); + } + + const uint32_t GetCreatureId() const + { + return m_CreatureId; + } + + CreatureDiedEvent(uint32_t creatureId) : + m_CreatureId(creatureId) + { + + } + + CreatureDiedEvent() = delete; + virtual ~CreatureDiedEvent() = default; + +private: + const uint32_t m_CreatureId; +}; \ No newline at end of file diff --git a/L2BotDll/Events/Event.h b/L2BotDll/Events/Event.h new file mode 100644 index 0000000..1be1f3d --- /dev/null +++ b/L2BotDll/Events/Event.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class Event +{ +public: + virtual const std::string GetName() const = 0; + + Event() = default; + virtual ~Event() = default; +}; \ No newline at end of file diff --git a/L2BotDll/Events/EventDispatcher.h b/L2BotDll/Events/EventDispatcher.h new file mode 100644 index 0000000..272aa6e --- /dev/null +++ b/L2BotDll/Events/EventDispatcher.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include +#include "Event.h" + +class EventDispatcher +{ +public: + using Delegate = std::function; + + static EventDispatcher& GetInstance() { + static EventDispatcher instance; + return instance; + } + + void Dispatch(const Event& evt) + { + const auto& name = evt.GetName(); + + if (m_Handlers.find(name) == m_Handlers.end()) + { + return; + } + + for (const auto& handler : m_Handlers[name]) + { + handler(evt); + } + } + + void Subscribe(std::string eventName, Delegate handler) + { + m_Handlers[eventName].push_back(handler); + } + +private: + EventDispatcher() = default; + virtual ~EventDispatcher() = default; + EventDispatcher(const EventDispatcher&) = delete; + EventDispatcher& operator=(const EventDispatcher&) = delete; + +private: + std::unordered_map> m_Handlers; +}; \ No newline at end of file diff --git a/L2BotDll/Events/HeroCreatedEvent.h b/L2BotDll/Events/HeroCreatedEvent.h new file mode 100644 index 0000000..61d34a1 --- /dev/null +++ b/L2BotDll/Events/HeroCreatedEvent.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include "Event.h" + +class HeroCreatedEvent : public Event +{ +public: + static constexpr const char* name = "heroCreated"; + + const std::string GetName() const + { + return std::string(name); + } + + HeroCreatedEvent() = default; + virtual ~HeroCreatedEvent() = default; +}; \ No newline at end of file diff --git a/L2BotDll/Events/HeroDeletedEvent.h b/L2BotDll/Events/HeroDeletedEvent.h new file mode 100644 index 0000000..af932bb --- /dev/null +++ b/L2BotDll/Events/HeroDeletedEvent.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Event.h" + +class HeroDeletedEvent : public Event +{ +public: + static constexpr const char* name = "heroDeleted"; + + const std::string GetName() const + { + return std::string(name); + } + + HeroDeletedEvent() = default; + virtual ~HeroDeletedEvent() = default; +}; \ No newline at end of file diff --git a/L2BotDll/Events/SkillCancelledEvent.h b/L2BotDll/Events/SkillCancelledEvent.h new file mode 100644 index 0000000..33954f9 --- /dev/null +++ b/L2BotDll/Events/SkillCancelledEvent.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "Event.h" + +class SkillCancelledEvent : public Event +{ +public: + static constexpr const char* name = "skillCancelled"; + + const std::string GetName() const + { + return std::string(name); + } + + const uint32_t GetInitiatorId() const + { + return m_InitiatorId; + } + + SkillCancelledEvent(const uint32_t initiatorId) : + m_InitiatorId(initiatorId) + { + + } + + SkillCancelledEvent() = delete; + virtual ~SkillCancelledEvent() = default; + +private: + const uint32_t m_InitiatorId; +}; \ No newline at end of file diff --git a/L2BotDll/Events/SkillCreatedEvent.h b/L2BotDll/Events/SkillCreatedEvent.h new file mode 100644 index 0000000..339cf71 --- /dev/null +++ b/L2BotDll/Events/SkillCreatedEvent.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "Event.h" + +class SkillCreatedEvent : public Event +{ +public: + static constexpr const char* name = "skillCreated"; + + const std::string GetName() const + { + return std::string(name); + } + + const std::vector GetSkillInfo() const + { + return m_SkillInfo; + } + + SkillCreatedEvent(const std::vector skillInfo) : + m_SkillInfo(skillInfo) + { + + } + + SkillCreatedEvent() = delete; + virtual ~SkillCreatedEvent() = default; + +private: + const std::vector m_SkillInfo; +}; \ No newline at end of file diff --git a/L2BotDll/Events/SkillUsedEvent.h b/L2BotDll/Events/SkillUsedEvent.h new file mode 100644 index 0000000..7c6863a --- /dev/null +++ b/L2BotDll/Events/SkillUsedEvent.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "Event.h" + +class SkillUsedEvent : public Event +{ +public: + static constexpr const char* name = "skillUsed"; + + const std::string GetName() const + { + return std::string(name); + } + + const std::vector GetSkillInfo() const + { + return m_SkillInfo; + } + + SkillUsedEvent(const std::vector skillInfo) : + m_SkillInfo(skillInfo) + { + + } + + SkillUsedEvent() = delete; + virtual ~SkillUsedEvent() = default; + +private: + const std::vector m_SkillInfo; +}; \ No newline at end of file diff --git a/L2BotDll/Events/SpoiledEvent.h b/L2BotDll/Events/SpoiledEvent.h new file mode 100644 index 0000000..0399ecc --- /dev/null +++ b/L2BotDll/Events/SpoiledEvent.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include "Event.h" + +class SpoiledEvent : public Event +{ +public: + static constexpr const char* name = "spoiled"; + + const std::string GetName() const + { + return std::string(name); + } + + SpoiledEvent() = default; + virtual ~SpoiledEvent() = default; +}; \ No newline at end of file diff --git a/L2BotDll/L2BotDll.vcxproj b/L2BotDll/L2BotDll.vcxproj new file mode 100644 index 0000000..1137f46 --- /dev/null +++ b/L2BotDll/L2BotDll.vcxproj @@ -0,0 +1,238 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f077b130-780f-4c72-af56-e98b104a2a7d} + L2BotDll + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + false + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;L2BOTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + D:\Lineage 2 bot\Bot 2.0\L2Bot\InjectionLibrary;D:\Lineage 2 bot\Bot 2.0\L2Bot\L2BotCore;%(AdditionalIncludeDirectories) + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;L2BOTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + stdc17 + D:\Lineage 2 bot\Bot 2.0\L2Bot\InjectionLibrary;D:\Lineage 2 bot\Bot 2.0\L2Bot\L2BotCore;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;L2BOTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;L2BOTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + {54fbe631-3f9b-458c-9db2-43a868cdb806} + + + {504a5403-ba08-46df-aa8a-b79993b56bca} + + + + + + \ No newline at end of file diff --git a/L2BotDll/L2BotDll.vcxproj.filters b/L2BotDll/L2BotDll.vcxproj.filters new file mode 100644 index 0000000..61a7805 --- /dev/null +++ b/L2BotDll/L2BotDll.vcxproj.filters @@ -0,0 +1,186 @@ +п»ї + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/L2BotDll/Serializers/JsonSerializer.h b/L2BotDll/Serializers/JsonSerializer.h new file mode 100644 index 0000000..d2ee74c --- /dev/null +++ b/L2BotDll/Serializers/JsonSerializer.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Domain/Serializers/SerializerInterface.h" +#include "Domain/Serializers/Node.h" + +using namespace L2Bot::Domain; + +class JsonSerializer : public Serializers::SerializerInterface +{ +public: + const std::string Serialize(std::vector nodes, const bool isArray = false) const override + { + std::string result = isArray ? "[" : "{"; + + for (auto it = nodes.begin(); it != nodes.end(); ++it) + { + if (!isArray) + { + result += "\"" + it->name + "\":"; + } + if (it->isContainer) + { + result += Serialize(it->children, it->isArray); + } + else + { + result += "\"" + it->value + "\""; + } + if (std::next(it) != nodes.end()) + { + result += ","; + } + } + + result += isArray ? "]" : "}"; + + return result; + } +}; \ No newline at end of file diff --git a/L2BotDll/Services/WorldHandler.h b/L2BotDll/Services/WorldHandler.h new file mode 100644 index 0000000..e2bdd4e --- /dev/null +++ b/L2BotDll/Services/WorldHandler.h @@ -0,0 +1,168 @@ +#pragma once +#include +#include +#include +#include "Domain/Services/DropService.h" +#include "Domain/Services/HeroService.h" +#include "Domain/Services/NPCService.h" +#include "Domain/Services/PlayerService.h" +#include "Domain/Services/SkillService.h" +#include "Domain/Serializers/SerializableStateContainer.h" +#include "Domain/Serializers/SerializerInterface.h" +#include "Domain/Repositories/DropRepositoryInterface.h" +#include "Domain/Repositories/SkillRepositoryInterface.h" +#include "Domain/Transports/TransportInterface.h" + +using namespace L2Bot::Domain; + +class WorldHandler +{ +public: + WorldHandler( + Repositories::HeroRepositoryInterface& heroRepository, + Repositories::DropRepositoryInterface& dropRepository, + Repositories::NPCRepositoryInterface& npcRepository, + Repositories::PlayerRepositoryInterface& playerRepository, + Repositories::SkillRepositoryInterface& skillRepository, + const Serializers::SerializerInterface& serializer, + Transports::TransportInterface& transport + ) : + m_DropService(Services::DropService(dropRepository)), + m_HeroService(Services::HeroService(heroRepository)), + m_NPCService(Services::NPCService(npcRepository)), + m_PlayerService(Services::PlayerService(playerRepository)), + m_SkillService(Services::SkillService(skillRepository)), + m_Serializer(serializer), + m_Transport(transport) + { + + } + + void Start() + { + m_ConnectingThread = std::thread(&WorldHandler::Connect, this); + m_SendingThread = std::thread(&WorldHandler::Send, this); + m_ReceivingThread = std::thread(&WorldHandler::Receive, this); + } + + void Stop() + { + m_Stopped = true; + if (m_ConnectingThread.joinable()) + { + m_ConnectingThread.join(); + } + if (m_SendingThread.joinable()) + { + m_SendingThread.join(); + } + if (m_ReceivingThread.joinable()) + { + m_ReceivingThread.join(); + } + } + + virtual ~WorldHandler() + { + Stop(); + } + +private: + void Send() + { + while (!m_Stopped) + { + const auto& data = GetData(); + + if (m_Transport.IsConnected()) + { + for (const auto& item : data) + { + m_Transport.Send( + m_Serializer.Serialize({ item }) + ); + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } + + void Receive() + { + while (!m_Stopped) + { + if (m_Transport.IsConnected()) + { + const std::string& response = m_Transport.Receive(); + if (response == "invalidate") + { + Invalidate(); + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } + + void Connect() + { + while (!m_Stopped) + { + if (!m_Transport.IsConnected()) + { + m_Transport.Connect(); + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } + + const std::vector GetData() + { + std::vector items + { + new Serializers::SerializableStateContainer(m_HeroService.GetObjects(), "hero"), + new Serializers::SerializableStateContainer(m_DropService.GetObjects(), "drop"), + new Serializers::SerializableStateContainer(m_NPCService.GetObjects(), "npc"), + new Serializers::SerializableStateContainer(m_PlayerService.GetObjects(), "player"), + new Serializers::SerializableStateContainer(m_SkillService.GetObjects(), "skill") + }; + + std::vector result; + for (const auto& item : items) + { + for (const auto node : item->BuildSerializationNodes()) + { + result.push_back(node); + } + } + + for (const auto& item : items) + { + delete item; + } + + return result; + } + + void Invalidate() + { + m_HeroService.Invalidate(); + m_DropService.Invalidate(); + m_NPCService.Invalidate(); + m_PlayerService.Invalidate(); + m_SkillService.Invalidate(); + } + +private: + Services::DropService m_DropService; + Services::HeroService m_HeroService; + Services::NPCService m_NPCService; + Services::PlayerService m_PlayerService; + Services::SkillService m_SkillService; + const Serializers::SerializerInterface& m_Serializer; + Transports::TransportInterface& m_Transport; + bool m_Stopped = false; + std::thread m_ConnectingThread; + std::thread m_SendingThread; + std::thread m_ReceivingThread; +}; diff --git a/L2BotDll/Transports/DebugViewTransport.h b/L2BotDll/Transports/DebugViewTransport.h new file mode 100644 index 0000000..367c343 --- /dev/null +++ b/L2BotDll/Transports/DebugViewTransport.h @@ -0,0 +1,34 @@ +#pragma once +#include "Domain/Transports/TransportInterface.h" +#include +#include + +using namespace L2Bot::Domain; + +class DebugViewTransport : public Transports::TransportInterface +{ +public: + const bool Connect() override + { + return true; + } + + const bool IsConnected() const + { + return true; + } + + const void Send(std::string data) override + { + OutputDebugStringA(data.c_str()); + } + const std::string Receive() override + { + // delay imitation + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + return ""; + } + + DebugViewTransport() = default; + virtual ~DebugViewTransport() = default; +}; \ No newline at end of file diff --git a/L2BotDll/Transports/NamedPipe.h b/L2BotDll/Transports/NamedPipe.h new file mode 100644 index 0000000..486e941 --- /dev/null +++ b/L2BotDll/Transports/NamedPipe.h @@ -0,0 +1,193 @@ +#pragma once + +#include +#include +#include + +#define BUFFER_SIZE 16384 + +class NamedPipe +{ +public: + const bool Connect(const std::string& pipeName) + { + if (m_Pipe == NULL || m_PipeName != pipeName) + { + if (m_Pipe != NULL) { + DisconnectNamedPipe(m_Pipe); + CloseHandle(m_Pipe); + } + else + { + CreateOverlapped(m_ConntectingOverlapped); + CreateOverlapped(m_ReadingOverlapped); + CreateOverlapped(m_WritingOverlapped); + } + + m_Pipe = CreateNamedPipeA(("\\\\.\\pipe\\" + pipeName).c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + BUFFER_SIZE * sizeof(char), + BUFFER_SIZE * sizeof(char), + NMPWAIT_USE_DEFAULT_WAIT, + NULL + ); + + if (m_Pipe == INVALID_HANDLE_VALUE) + { + OutputDebugStringA(std::to_string(GetLastError()).c_str()); + return false; + } + } + else + { + DisconnectNamedPipe(m_Pipe); + } + + TryToConnect(); + + WaitForMultipleObjects(1, &m_ConntectingOverlapped.hEvent, false, INFINITE); + + DWORD ret; + m_Connected = GetOverlappedResult(m_Pipe, &m_ConntectingOverlapped, &ret, false); + + m_PipeName = pipeName; + + return m_Connected; + } + + void Send(const std::string& message) + { + if (!m_Connected) + { + return; + } + + DWORD written; + const auto result = WriteFile(m_Pipe, message.c_str(), message.size() + 1, &written, &m_WritingOverlapped); + + const auto lastError = GetLastError(); + if (!result) + { + if (lastError == ERROR_IO_PENDING) + { + WaitForMultipleObjects(1, &m_WritingOverlapped.hEvent, false, INFINITE); + DWORD ret; + const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_WritingOverlapped, &ret, false); + if (!overlappedResult) + { + m_Connected = false; + } + } + else + { + m_Connected = false; + } + } + } + + const std::string Receive() + { + if (!m_Connected) + { + return ""; + } + + DWORD dwRead; + char* buffer = new char[BUFFER_SIZE]; + const auto result = ReadFile(m_Pipe, buffer, BUFFER_SIZE * sizeof(char), &dwRead, &m_ReadingOverlapped); + + const auto lastError = GetLastError(); + if (!result) + { + if (lastError == ERROR_IO_PENDING) + { + WaitForMultipleObjects(1, &m_ReadingOverlapped.hEvent, false, INFINITE); + DWORD ret; + const auto overlappedResult = GetOverlappedResult(m_Pipe, &m_ReadingOverlapped, &ret, false); + if (!overlappedResult) + { + delete[] buffer; + m_Connected = false; + return ""; + } + } + else + { + delete[] buffer; + m_Connected = false; + return ""; + } + } + + std::string message = std::string(buffer); + delete[] buffer; + + return message; + } + + const bool IsConnected() const + { + return m_Connected; + } + + virtual ~NamedPipe() + { + if (m_Pipe != NULL) + { + CloseHandle(m_Pipe); + } + } + NamedPipe() = default; + +private: + void TryToConnect() + { + const bool connected = ConnectNamedPipe(m_Pipe, &m_ConntectingOverlapped) == 0; + if (!connected) + { + OutputDebugStringA(std::to_string(GetLastError()).c_str()); + } + + switch (GetLastError()) + { + // The overlapped connection in progress. + case ERROR_IO_PENDING: + break; + + // Client is already connected, so signal an event. + + case ERROR_PIPE_CONNECTED: + if (SetEvent(m_ConntectingOverlapped.hEvent)) + break; + + // If an error occurs during the connect operation... + default: + OutputDebugStringA(std::to_string(GetLastError()).c_str()); + } + } + + void CreateOverlapped(OVERLAPPED& overlapped) + { + if (overlapped.hEvent == NULL) + { + overlapped.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (overlapped.hEvent == NULL) + { + OutputDebugStringA(std::to_string(GetLastError()).c_str()); + return; + } + overlapped.Offset = 0; + overlapped.OffsetHigh = 0; + } + } + +private: + std::string m_PipeName = ""; + HANDLE m_Pipe = NULL; + bool m_Connected = false; + OVERLAPPED m_ConntectingOverlapped = OVERLAPPED(); + OVERLAPPED m_ReadingOverlapped = OVERLAPPED(); + OVERLAPPED m_WritingOverlapped = OVERLAPPED(); +}; \ No newline at end of file diff --git a/L2BotDll/Transports/NamedPipeTransport.h b/L2BotDll/Transports/NamedPipeTransport.h new file mode 100644 index 0000000..3f25985 --- /dev/null +++ b/L2BotDll/Transports/NamedPipeTransport.h @@ -0,0 +1,76 @@ +#pragma once +#include "Domain/Transports/TransportInterface.h" +#include +#include "NamedPipe.h" +#include "../Common/Common.h" + +using namespace L2Bot::Domain; + +class NamedPipeTransport : public Transports::TransportInterface +{ +public: + const bool Connect() override + { + OutputDebugStringA(m_PipeName.c_str()); + if (!m_ConnectionPipe.Connect(m_PipeName)) + { + return false; + } + + OutputDebugStringA("Client connected to connection pipe"); + + const std::string mainPipeName = GenerateUUID(); + + m_ConnectionPipe.Send("\\\\.\\pipe\\" + mainPipeName); + + OutputDebugStringA("Name of main pipe sended"); + + if (!m_Pipe.Connect(mainPipeName)) + { + OutputDebugStringA(std::to_string(GetLastError()).c_str()); + return false; + } + OutputDebugStringA("Client connected to main pipe"); + + m_Pipe.Send("Hello!"); + + return true; + } + + const void Send(std::string data) override + { + if (!m_Pipe.IsConnected()) + { + return; + } + + m_Pipe.Send(data); + } + const std::string Receive() override + { + if (!m_Pipe.IsConnected()) + { + return ""; + } + + return m_Pipe.Receive(); + } + + const bool IsConnected() const override + { + return m_Pipe.IsConnected(); + } + + NamedPipeTransport(const std::string& pipeName) : + m_PipeName(pipeName) + { + } + + NamedPipeTransport() = delete; + virtual ~NamedPipeTransport() = default; + +private: + NamedPipe m_ConnectionPipe; + NamedPipe m_Pipe; + std::string m_PipeName = ""; +}; \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/FNameInterface.h b/L2BotDll/Versions/GameStructs/FNameInterface.h new file mode 100644 index 0000000..9c12a9e --- /dev/null +++ b/L2BotDll/Versions/GameStructs/FNameInterface.h @@ -0,0 +1,12 @@ +#pragma once + +#include "GameStructs.h" + +class FNameInterface +{ +public: + FNameInterface() = default; + virtual ~FNameInterface() = default; + + virtual void Init(HMODULE hModule) = 0; +}; \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/FindObjectsTrait.h b/L2BotDll/Versions/GameStructs/FindObjectsTrait.h new file mode 100644 index 0000000..f43d53b --- /dev/null +++ b/L2BotDll/Versions/GameStructs/FindObjectsTrait.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include "GameStructs.h" + +class FindObjectsTrait +{ +public: + template + std::map GetAllObjects(float_t radius, std::function getNextObject) const + { + std::map result; + + auto object = getNextObject(radius, -1); + + while (object) + { + if (result.find(object->objectId) != result.end()) { + break; + } + else { + result.emplace(object->objectId, object); + } + object = getNextObject(radius, object->objectId); + } + + return result; + } +}; \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/GameEngineInterface.h b/L2BotDll/Versions/GameStructs/GameEngineInterface.h new file mode 100644 index 0000000..407a54c --- /dev/null +++ b/L2BotDll/Versions/GameStructs/GameEngineInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class GameEngineInterface +{ +public: + GameEngineInterface() = default; + virtual ~GameEngineInterface() = default; + + virtual void Init(HMODULE hModule) = 0; + virtual void Restore() = 0; +}; \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/GameStructs.h b/L2BotDll/Versions/GameStructs/GameStructs.h new file mode 100644 index 0000000..eec6708 --- /dev/null +++ b/L2BotDll/Versions/GameStructs/GameStructs.h @@ -0,0 +1,184 @@ +#pragma once + +#include "pch.h" + +namespace L2 +{ + enum class UserType : int32_t + { + NPC = 1, + USER = 0 + }; + + enum class Race : int32_t + { + DARK_ELF = 2, + DWARF = 4, + ELF = 1, + HUMAN = 0, + ORC = 3 + }; + + enum class Gender : int32_t + { + FEMALE = 1, + MALE = 0 + }; + + enum class ItemSlot : int32_t + { + BABYPET = 4194304, + BACK = 8192, + CHEST = 1024, + DHAIR = 524288, + FACE = 262144, + FEET = 4096, + FULL_ARMOR = 32768, + GLOVES = 512, + HAIR = 65536, + HATCHLING = 1048576, + HEAD = 64, + L_EAR = 4, + L_FINGER = 32, + L_HAND = 256, + LEGS = 2048, + LR_HAND = 16384, + NECK = 8, + NONE = 0, + R_EAR = 2, + R_FINGER = 16, + LoR_EAR = L_EAR | R_EAR, + LoR_FINGER = L_FINGER | R_FINGER, + R_HAND = 128, + STRIDER = 2097152, + UNDERWEAR = 1, + WOLF = 131072 + }; + + enum class ItemDataType : int32_t + { + ARMOR = 1, + ETC = 2, + WEAPON = 0 + }; + + enum class ItemType2 : int16_t + { + ACCESSORY = 2, + MONEY = 4, + OTHER = 5, + PET_BABY = 9, + PET_HATCHLING = 7, + PET_STRIDER = 8, + PET_WOLF = 6, + QUEST = 3, + SHIELD_ARMOR = 1, + WEAPON = 0 + }; + + enum class CrystalType : int32_t + { + A = 4, + B = 3, + C = 2, + D = 1, + NG = 0, + S = 5, + NONE = -1 + }; + + enum class WeaponType : int32_t + { + BLUNT = 2, + BOW = 6, + DAGGER = 3, + DUALSWORD = 8, + ETC = 7, + FISHING_ROD = 10, + FIST = 5, + PET = 9, + POLE = 4, + SHIELD = 0, + SWORD = 1 + }; + + enum class ArmorType : int32_t + { + NONE = 0, + HEAVY = 2, + LIGHT = 1, + ROBE = 3 + }; + + class UserWear + { + public: + char pad_0000[4]; //0x0000 + int32_t leftEarring; //0x0004 + int32_t rightEarring; //0x0008 + int32_t neclace; //0x000C + int32_t leftRing; //0x0010 + int32_t rightRing; //0x0014 + int32_t helmet; //0x0018 + int32_t weapon; //0x001C + int32_t shield; //0x0020 + int32_t gloves; //0x0024 + int32_t breastplate; //0x0028 + int32_t gaiters; //0x002C + int32_t boots; //0x0030 + char pad_0034[64]; //0x0034 + }; //Size: 0x0074 + + class FColor + { + public: + uint8_t r; //0x0000 + uint8_t g; //0x0001 + uint8_t b; //0x0002 + uint8_t a; //0x0003 + }; //Size: 0x0004 + + class FVector + { + public: + float x = 0; //0x0000 + float y = 0; //0x0004 + float z = 0; //0x0008 + }; //Size: 0x000C + + class FRotator + { + public: + int32_t Pitch; //0x0000 + int32_t Yaw; //0x0004 + int32_t Roll; //0x0008 + }; //Size: 0x000C + +#pragma pack(push, 1) + struct NetworkPacket + { + unsigned char id, _padding1, exid, _padding2; + unsigned short size, _padding3; + unsigned char* data; + }; +#pragma pack(pop) + + struct SystemMessagePacket : NetworkPacket + { + enum class Type + { + ALREADY_SPOILED = 357, + SPOIL_SUCCESS = 612, + }; + + const uint32_t GetMessageId() const + { + return ((uint32_t*)data)[0]; + } + }; + + enum class NetworkPacketId + { + SYSTEM_MESSAGE = 0x64 + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/L2GameDataInterface.h b/L2BotDll/Versions/GameStructs/L2GameDataInterface.h new file mode 100644 index 0000000..e174c6c --- /dev/null +++ b/L2BotDll/Versions/GameStructs/L2GameDataInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include "GameStructs.h" + +class L2GameDataInterface +{ +public: + L2GameDataInterface() = default; + virtual ~L2GameDataInterface() = default; + + virtual void Init(HMODULE hModule) = 0; + virtual void Restore() = 0; +}; \ No newline at end of file diff --git a/L2BotDll/Versions/GameStructs/NetworkHandlerInterface.h b/L2BotDll/Versions/GameStructs/NetworkHandlerInterface.h new file mode 100644 index 0000000..8077c7b --- /dev/null +++ b/L2BotDll/Versions/GameStructs/NetworkHandlerInterface.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +class NetworkHandlerInterface +{ +public: + NetworkHandlerInterface() = default; + virtual ~NetworkHandlerInterface() = default; + + virtual void Init(HMODULE hModule) = 0; + virtual void Restore() = 0; +}; \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/AbstractFactory.h b/L2BotDll/Versions/Interlude/AbstractFactory.h new file mode 100644 index 0000000..1a79f29 --- /dev/null +++ b/L2BotDll/Versions/Interlude/AbstractFactory.h @@ -0,0 +1,93 @@ +#pragma once + +#include "../VersionAbstractFactory.h" +#include "Factories/HeroFactory.h" +#include "Factories/DropFactory.h" +#include "Factories/NPCFactory.h" +#include "Factories/PlayerFactory.h" +#include "Factories/SkillFactory.h" +#include "Repositories/HeroRepository.h" +#include "Repositories/DropRepository.h" +#include "Repositories/NPCRepository.h" +#include "Repositories/PlayerRepository.h" +#include "Repositories/SkillRepository.h" +#include "GameStructs/NetworkHandlerWrapper.h" +#include "GameStructs/GameEngineWrapper.h" +#include "GameStructs/L2GameDataWrapper.h" +#include "GameStructs/FName.h" + +namespace Interlude +{ + class AbstractFactory : public VersionAbstractFactory + { + public: + AbstractFactory(const uint16_t radius) : + m_Radius(radius) + { + + } + AbstractFactory() = delete; + virtual ~AbstractFactory() = default; + + HeroRepository& GetHeroRepository() const override + { + static auto factory = HeroFactory(); + static auto result = HeroRepository( + GetNetworkHandler(), + factory + ); + return result; + } + DropRepository& GetDropRepository() const override + { + static auto factory = DropFactory(GetL2GameData(), GetFName()); + static auto result = DropRepository( + GetNetworkHandler(), + factory, + m_Radius + ); + return result; + } + NPCRepository& GetNPCRepository() const override + { + static auto factory = NPCFactory(); + static auto result = NPCRepository(GetNetworkHandler(), factory, m_Radius); + return result; + } + PlayerRepository& GetPlayerRepository() const override + { + static auto factory = PlayerFactory(); + static auto result = PlayerRepository(GetNetworkHandler(), factory, m_Radius); + return result; + } + SkillRepository& GetSkillRepository() const override + { + static auto factory = SkillFactory(GetL2GameData(), GetFName()); + static auto result = SkillRepository(GetNetworkHandler(), factory); + return result; + } + NetworkHandlerWrapper& GetNetworkHandler() const override + { + static NetworkHandlerWrapper result; + return result; + } + GameEngineWrapper& GetGameEngine() const override + { + static GameEngineWrapper result; + return result; + } + L2GameDataWrapper& GetL2GameData() const override + { + static L2GameDataWrapper result; + return result; + } + FName& GetFName() const override + { + static FName result; + return result; + } + + private: + const uint16_t m_Radius; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Factories/DropFactory.h b/L2BotDll/Versions/Interlude/Factories/DropFactory.h new file mode 100644 index 0000000..4ec8336 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Factories/DropFactory.h @@ -0,0 +1,50 @@ +#pragma once + +#include "../GameStructs/L2GameDataWrapper.h" +#include "../GameStructs/FName.h" +#include "../../../Common/Common.h" + +namespace Interlude +{ + class DropFactory + { + public: + DropFactory(const L2GameDataWrapper& l2GameData, const FName& fName) : + m_L2GameData(l2GameData), + m_FName(fName) + { + } + + DropFactory() = delete; + virtual ~DropFactory() = default; + + const DTO::Drop Create(const Item* item) const + { + const auto itemData = m_L2GameData.GetItemData(item->itemId); + const auto nameEntry = itemData ? m_FName.GetEntry(itemData->nameIndex) : nullptr; + const auto iconEntry = itemData ? m_FName.GetEntry(itemData->iconNameIndex) : nullptr; + + return DTO::Drop{ + item->objectId, + ValueObjects::Transform( + ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), + ValueObjects::Vector3( + static_cast(item->pawn->Rotation.Pitch), + static_cast(item->pawn->Rotation.Yaw), + static_cast(item->pawn->Rotation.Roll) + ), + ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z), + ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z) + ), + item->itemId, + item->amount, + nameEntry ? ConvertFromWideChar(nameEntry->value) : "", + iconEntry ? ConvertFromWideChar(iconEntry->value) : "" + }; + } + + private: + const L2GameDataWrapper& m_L2GameData; + const FName& m_FName; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Factories/HeroFactory.h b/L2BotDll/Versions/Interlude/Factories/HeroFactory.h new file mode 100644 index 0000000..6d34c5d --- /dev/null +++ b/L2BotDll/Versions/Interlude/Factories/HeroFactory.h @@ -0,0 +1,86 @@ +#pragma once + +#include "../GameStructs/NetworkHandlerWrapper.h" +#include "../../../Common/Common.h" + +namespace Interlude +{ + class HeroFactory + { + public: + HeroFactory() = default; + virtual ~HeroFactory() = default; + + const DTO::Hero Create(const User* item) const + { + const auto playerController = item->pawn ? item->pawn->lineagePlayerController : nullptr; + + return DTO::Hero{ + item->objectId, + ValueObjects::Transform( + ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), + ValueObjects::Vector3( + static_cast(item->pawn->Rotation.Pitch), + static_cast(item->pawn->Rotation.Yaw), + static_cast(item->pawn->Rotation.Roll) + ), + ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z), + ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z) + ), + ValueObjects::FullName( + ConvertFromWideChar(item->nickname), + ConvertFromWideChar(item->title) + ), + ValueObjects::VitalStats( + item->maxHp, item->hp, + item->maxMp, item->mp, + item->maxCp, item->cp + ), + ValueObjects::Phenotype( + (Enums::RaceEnum)item->raceId, + item->gender == L2::Gender::MALE, + (Enums::ClassEnum)item->classId, + (Enums::ClassEnum)item->activeClassId + ), + ValueObjects::ExperienceInfo( + item->lvl, + item->exp, + item->sp + ), + ValueObjects::PermanentStats( + item->str, + item->dex, + item->con, + item->int_, + item->men, + item->wit + ), + ValueObjects::VariableStats( + item->accuracy, + item->critRate, + item->pAttack, + item->attackSpeed, + item->pDefense, + item->evasion, + item->mAttack, + item->mDefense, + item->castingSpeed + ), + ValueObjects::Reputation( + item->karma, + item->pkKills, + item->pvpKills, + static_cast(item->recRemaining), + static_cast(item->evalScore) + ), + ValueObjects::InventoryInfo( + item->maxWeight, + item->weight, + item->invSlotCount + ), + playerController ? playerController->targetObjectId : 0, + playerController ? playerController->isStanding == 1 : true + }; + } + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Factories/NPCFactory.h b/L2BotDll/Versions/Interlude/Factories/NPCFactory.h new file mode 100644 index 0000000..adb2ea4 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Factories/NPCFactory.h @@ -0,0 +1,43 @@ +#pragma once + + +#include "../../../Common/Common.h" + +namespace Interlude +{ + class NPCFactory + { + public: + NPCFactory() = default; + virtual ~NPCFactory() = default; + + const DTO::NPC Create(const User* item, const Enums::SpoilStateEnum spoiledState) const + { + return DTO::NPC{ + item->objectId, + ValueObjects::Transform( + ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), + ValueObjects::Vector3( + static_cast(item->pawn->Rotation.Pitch), + static_cast(item->pawn->Rotation.Yaw), + static_cast(item->pawn->Rotation.Roll) + ), + ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z), + ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z) + ), + item->isMob != 0, + item->npcId, + spoiledState, + ValueObjects::FullName( + ConvertFromWideChar(item->nickname), + ConvertFromWideChar(item->title) + ), + ValueObjects::VitalStats( + item->maxHp, item->hp, + item->maxMp, item->mp, + item->maxCp, item->cp + ) + }; + } + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h b/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h new file mode 100644 index 0000000..21a3a18 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Factories/PlayerFactory.h @@ -0,0 +1,40 @@ +#pragma once + +#include "../../../Common/Common.h" + +namespace Interlude +{ + class PlayerFactory + { + public: + PlayerFactory() = default; + virtual ~PlayerFactory() = default; + + const DTO::Player Create(const User* item) const + { + return DTO::Player{ + item->objectId, + ValueObjects::Transform( + ValueObjects::Vector3(item->pawn->Location.x, item->pawn->Location.y, item->pawn->Location.z), + ValueObjects::Vector3( + static_cast(item->pawn->Rotation.Pitch), + static_cast(item->pawn->Rotation.Yaw), + static_cast(item->pawn->Rotation.Roll) + ), + ValueObjects::Vector3(item->pawn->Velocity.x, item->pawn->Velocity.y, item->pawn->Velocity.z), + ValueObjects::Vector3(item->pawn->Acceleration.x, item->pawn->Acceleration.y, item->pawn->Acceleration.z) + ), + ValueObjects::FullName( + ConvertFromWideChar(item->nickname), + ConvertFromWideChar(item->title) + ), + ValueObjects::Phenotype( + (Enums::RaceEnum)item->raceId, + item->gender == L2::Gender::MALE, + (Enums::ClassEnum)item->classId, + (Enums::ClassEnum)item->activeClassId + ), + }; + } + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Factories/SkillFactory.h b/L2BotDll/Versions/Interlude/Factories/SkillFactory.h new file mode 100644 index 0000000..22dc540 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Factories/SkillFactory.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include "../GameStructs/L2GameDataWrapper.h" +#include "../GameStructs/FName.h" +#include "../../../Common/Common.h" + +namespace Interlude +{ + class SkillFactory + { + public: + SkillFactory(const L2GameDataWrapper& l2GameData, const FName& fName) : + m_L2GameData(l2GameData), + m_FName(fName) + { + } + + SkillFactory() = delete; + virtual ~SkillFactory() = default; + + const DTO::Skill Create(const DTO::Skill source, const uint32_t skillId, const uint32_t level, const uint32_t isActive) const + { + const auto data = m_L2GameData.GetMSData(skillId, level); + + const auto cost = data ? data->mpCost : 0; + const auto range = data ? data->range : 0; + const auto name = data ? data->name : L""; + const auto description = data ? data->description : L""; + const auto iconEntry = data ? m_FName.GetEntry(data->iconNameIndex) : nullptr; + + return DTO::Skill + { + skillId, + static_cast(level), + isActive != 1, + static_cast(cost), + static_cast(range), + ConvertFromWideChar(name), + ConvertFromWideChar(description), + iconEntry ? ConvertFromWideChar(iconEntry->value) : "", + source.isToggled, + source.isCasting, + source.isReloading + }; + } + + const DTO::Skill UpdateToggle(const DTO::Skill source, const bool isToggled) const + { + return DTO::Skill + { + source.skillId, + source.level, + source.isActive, + source.cost, + source.range, + source.name, + source.description, + source.iconName, + isToggled, + source.isCasting, + source.isReloading + }; + } + + const DTO::Skill UpdateCastingState(const DTO::Skill source, const bool isCasting) const + { + return DTO::Skill + { + source.skillId, + source.level, + source.isActive, + source.cost, + source.range, + source.name, + source.description, + source.iconName, + source.isToggled, + isCasting, + source.isReloading + }; + } + + const DTO::Skill UpdateReloadingState(const DTO::Skill source, const bool isReloading) const + { + return DTO::Skill + { + source.skillId, + source.level, + source.isActive, + source.cost, + source.range, + source.name, + source.description, + source.iconName, + source.isToggled, + source.isCasting, + isReloading + }; + } + + private: + const L2GameDataWrapper& m_L2GameData; + const FName& m_FName; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/GameStructs/FName.cpp b/L2BotDll/Versions/Interlude/GameStructs/FName.cpp new file mode 100644 index 0000000..143ddc2 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/FName.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "FName.h" + +namespace Interlude +{ + FNameEntry* (__cdecl* FName::__GetEntry)(int) = 0; + + //todo exception(?) + FNameEntry* FName::GetEntry(int index) const + { + if (__GetEntry) { + return(*__GetEntry)(index); + } + return 0; + } + + void FName::Init(HMODULE hModule) + { + (FARPROC&)__GetEntry = GetProcAddress(hModule, "?GetEntry@FName@@SAPAUFNameEntry@@H@Z"); + } +} diff --git a/L2BotDll/Versions/Interlude/GameStructs/FName.h b/L2BotDll/Versions/Interlude/GameStructs/FName.h new file mode 100644 index 0000000..4b3ba7f --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/FName.h @@ -0,0 +1,21 @@ +#pragma once + +#include "pch.h" +#include "GameStructs.h" +#include "../../GameStructs/FNameInterface.h" + +namespace Interlude +{ + class FName : public FNameInterface + { + public: + FName() = default; + virtual ~FName() = default; + + FNameEntry* GetEntry(int index) const; + void Init(HMODULE hModule) override; + private: + static FNameEntry* (__cdecl* __GetEntry)(int); + }; + +} diff --git a/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.cpp b/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.cpp new file mode 100644 index 0000000..0b40151 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.cpp @@ -0,0 +1,88 @@ +#include "pch.h" +#include "../../../Common/apihook.h" +#include "GameEngineWrapper.h" +#include "ProcessManipulation.h" +#include "../../../Events/SkillCreatedEvent.h" +#include "../../../Events/SkillUsedEvent.h" +#include "../../../Events/SkillCancelledEvent.h" +#include "../../../Events/AbnormalEffectChangedEvent.h" +#include "../../../Events/EventDispatcher.h" + +namespace Interlude +{ + void* GameEngineWrapper::originalInitAddress = 0; + GameEngineWrapper::GameEngine* GameEngineWrapper::_target = 0; + + void(__thiscall* GameEngineWrapper::__Init)(GameEngine*, float_t) = 0; + void(__thiscall* GameEngineWrapper::__OnSkillListPacket)(GameEngine*, L2ParamStack&) = 0; + int(__thiscall* GameEngineWrapper::__OnReceiveMagicSkillUse)(GameEngine*, User*, User*, L2ParamStack&) = 0; + void(__thiscall* GameEngineWrapper::__OnReceiveMagicSkillCanceled)(GameEngine*, User*) = 0; + void(__thiscall* GameEngineWrapper::__AddAbnormalStatus)(GameEngine*, L2ParamStack&) = 0; + + + void GameEngineWrapper::Init(HMODULE hModule) + { + void* initAddress = GetProcAddress(hModule, "?Tick@UGameEngine@@UAEXM@Z"); + originalInitAddress = splice(initAddress, __Init_hook); + (FARPROC&)__Init = (FARPROC)initAddress; + + (FARPROC&)__OnSkillListPacket = (FARPROC)splice( + GetProcAddress(hModule, "?OnSkillListPacket@UGameEngine@@UAEXAAVL2ParamStack@@@Z"), __OnSkillListPacket_hook + ); + (FARPROC&)__OnReceiveMagicSkillUse = (FARPROC)splice( + GetProcAddress(hModule, "?OnReceiveMagicSkillUse@UGameEngine@@UAEXPAUUser@@0AAVL2ParamStack@@@Z"), __OnReceiveMagicSkillUse_hook + ); + (FARPROC&)__OnReceiveMagicSkillCanceled = (FARPROC)splice( + GetProcAddress(hModule, "?OnReceiveMagicSkillCanceled@UGameEngine@@UAEXPAUUser@@@Z"), __OnReceiveMagicSkillCanceled_hook + ); + (FARPROC&)__AddAbnormalStatus = (FARPROC)splice( + GetProcAddress(hModule, "?AddAbnormalStatus@UGameEngine@@UAEXAAVL2ParamStack@@@Z"), __AddAbnormalStatus_hook + ); + } + + void GameEngineWrapper::Restore() + { + restore(originalInitAddress); + restore((void*&)__OnSkillListPacket); + restore((void*&)__OnReceiveMagicSkillUse); + restore((void*&)__OnReceiveMagicSkillCanceled); + restore((void*&)__AddAbnormalStatus); + } + + void __fastcall GameEngineWrapper::__Init_hook(GameEngine* This, uint32_t /*edx*/, float_t unk) + { + if (_target == 0) { + _target = This; + + InjectLibrary::StopCurrentProcess(); + restore(originalInitAddress); + InjectLibrary::StartCurrentProcess(); + + (*__Init)(This, unk); + } + } + + void __fastcall GameEngineWrapper::__OnSkillListPacket_hook(GameEngine* This, uint32_t, L2ParamStack& stack) + { + EventDispatcher::GetInstance().Dispatch(SkillCreatedEvent{stack.GetBufferAsVector()}); + (*__OnSkillListPacket)(This, stack); + } + + int __fastcall GameEngineWrapper::__OnReceiveMagicSkillUse_hook(GameEngine* This, uint32_t, User* u1, User* u2, L2ParamStack& stack) + { + EventDispatcher::GetInstance().Dispatch(SkillUsedEvent{ stack.GetBufferAsVector() }); + return (*__OnReceiveMagicSkillUse)(This, u1, u2, stack); + } + + void __fastcall GameEngineWrapper::__OnReceiveMagicSkillCanceled_hook(GameEngine* This, uint32_t, User* user) + { + EventDispatcher::GetInstance().Dispatch(SkillCancelledEvent{ user->objectId }); + (*__OnReceiveMagicSkillCanceled)(This, user); + } + + void __fastcall GameEngineWrapper::__AddAbnormalStatus_hook(GameEngine* This, uint32_t, L2ParamStack& stack) + { + EventDispatcher::GetInstance().Dispatch(AbnormalEffectChangedEvent{ stack.GetBufferAsVector(3) }); + (*__AddAbnormalStatus)(This, stack); + } +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.h b/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.h new file mode 100644 index 0000000..8572159 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/GameEngineWrapper.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#include "../../GameStructs/GameEngineInterface.h" +#include "GameStructs.h" +#include "L2ParamStack.h" + +namespace Interlude +{ + class GameEngineWrapper : public GameEngineInterface + { + public: + class GameEngine {}; + GameEngineWrapper() = default; + virtual ~GameEngineWrapper() = default; + + void Init(HMODULE hModule) override; + void Restore() override; + + private: + static void(__thiscall* __Init)(GameEngine*, float_t); + static void(__thiscall* __OnSkillListPacket)(GameEngine*, L2ParamStack& stack); + static int(__thiscall* __OnReceiveMagicSkillUse)(GameEngine*, User*, User*, L2ParamStack&); + static void(__thiscall* __OnReceiveMagicSkillCanceled)(GameEngine*, User*); + static void(__thiscall* __AddAbnormalStatus)(GameEngine*, L2ParamStack&); + + static void __fastcall __Init_hook(GameEngine* This, uint32_t /*edx*/, float_t unk); + static void __fastcall __OnSkillListPacket_hook(GameEngine* This, uint32_t /*edx*/, L2ParamStack& stack); + static int __fastcall __OnReceiveMagicSkillUse_hook(GameEngine* This, uint32_t /*edx*/, User* u1, User* u2, L2ParamStack& stack); + static void __fastcall __OnReceiveMagicSkillCanceled_hook(GameEngine* This, uint32_t /*edx*/, User* user); + static void __fastcall __AddAbnormalStatus_hook(GameEngine* This, uint32_t /*edx*/, L2ParamStack& stack); + private: + static void* originalInitAddress; + static GameEngine* _target; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/GameStructs/GameStructs.h b/L2BotDll/Versions/Interlude/GameStructs/GameStructs.h new file mode 100644 index 0000000..e74f17a --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/GameStructs.h @@ -0,0 +1,258 @@ +#pragma once + +#include "../../GameStructs/GameStructs.h" + +namespace Interlude +{ + class User + { + public: + char pad_0000[8]; //0x0000 + L2::UserType userType; //0x0008 + char pad_000C[4]; //0x000C + int32_t isMob; //0x0010 + uint32_t npcId; //0x0014 + uint32_t objectId; //0x0018 + wchar_t nickname[24]; //0x001C + L2::Race raceId; //0x004C + L2::Gender gender; //0x0050 + int32_t classId; //0x0054 + uint32_t lvl; //0x0058 + int32_t exp; //0x005C + char pad_0060[4]; //0x0060 + int32_t str; //0x0064 + int32_t dex; //0x0068 + int32_t con; //0x006C + int32_t int_; //0x0070 + int32_t wit; //0x0074 + int32_t men; //0x0078 + int32_t maxHp; //0x007C + int32_t hp; //0x0080 + int32_t maxMp; //0x0084 + int32_t mp; //0x0088 + int32_t maxWeight; //0x008C + char pad_0090[8]; //0x0090 + class L2::UserWear wear; //0x0098 + char pad_010C[132]; //0x010C + class L2::FColor titleColor; //0x0190 + int32_t pad_0194; //0x0194 pvp state: 0 - normal, 1 - pvp, 2 - blinking + int32_t karma; //0x0198 + char pad_019C[104]; //0x019C + class APawn* pawn; //0x0204 + char pad_0208[12]; //0x0208 + int32_t weight; //0x0214 + int32_t sp; //0x0218 + int32_t accuracy; //0x021C + int32_t critRate; //0x0220 + int32_t pAttack; //0x0224 + int32_t attackSpeed; //0x0228 + int32_t pDefense; //0x022C + int32_t evasion; //0x0230 + int32_t mAttack; //0x0234 + int32_t mDefense; //0x0238 + int32_t castingSpeed; //0x023C + char pad_0240[20]; //0x0240 + wchar_t title[16]; //0x0254 + char pad_0274[32]; //0x0274 + int32_t pad_0294; //0x0294 + char pad_0298[16]; //0x0298 + int32_t hasDwarvenCraft; //0x02A8 + int32_t attackSpeed2; //0x02AC + char pad_02B0[4]; //0x02B0 + int32_t pkKills; //0x02B4 + int32_t pvpKills; //0x02B8 + char pad_02BC[4]; //0x02BC + int32_t activeClassId; //0x02C0 + int32_t maxCp; //0x02C4 + int32_t cp; //0x02C8 + char pad_02CC[20]; //0x02CC + int16_t recRemaining; //0x02E0 + int16_t evalScore; //0x02E2 + int32_t invSlotCount; //0x02E4 + char pad_02E8[32]; //0x02E8 + class L2::FColor nicknameColor; //0x0308 + char pad_030C[164]; //0x030C + }; //Size: 0x03B0 + + class APawn + { + public: + char pad_0000[8]; //0x0000 + void* uStaticMeshInstance; //0x0008 + void* fStateFrame; //0x000C + char pad_0010[8]; //0x0010 + void* uPackage; //0x0018 + char pad_001C[32]; //0x001C + class ALineagePlayerController* lineagePlayerController; //0x003C + void* terrainInfo; //0x0040 + char pad_0044[28]; //0x0044 + int32_t ownerObjectId; //0x0060 + char pad_0064[344]; //0x0064 + class L2::FVector Location; //0x01BC + class L2::FRotator Rotation; //0x01C8 + class L2::FVector Velocity; //0x01D4 + class L2::FVector Acceleration; //0x01E0 + char pad_01EC[336]; //0x01EC + class L2::FVector Location2; //0x033C + char pad_0348[1244]; //0x0348 + }; //Size: 0x0824 + + class ALineagePlayerController + { + public: + char pad_0000[444]; //0x0000 + class L2::FVector cameraPosition; //0x01BC + class L2::FRotator cameraRotation; //0x01C8 + char pad_01D4[544]; //0x01D4 + class L2::FVector moveLocation; //0x03F4 + char pad_0400[16]; //0x0400 + int32_t isIdle; //0x0410 + uint32_t targetObjectId; //0x0414 + char pad_0418[28]; //0x0418 + int8_t isRunning; //0x0434 + int8_t isStanding; //0x0435 + char pad_0436[26]; //0x0436 + }; //Size: 0x0450 + + struct Item + { + uint32_t objectId; + unsigned int itemId; + unsigned int isStackable; // ?? + unsigned int amount; + APawn* pawn; + }; + + class ItemInfo + { + public: + L2::ItemType2 type2; //0x0000 + char pad_0002[2]; //0x0002 + uint32_t objectId; //0x0004 + uint32_t itemId; //0x0008 + uint32_t amount; //0x000C + char pad_0010[8]; //0x0010 + L2::ItemSlot itemSlot; //0x0018 + uint16_t customType1; //0x001C + uint16_t isEquipped; //0x001E + uint16_t enchantLevel; //0x0020 + char pad_0022[2]; //0x0022 + uint16_t customType2; //0x0024 + char pad_0026[10]; //0x0026 + uint32_t augmentation1; //0x0030 + uint32_t augmentation2; //0x0034 + int32_t mana; //0x0038 + }; //Size: 0x003C + + class FL2ItemDataBase + { + public: + char pad_0000[4]; //0x0000 + L2::ItemDataType dataType; //0x0004 + char pad_0008[4]; //0x0008 + int32_t itemId; //0x000C + char pad_0010[20]; //0x0010 + int32_t dropItemsNameIndex; //0x0024 + char pad_0028[8]; //0x0028 + int32_t dropItemsTexNameIndex; //0x0030 + char pad_0034[8]; //0x0034 + int32_t iconNameIndex; //0x003C + char pad_0040[16]; //0x0040 + int32_t nameIndex; //0x0050 + char pad_0054[4]; //0x0054 + wchar_t* description; //0x0058 + char pad_005C[12]; //0x005C + wchar_t* setItem; //0x0068 + char pad_006C[8]; //0x006C + wchar_t* setEffect; //0x0074 + char pad_0078[8]; //0x0078 + wchar_t* addSetItem; //0x0080 + char pad_0084[8]; //0x0084 + wchar_t* addSetEffect; //0x008C + char pad_0090[36]; //0x0090 + wchar_t* enchantEffect; //0x00B4 + char pad_00B8[12]; //0x00B8 + int32_t weight; //0x00C4 + }; //Size: 0x0140 + + class FL2EtcItemData : public FL2ItemDataBase + { + }; + + class FL2ArmorItemData : public FL2ItemDataBase + { + public: + char pad_00C8[1308]; //0x00C8 + L2::ArmorType armorType; //0x05E4 + L2::CrystalType crystalType; //0x05E8 + char pad_05EC[4]; //0x05EC + int32_t pDef; //0x05F0 + int32_t mDef; //0x05F4 + char pad_05F8[8]; //0x05F8 + }; + + class FL2WeaponItemData : public FL2ItemDataBase + { + public: + char pad_00C8[24]; //0x00C8 + int32_t wtfNameIndex1; //0x00E0 + char pad_00E4[16]; //0x00E4 + int32_t wtfNameIndex2; //0x00F4 + int32_t wtfNameIndex3; //0x00F8 + char pad_00FC[12]; //0x00FC + int32_t wtfNameIndex4; //0x0108 + int32_t wtfNameIndex5; //0x010C + int32_t wtfNameIndex6; //0x0110 + int32_t wtfNameIndex7; //0x0114 + int32_t wtfNameIndex8; //0x0118 + int32_t wtfNameIndex9; //0x011C + char pad_0120[4]; //0x0120 + int32_t rndDamage; //0x0124 + int32_t pAtk; //0x0128 + int32_t mAtk; //0x012C + L2::WeaponType weaponType; //0x0130 + L2::CrystalType crystalType; //0x0134 + int32_t critical; //0x0138 + int32_t hitModify; //0x013C + int32_t shieldEvasion; //0x0140 + int32_t shieldPdef; //0x0144 + int32_t shieldDefRate; //0x0148 + int32_t atkSpd; //0x014C + int32_t mpConsume; //0x0150 + int32_t soulshotCount; //0x0154 + int32_t spiritshotCount; //0x0158 + char pad_015C[16]; //0x015C + int32_t wtfNameIndex10; //0x016C + char pad_0170[220]; //0x0170 + int32_t wtfNameIndex11; //0x024C + char pad_0250[48]; //0x0250 + }; //Size: 0x0280 + + class FNameEntry + { + public: + char pad_0000[12]; //0x0000 + wchar_t value[36]; //0x000C + }; + + class FL2MagicSkillData + { + public: + wchar_t* name; //0x0000 + char pad_0004[8]; //0x0004 + wchar_t* description; //0x000C + char pad_0010[8]; //0x0010 + int32_t skillId; //0x0018 + int32_t lvl; //0x001C + char pad_0020[4]; //0x0020 + int32_t mpCost; //0x0024 + char pad_0028[4]; //0x0028 + int32_t range; //0x002C + char pad_0030[4]; //0x0030 + float hitTime; //0x0034 + char pad_0038[12]; //0x0038 + int32_t wtfNameIndex; //0x0044 + int32_t iconNameIndex; //0x0048 + char pad_004C[52]; //0x004C + }; //Size: 0x0080 +}; diff --git a/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.cpp b/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.cpp new file mode 100644 index 0000000..e3b6be3 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.cpp @@ -0,0 +1,61 @@ +#include "pch.h" +#include "../../../Common/apihook.h" +#include "L2GameDataWrapper.h" +#include "ProcessManipulation.h" + +namespace Interlude +{ + void* L2GameDataWrapper::originalInitAddress = 0; + L2GameDataWrapper::L2GameData* L2GameDataWrapper::_target = 0; + + int(__thiscall* L2GameDataWrapper::__Init)(L2GameData*, int, int) = 0; + FL2ItemDataBase* (__thiscall* L2GameDataWrapper::__GetItemData)(L2GameData*, int) = 0; + FL2MagicSkillData* (__thiscall* L2GameDataWrapper::__GetMSData)(L2GameData*, int, int) = 0; + + void L2GameDataWrapper::Init(HMODULE hModule) + { + void* initAddress = GetProcAddress(hModule, "?GetMSData@FL2GameData@@QAEPAUFL2MagicSkillData@@HH@Z"); + originalInitAddress = splice(initAddress, __Init_hook); + (FARPROC&)__Init = (FARPROC)initAddress; + + (FARPROC&)__GetItemData = GetProcAddress(hModule, "?GetItemData@FL2GameData@@QAEPAVFL2ItemDataBase@@H@Z"); + (FARPROC&)__GetMSData = GetProcAddress(hModule, "?GetMSData@FL2GameData@@QAEPAUFL2MagicSkillData@@HH@Z"); + } + + void L2GameDataWrapper::Restore() + { + restore(originalInitAddress); + } + + //todo exception(?) + FL2ItemDataBase* L2GameDataWrapper::GetItemData(int itemId) const + { + if (__GetItemData && _target) { + return (*__GetItemData)(_target, itemId); + } + return 0; + } + + FL2MagicSkillData* L2GameDataWrapper::GetMSData(int skillId, int level) const + { + if (__GetMSData && _target) { + return (*__GetMSData)(_target, skillId, level); + } + return 0; + } + + int __fastcall L2GameDataWrapper::__Init_hook(L2GameData* This, int, int unk, int unk1) + { + if (_target == 0) { + _target = This; + + InjectLibrary::StopCurrentProcess(); + restore(originalInitAddress); + InjectLibrary::StartCurrentProcess(); + + return (*__Init)(This, unk, unk1); + } + + return 0; + } +}; diff --git a/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.h b/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.h new file mode 100644 index 0000000..90e0bca --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/L2GameDataWrapper.h @@ -0,0 +1,30 @@ +#pragma once + +#include "pch.h" +#include "GameStructs.h" +#include "../../GameStructs/L2GameDataInterface.h" + +namespace Interlude +{ + class L2GameDataWrapper : public L2GameDataInterface + { + public: + class L2GameData {}; + L2GameDataWrapper() = default; + virtual ~L2GameDataWrapper() = default; + + FL2ItemDataBase* GetItemData(int itemId) const; + FL2MagicSkillData* GetMSData(int skillId, int level) const; + void Init(HMODULE hModule) override; + void Restore() override; + private: + static int(__thiscall* __Init)(L2GameData*, int, int); + static FL2ItemDataBase* (__thiscall* __GetItemData)(L2GameData*, int); + static FL2MagicSkillData* (__thiscall* __GetMSData)(L2GameData*, int, int); + + static int __fastcall __Init_hook(L2GameData* This, int /*edx*/, int unk, int unk1); + private: + static void* originalInitAddress; + static L2GameData* _target; + }; +}; diff --git a/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.cpp b/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.cpp new file mode 100644 index 0000000..5556a93 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.cpp @@ -0,0 +1,71 @@ +#include "pch.h" +#include "L2ParamStack.h" + +namespace Interlude +{ + void(__thiscall* L2ParamStack::__Ctor)(L2ParamStack* This, int) = 0; + void(__thiscall* L2ParamStack::__Dtor)(L2ParamStack* This) = 0; + int(__thiscall* L2ParamStack::__PushBack)(L2ParamStack* This, void*) = 0; + void* (__thiscall* L2ParamStack::__Top)(L2ParamStack* This) = 0; + void** (__thiscall* L2ParamStack::__GetBuffer)(L2ParamStack* This) = 0; + int (__thiscall* L2ParamStack::__GetBufferSize)(L2ParamStack* This) = 0; + int (__thiscall* L2ParamStack::__GetTotalBufferSize)(L2ParamStack* This) = 0; + HMODULE L2ParamStack::_hModule = 0; + + L2ParamStack::L2ParamStack(int size) + { + Init(); + (*__Ctor)(this, size); + } + + L2ParamStack::~L2ParamStack() + { + Init(); + (*__Dtor)(this); + } + + int L2ParamStack::PushBack(void* val) + { + Init(); + return (*__PushBack)(this, val); + } + + void* L2ParamStack::Top() + { + Init(); + return (*__Top)(this); + } + + void** L2ParamStack::GetBuffer() + { + Init(); + return (__GetBuffer)(this); + } + + int L2ParamStack::GetBufferSize() + { + Init(); + return (__GetBufferSize)(this); + } + + int L2ParamStack::GetTotalBufferSize() + { + Init(); + return (__GetTotalBufferSize)(this); + } + + void L2ParamStack::Init() + { + // todo exceptions + if (_hModule == 0) { + _hModule = GetModuleHandleA("Core.dll"); + (FARPROC&)__Ctor = GetProcAddress(_hModule, "??0L2ParamStack@@QAE@H@Z"); + (FARPROC&)__Dtor = GetProcAddress(_hModule, "??1L2ParamStack@@QAE@XZ"); + (FARPROC&)__PushBack = GetProcAddress(_hModule, "?PushBack@L2ParamStack@@QAEHPAX@Z"); + (FARPROC&)__Top = GetProcAddress(_hModule, "?Top@L2ParamStack@@QAEPAXXZ"); + (FARPROC&)__GetBuffer = GetProcAddress(_hModule, "?GetBuffer@L2ParamStack@@QAEPAPAXXZ"); + (FARPROC&)__GetBufferSize = GetProcAddress(_hModule, "?GetBufferSize@L2ParamStack@@QAEHXZ"); + (FARPROC&)__GetTotalBufferSize = GetProcAddress(_hModule, "?GetTotalBufferSize@L2ParamStack@@QAEHXZ"); + } + } +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.h b/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.h new file mode 100644 index 0000000..d7a9cac --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/L2ParamStack.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +namespace Interlude +{ + class L2ParamStack + { + char padding[16] = ""; + public: + L2ParamStack(int size); + // Деструктор не должен быть виртуальным + ~L2ParamStack(); + int PushBack(void* val); + void* Top(); + void** GetBuffer(); + int GetBufferSize(); + int GetTotalBufferSize(); + + template + std::vector GetBufferAsVector() + { + std::vector result; + + auto buffer = GetBuffer(); + for (int i = 0; i < GetBufferSize(); i++) { + result.push_back((T) buffer[i]); + } + + return result; + } + + template + std::vector GetBufferAsVector(uint16_t objectSize) + { + std::vector result; + + uint32_t* tmpBuffer = (uint32_t*)GetBuffer(); + uint32_t objectsCount = tmpBuffer[0]; + + auto buffer = GetBuffer(); + + for (size_t i = 0; i < objectsCount; i++) { + for (size_t j = 0; j < objectSize; j++) { + result.push_back((T)buffer[i * objectSize + j + 1]); + } + } + + return result; + } + private: + void Init(); + private: + static void(__thiscall* __Ctor)(L2ParamStack* This, int); + static void(__thiscall* __Dtor)(L2ParamStack* This); + static int(__thiscall* __PushBack)(L2ParamStack* This, void*); + static void*(__thiscall* __Top)(L2ParamStack* This); + static void**(__thiscall* __GetBuffer)(L2ParamStack* This); + static int(__thiscall* __GetBufferSize)(L2ParamStack* This); + static int(__thiscall* __GetTotalBufferSize)(L2ParamStack* This); + static HMODULE _hModule; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.cpp b/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.cpp new file mode 100644 index 0000000..3d38c83 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.cpp @@ -0,0 +1,126 @@ +#include "pch.h" +#include "../../../Common/apihook.h" +#include "NetworkHandlerWrapper.h" +#include "../../../Events/SpoiledEvent.h" +#include "../../../Events/CreatureDiedEvent.h" +#include "../../../Events/EventDispatcher.h" +#include "ProcessManipulation.h" + +namespace Interlude +{ + void* NetworkHandlerWrapper::originalInitAddress = 0; + NetworkHandlerWrapper::NetworkHandler* NetworkHandlerWrapper::_target = 0; + + void(__thiscall* NetworkHandlerWrapper::__Init)(NetworkHandler*, float) = 0; + Item* (__thiscall* NetworkHandlerWrapper::__GetNextItem)(NetworkHandler*, float, int) = 0; + User* (__thiscall* NetworkHandlerWrapper::__GetNextCreature)(NetworkHandler*, float, int) = 0; + float(__thiscall* NetworkHandlerWrapper::__GetMaxTickRate)(NetworkHandler*) = 0; + int(__thiscall* NetworkHandlerWrapper::__AddNetworkQueue)(NetworkHandler*, L2::NetworkPacket*) = 0; + int(__thiscall* NetworkHandlerWrapper::__OnDie)(NetworkHandler*, User*, L2ParamStack&) = 0; + + //todo exception + Item* NetworkHandlerWrapper::GetNextItem(float_t radius, int prevId) const + { + if (__GetNextItem && _target) { + return (*__GetNextItem)(_target, radius, prevId); + } + return 0; + } + + //todo exception + User* NetworkHandlerWrapper::GetNextCreature(float_t radius, int prevId) const + { + if (__GetNextCreature && _target) { + return (*__GetNextCreature)(_target, radius, prevId); + } + return 0; + } + + User* NetworkHandlerWrapper::GetHero() const + { + const auto creatures = GetAllObjects(0.1f, [this](float_t radius, int32_t prevId) { + return GetNextCreature(radius, prevId); + }); + + for (const auto& kvp : creatures) + { + const auto& creature = static_cast(kvp.second); + if (creature->userType == L2::UserType::USER && creature->lvl > 0) + { + return creature; + } + } + return 0; + } + + void NetworkHandlerWrapper::Init(HMODULE hModule) + { + void* initAddress = GetProcAddress(hModule, "?Tick@UNetworkHandler@@UAEXM@Z"); + originalInitAddress = splice(initAddress, __Init_hook); + (FARPROC&)__Init = (FARPROC)initAddress; + + (FARPROC&)__GetNextItem = GetProcAddress(hModule, "?GetNextItem@UNetworkHandler@@UAEPAUItem@@MH@Z"); + (FARPROC&)__GetNextCreature = GetProcAddress(hModule, "?GetNextCreature@UNetworkHandler@@UAEPAUUser@@MH@Z"); + + (FARPROC&)__GetMaxTickRate = (FARPROC)splice( + GetProcAddress(hModule, "?GetMaxTickRate@UGameEngine@@UAEMXZ"), __GetMaxTickRate_hook + ); + (FARPROC&)__AddNetworkQueue = (FARPROC)splice( + GetProcAddress(hModule, "?AddNetworkQueue@UNetworkHandler@@UAEHPAUNetworkPacket@@@Z"), __AddNetworkQueue_hook + ); + (FARPROC&)__OnDie = (FARPROC)splice( + GetProcAddress(hModule, "?OnDie@UGameEngine@@UAEHPAUUser@@AAVL2ParamStack@@@Z"), __OnDie_hook + ); + } + + void NetworkHandlerWrapper::Restore() + { + restore(originalInitAddress); + restore((void*&)__GetMaxTickRate); + restore((void*&)__AddNetworkQueue); + restore((void*&)__OnDie); + } + + void __fastcall NetworkHandlerWrapper::__Init_hook(NetworkHandler* This, int /*edx*/, float unk) + { + if (_target == 0) { + _target = This; + + InjectLibrary::StopCurrentProcess(); + restore(originalInitAddress); + InjectLibrary::StartCurrentProcess(); + + (*__Init)(This, unk); + } + } + + // TODO ini + // 0 - фпс без ограничений + float __fastcall NetworkHandlerWrapper::__GetMaxTickRate_hook(NetworkHandler* This, int) + { + float fps = (*__GetMaxTickRate)(This); + return 0.0f; + } + + int __fastcall NetworkHandlerWrapper::__AddNetworkQueue_hook(NetworkHandler* This, int, L2::NetworkPacket* packet) + { + if (packet->id == static_cast(L2::NetworkPacketId::SYSTEM_MESSAGE)) { + L2::SystemMessagePacket* p = static_cast(packet); + if ( + p->GetMessageId() == static_cast(L2::SystemMessagePacket::Type::SPOIL_SUCCESS) || + p->GetMessageId() == static_cast(L2::SystemMessagePacket::Type::ALREADY_SPOILED) + ) { + EventDispatcher::GetInstance().Dispatch(SpoiledEvent{}); + } + } + + return (*__AddNetworkQueue)(This, packet); + } + + int __fastcall NetworkHandlerWrapper::__OnDie_hook(NetworkHandler* This, int, User* creature, L2ParamStack& stack) + { + EventDispatcher::GetInstance().Dispatch(CreatureDiedEvent{ creature->objectId }); + + return (*__OnDie)(This, creature, stack); + } +} diff --git a/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.h b/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.h new file mode 100644 index 0000000..5676832 --- /dev/null +++ b/L2BotDll/Versions/Interlude/GameStructs/NetworkHandlerWrapper.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include "../../GameStructs/NetworkHandlerInterface.h" +#include "GameStructs.h" +#include "../../GameStructs/FindObjectsTrait.h" +#include "L2ParamStack.h" + +namespace Interlude +{ + class NetworkHandlerWrapper : public NetworkHandlerInterface, public FindObjectsTrait + { + public: + class NetworkHandler {}; + NetworkHandlerWrapper() = default; + virtual ~NetworkHandlerWrapper() = default; + + void Init(HMODULE hModule) override; + void Restore() override; + + Item* GetNextItem(float_t radius, int prevId) const; + User* GetNextCreature(float_t radius, int prevId) const; + User* GetHero() const; + private: + + static void __fastcall __Init_hook(NetworkHandler* This, int /*edx*/, float unk); + static int __fastcall __AddNetworkQueue_hook(NetworkHandler* This, int /*edx*/, L2::NetworkPacket* packet); + static int __fastcall __OnDie_hook(NetworkHandler* This, int /*edx*/, User* creature, L2ParamStack& stack); + static float __fastcall __GetMaxTickRate_hook(NetworkHandler* This, int /*edx*/); + + static void(__thiscall* __Init)(NetworkHandler*, float); + static Item* (__thiscall* __GetNextItem)(NetworkHandler*, float, int); + static User* (__thiscall* __GetNextCreature)(NetworkHandler*, float, int); + static float(__thiscall* __GetMaxTickRate)(NetworkHandler*); + static int(__thiscall* __AddNetworkQueue)(NetworkHandler*, L2::NetworkPacket*); + static int(__thiscall* __OnDie)(NetworkHandler*, User*, L2ParamStack&); + private: + static void* originalInitAddress; + static NetworkHandler* _target; + }; +} diff --git a/L2BotDll/Versions/Interlude/Repositories/DropRepository.h b/L2BotDll/Versions/Interlude/Repositories/DropRepository.h new file mode 100644 index 0000000..1bb3ecb --- /dev/null +++ b/L2BotDll/Versions/Interlude/Repositories/DropRepository.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include "Domain/Repositories/DropRepositoryInterface.h" +#include "../Factories/DropFactory.h" +#include "../../GameStructs/FindObjectsTrait.h" + +using namespace L2Bot::Domain; + +namespace Interlude +{ + class DropRepository : public Repositories::DropRepositoryInterface, public FindObjectsTrait + { + public: + const std::map GetObjects() override + { + const auto items = GetAllObjects(m_Radius, [this](float_t radius, int32_t prevId) { + return m_NetworkHandler.GetNextItem(radius, prevId); + }); + + std::map map; + + for (const auto& kvp : items) + { + const auto item = kvp.second; + map.emplace(item->objectId, m_Factory.Create(item)); + } + + return map; + } + + DropRepository(const NetworkHandlerWrapper& networkHandler, const DropFactory& factory, const uint16_t radius) : + m_NetworkHandler(networkHandler), + m_Factory(factory), + m_Radius(radius) + { + + } + + DropRepository() = delete; + virtual ~DropRepository() = default; + + private: + const NetworkHandlerWrapper& m_NetworkHandler; + const DropFactory& m_Factory; + const uint16_t m_Radius; + }; +} diff --git a/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h b/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h new file mode 100644 index 0000000..6462278 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Repositories/HeroRepository.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Domain/Repositories/HeroRepositoryInterface.h" +#include "../Factories/HeroFactory.h" +#include "../../../Events/EventDispatcher.h" +#include "../../../Events/HeroCreatedEvent.h" +#include "../../../Events/HeroDeletedEvent.h" + +using namespace L2Bot::Domain; + +namespace Interlude +{ + class HeroRepository : public Repositories::HeroRepositoryInterface + { + public: + const std::map GetObjects() override + { + std::map map; + const auto hero = m_NetworkHandler.GetHero(); + if (hero) + { + map.emplace(hero->objectId, m_Factory.Create(hero)); + } + + if (hero != nullptr && m_PrevHero == nullptr) + { + EventDispatcher::GetInstance().Dispatch(HeroCreatedEvent{}); + } + if (hero == nullptr && m_PrevHero != nullptr) + { + EventDispatcher::GetInstance().Dispatch(HeroDeletedEvent{}); + } + + m_PrevHero = hero; + + return map; + } + + HeroRepository(const NetworkHandlerWrapper& networkHandler, const HeroFactory& factory) : + m_NetworkHandler(networkHandler), + m_Factory(factory) + { + + } + + HeroRepository() = delete; + virtual ~HeroRepository() = default; + + private: + const HeroFactory& m_Factory; + const NetworkHandlerWrapper& m_NetworkHandler; + User* m_PrevHero = nullptr; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Repositories/NPCRepository.h b/L2BotDll/Versions/Interlude/Repositories/NPCRepository.h new file mode 100644 index 0000000..d36ff83 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Repositories/NPCRepository.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include "../GameStructs/NetworkHandlerWrapper.h" +#include "Domain/Repositories/NPCRepositoryInterface.h" +#include "../Factories/NPCFactory.h" +#include "../../../Events/EventDispatcher.h" +#include "../../../Events/SpoiledEvent.h" +#include "../../../Events/CreatureDiedEvent.h" +#include "../../GameStructs/FindObjectsTrait.h" + +using namespace L2Bot::Domain; + +namespace Interlude +{ + class NPCRepository : public Repositories::NPCRepositoryInterface, public FindObjectsTrait + { + public: + const std::map GetObjects() override + { + const auto creatures = GetAllObjects(m_Radius, [this](float_t radius, int32_t prevId) { + return m_NetworkHandler.GetNextCreature(radius, prevId); + }); + + std::map map; + + for (const auto& kvp : creatures) + { + const auto creature = kvp.second; + if (creature->userType == L2::UserType::NPC) { + const auto spoilState = m_Spoiled.find(creature->objectId) == m_Spoiled.end() ? Enums::SpoilStateEnum::none : m_Spoiled.at(creature->objectId); + map.emplace(creature->objectId, m_Factory.Create(creature, spoilState)); + } + } + + return map; + } + + NPCRepository(const NetworkHandlerWrapper& networkHandler, const NPCFactory& factory, const uint16_t radius) : + m_NetworkHandler(networkHandler), + m_Factory(factory), + m_Radius(radius) + { + EventDispatcher::GetInstance().Subscribe(SpoiledEvent::name, [this](const Event& evt) { + OnSpoiled(evt); + }); + EventDispatcher::GetInstance().Subscribe(CreatureDiedEvent::name, [this](const Event& evt) { + OnCreatureDied(evt); + }); + } + + NPCRepository() = delete; + virtual ~NPCRepository() = default; + + void OnSpoiled(const Event& evt) + { + if (evt.GetName() == SpoiledEvent::name) + { + const auto casted = static_cast(evt); + const auto hero = m_NetworkHandler.GetHero(); + if (hero && hero->pawn && hero->pawn->lineagePlayerController) + { + const auto targetId = hero->pawn->lineagePlayerController->targetObjectId; + if (targetId) + { + m_Spoiled[targetId] = Enums::SpoilStateEnum::spoiled; + } + } + } + } + + void OnCreatureDied(const Event& evt) + { + if (evt.GetName() == CreatureDiedEvent::name) + { + const auto casted = static_cast(evt); + if (m_Spoiled.find(casted.GetCreatureId()) != m_Spoiled.end()) + { + if (m_Spoiled[casted.GetCreatureId()] == Enums::SpoilStateEnum::spoiled) + { + m_Spoiled[casted.GetCreatureId()] = Enums::SpoilStateEnum::sweepable; + } + else + { + m_Spoiled[casted.GetCreatureId()] = Enums::SpoilStateEnum::none; + } + } + } + } + + private: + const NPCFactory& m_Factory; + std::map m_Spoiled; + const NetworkHandlerWrapper& m_NetworkHandler; + const uint16_t m_Radius = 0; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Repositories/PlayerRepository.h b/L2BotDll/Versions/Interlude/Repositories/PlayerRepository.h new file mode 100644 index 0000000..6445ed6 --- /dev/null +++ b/L2BotDll/Versions/Interlude/Repositories/PlayerRepository.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include "Domain/Repositories/PlayerRepositoryInterface.h" +#include "../Factories/PlayerFactory.h" +#include "../../GameStructs/FindObjectsTrait.h" +#include "../GameStructs/NetworkHandlerWrapper.h" + +using namespace L2Bot::Domain; + +namespace Interlude +{ + class PlayerRepository : public Repositories::PlayerRepositoryInterface, public FindObjectsTrait + { + public: + const std::map GetObjects() override + { + const auto creatures = GetAllObjects(m_Radius, [this](float_t radius, int32_t prevId) { + return m_NetworkHandler.GetNextCreature(radius, prevId); + }); + + std::map map; + + for (const auto& kvp : creatures) + { + const auto creature = kvp.second; + if (creature->userType == L2::UserType::USER && creature->lvl == 0) { + map.emplace(creature->objectId, m_Factory.Create(creature)); + } + } + + return map; + } + + PlayerRepository(const NetworkHandlerWrapper& networkHandler, const PlayerFactory& factory, const uint16_t radius) : + m_NetworkHandler(networkHandler), + m_Factory(factory), + m_Radius(radius) + { + + } + + PlayerRepository() = delete; + virtual ~PlayerRepository() = default; + + private: + const PlayerFactory& m_Factory; + const NetworkHandlerWrapper& m_NetworkHandler; + const uint16_t m_Radius; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/Interlude/Repositories/SkillRepository.h b/L2BotDll/Versions/Interlude/Repositories/SkillRepository.h new file mode 100644 index 0000000..ee9e93d --- /dev/null +++ b/L2BotDll/Versions/Interlude/Repositories/SkillRepository.h @@ -0,0 +1,214 @@ +#pragma once + +#include +#include +#include +#include "Domain/Repositories/SkillRepositoryInterface.h" +#include "../Factories/SkillFactory.h" +#include "../../../Events/SkillCreatedEvent.h" +#include "../../../Events/SkillUsedEvent.h" +#include "../../../Events/SkillCancelledEvent.h" +#include "../../../Events/AbnormalEffectChangedEvent.h" +#include "../../../Events/HeroDeletedEvent.h" +#include "../GameStructs/NetworkHandlerWrapper.h" +#include "../../../Common/TimerMap.h" + +using namespace L2Bot::Domain; + +namespace Interlude +{ + class SkillRepository : public Repositories::SkillRepositoryInterface + { + public: + const std::map GetObjects() override + { + std::unique_lock(m_Mutex); + return m_Skills; + } + + SkillRepository(const NetworkHandlerWrapper& networkHandler, const SkillFactory& factory) : + m_NetworkHandler(networkHandler), + m_Factory(factory) + { + EventDispatcher::GetInstance().Subscribe(SkillCreatedEvent::name, [this](const Event& evt) { + OnSkillCreated(evt); + }); + EventDispatcher::GetInstance().Subscribe(SkillUsedEvent::name, [this](const Event& evt) { + OnSkillUsed(evt); + }); + EventDispatcher::GetInstance().Subscribe(SkillCancelledEvent::name, [this](const Event& evt) { + OnSkillCancelled(evt); + }); + EventDispatcher::GetInstance().Subscribe(AbnormalEffectChangedEvent::name, [this](const Event& evt) { + OnSkillToggled(evt); + }); + EventDispatcher::GetInstance().Subscribe(HeroDeletedEvent::name, [this](const Event& evt) { + OnHeroDeleted(evt); + }); + } + + SkillRepository() = delete; + virtual ~SkillRepository() = default; + + void OnHeroDeleted(const Event& evt) + { + std::shared_lock(m_Mutex); + if (evt.GetName() == HeroDeletedEvent::name) + { + m_Skills.clear(); + m_CastingTimers.StopAll(); + m_ReloadingTimers.StopAll(); + } + } + void OnSkillCreated(const Event& evt) + { + std::shared_lock(m_Mutex); + if (evt.GetName() == SkillCreatedEvent::name) + { + const auto casted = static_cast(evt); + const auto skillInfo = casted.GetSkillInfo(); + const auto skillId = skillInfo[2]; + + const auto alreadyExists = m_Skills.find(skillId) != m_Skills.end(); + + auto skill = m_Factory.Create( + alreadyExists ? m_Skills[skillId] : DTO::Skill(), + skillInfo[2], + skillInfo[1], + skillInfo[0] + ); + + UpdateSkill(skill); + } + } + void OnSkillUsed(const Event& evt) + { + std::shared_lock(m_Mutex); + if (evt.GetName() == SkillUsedEvent::name) + { + const auto casted = static_cast(evt); + const auto skillInfo = casted.GetSkillInfo(); + const auto skillId = skillInfo[0]; + + if (m_Skills.find(skillId) == m_Skills.end()) + { + //todo exception? + return; + } + + auto skill = m_Factory.UpdateReloadingState( + m_Factory.UpdateCastingState( + m_Skills[skillId], + true + ), + true + ); + + UpdateSkill(skill); + m_UsedSkillId = skill.skillId; + + m_ReloadingTimers.StartTimer(skill.skillId, skillInfo[3], [this] (uint32_t skillId) { + std::shared_lock(m_Mutex); + auto skill = m_Factory.UpdateReloadingState( + m_Skills[skillId], + false + ); + UpdateSkill(skill); + }); + m_CastingTimers.StartTimer(skill.skillId, skillInfo[2], [this] (uint32_t skillId) { + std::shared_lock(m_Mutex); + auto skill = m_Factory.UpdateCastingState( + m_Skills[m_UsedSkillId], + false + ); + UpdateSkill(skill); + }); + } + } + void OnSkillCancelled(const Event& evt) + { + std::shared_lock(m_Mutex); + if (evt.GetName() == SkillCancelledEvent::name) + { + const auto casted = static_cast(evt); + + const auto hero = m_NetworkHandler.GetHero(); + + if (hero && hero->objectId == casted.GetInitiatorId()) + { + if (m_Skills.find(m_UsedSkillId) == m_Skills.end()) + { + //todo exception? + return; + } + + auto skill = m_Factory.UpdateCastingState( + m_Skills[m_UsedSkillId], + false + ); + + UpdateSkill(skill); + m_UsedSkillId = 0; + + m_CastingTimers.StopTimer(skill.skillId); + } + } + } + void OnSkillToggled(const Event& evt) + { + std::shared_lock(m_Mutex); + if (evt.GetName() == AbnormalEffectChangedEvent::name) + { + const auto casted = static_cast(evt); + const auto skillInfo = casted.GetSkillInfo(); + + std::map ids; + + for (size_t i = 0; i < skillInfo.size(); i += 3) + { + ids[skillInfo[i]] = skillInfo[i + 2]; + } + + for (auto it = m_Skills.begin(); it != m_Skills.end();) + { + const auto needToToggle = ids.find(it->second.skillId) != ids.end(); + // buff time less than zero means this is a aura + const auto isAura = needToToggle ? ids[it->second.skillId] < 0 : false; + + if (it->second.isToggled && !needToToggle) + { + auto skill = m_Factory.UpdateToggle(it->second, false); + it = m_Skills.erase(it); + m_Skills.emplace(skill.skillId, skill); + } + else if (!it->second.isToggled && needToToggle && isAura) + { + auto skill = m_Factory.UpdateToggle(it->second, true); + it = m_Skills.erase(it); + m_Skills.emplace(skill.skillId, skill); + } + else + { + ++it; + } + } + } + } + + private: + void UpdateSkill(const DTO::Skill skill) + { + m_Skills.erase(skill.skillId); + m_Skills.emplace(skill.skillId, skill); + } + + private: + const SkillFactory& m_Factory; + std::map m_Skills; + uint32_t m_UsedSkillId = 0; + const NetworkHandlerWrapper& m_NetworkHandler; + TimerMap m_ReloadingTimers; + TimerMap m_CastingTimers; + std::shared_timed_mutex m_Mutex; + }; +} \ No newline at end of file diff --git a/L2BotDll/Versions/VersionAbstractFactory.cpp b/L2BotDll/Versions/VersionAbstractFactory.cpp new file mode 100644 index 0000000..6f39050 --- /dev/null +++ b/L2BotDll/Versions/VersionAbstractFactory.cpp @@ -0,0 +1,15 @@ +#include "pch.h" +#include "VersionAbstractFactory.h" +#include "Interlude/AbstractFactory.h" + +const VersionAbstractFactory& VersionAbstractFactory::GetFactory(const Version version, const uint16_t radius) +{ + switch (version) + { + case Version::interlude: + static Interlude::AbstractFactory interlude = Interlude::AbstractFactory(radius); + return interlude; + } + + //todo throw exception +} \ No newline at end of file diff --git a/L2BotDll/Versions/VersionAbstractFactory.h b/L2BotDll/Versions/VersionAbstractFactory.h new file mode 100644 index 0000000..d19f394 --- /dev/null +++ b/L2BotDll/Versions/VersionAbstractFactory.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Domain/Repositories/HeroRepositoryInterface.h" +#include "Domain/Repositories/DropRepositoryInterface.h" +#include "Domain/Repositories/NPCRepositoryInterface.h" +#include "Domain/Repositories/PlayerRepositoryInterface.h" +#include "Domain/Repositories/SkillRepositoryInterface.h" +#include "GameStructs/NetworkHandlerInterface.h" +#include "GameStructs/GameEngineInterface.h" +#include "GameStructs/L2GameDataInterface.h" +#include "GameStructs/FNameInterface.h" + +using namespace L2Bot::Domain; + +class VersionAbstractFactory +{ +public: + enum class Version + { + interlude + }; + + virtual Repositories::HeroRepositoryInterface& GetHeroRepository() const = 0; + virtual Repositories::DropRepositoryInterface& GetDropRepository() const = 0; + virtual Repositories::NPCRepositoryInterface& GetNPCRepository() const = 0; + virtual Repositories::PlayerRepositoryInterface& GetPlayerRepository() const = 0; + virtual Repositories::SkillRepositoryInterface& GetSkillRepository() const = 0; + virtual NetworkHandlerInterface& GetNetworkHandler() const = 0; + virtual GameEngineInterface& GetGameEngine() const = 0; + virtual L2GameDataInterface& GetL2GameData() const = 0; + virtual FNameInterface& GetFName() const = 0; + + static const VersionAbstractFactory& GetFactory(Version version, const uint16_t radius); +}; \ No newline at end of file diff --git a/L2BotDll/dllmain.cpp b/L2BotDll/dllmain.cpp new file mode 100644 index 0000000..6d7ee47 --- /dev/null +++ b/L2BotDll/dllmain.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "Common/apihook.h" +#include "Application.h" +#include "ProcessManipulation.h" +#include "Injector.h" + +InjectLibrary::Injector injector("L2BotHookMutex", WH_CALLWNDPROC); +Application application(VersionAbstractFactory::Version::interlude); + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + const std::string& processName = InjectLibrary::GetCurrentProcessName(); + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + injector.SetHook(hModule); + if (processName == "l2.exe") { + MessageBox(0, L"A", L"B", MB_OK); + InjectLibrary::StopCurrentProcess(); + application.Start(); + InjectLibrary::StartCurrentProcess(); + } + break; + case DLL_PROCESS_DETACH: + if (processName == "l2.exe") { + InjectLibrary::StopCurrentProcess(); + application.Stop(); + InjectLibrary::StartCurrentProcess(); + } + injector.SetHook(); + break; + } + return TRUE; +} \ No newline at end of file diff --git a/L2BotDll/framework.h b/L2BotDll/framework.h new file mode 100644 index 0000000..54b83e9 --- /dev/null +++ b/L2BotDll/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include diff --git a/L2BotDll/pch.cpp b/L2BotDll/pch.cpp new file mode 100644 index 0000000..379204f --- /dev/null +++ b/L2BotDll/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. \ No newline at end of file diff --git a/L2BotDll/pch.h b/L2BotDll/pch.h new file mode 100644 index 0000000..7b7042a --- /dev/null +++ b/L2BotDll/pch.h @@ -0,0 +1,35 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string ConvertFromWideChar(const wchar_t* str); + +#endif //PCH_H diff --git a/Sandbox/Sandbox.cpp b/Sandbox/Sandbox.cpp new file mode 100644 index 0000000..bc965d9 --- /dev/null +++ b/Sandbox/Sandbox.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main() +{ + /*NamedPipe p; + p.Connect("PipeL2Bot"); + p.Send("\\\\.\\pipe\\Test"); + + NamedPipe p1; + p1.Connect("Test"); + + while (true) + { + p1.Send("123"); + std::cin.get(); + }*/ + + HMODULE hDll = LoadLibraryA("L2BotDll.dll"); + std::cin.get(); + FreeLibrary(hDll); +} diff --git a/Sandbox/Sandbox.vcxproj b/Sandbox/Sandbox.vcxproj new file mode 100644 index 0000000..cebd760 --- /dev/null +++ b/Sandbox/Sandbox.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {28b1b2bc-bb6c-483e-b18d-e532a29ed8ea} + Sandbox + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + false + $(SolutionDir)$(Configuration)\bin\ + $(SolutionDir)$(Configuration)\$(ProjectName)\ + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc17 + D:\Lineage 2 bot\Bot 2.0\L2Bot\L2BotCore;%(AdditionalIncludeDirectories) + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + stdc17 + D:\Lineage 2 bot\Bot 2.0\L2Bot\L2BotCore;%(AdditionalIncludeDirectories) + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/Sandbox/Sandbox.vcxproj.filters b/Sandbox/Sandbox.vcxproj.filters new file mode 100644 index 0000000..961f0f2 --- /dev/null +++ b/Sandbox/Sandbox.vcxproj.filters @@ -0,0 +1,22 @@ +п»ї + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file