diff --git a/release/docs/MultiSend.txt b/release/docs/MultiSend.txt new file mode 100644 index 0000000..7a1e68c --- /dev/null +++ b/release/docs/MultiSend.txt @@ -0,0 +1,16 @@ +Multisend is a replacement for servo. No synchronization is needed, just load and go. +Commands are as follows(all can be prefixed with /ms or /multisend): + +/ms send [command] - Sends the command to all characters with MultiSend loaded. +/ms sendto CharName [command] - Sends the command to a specific character, if they have MultiSend loaded. +/ms sendgroup GroupName [command] - Sends the command to all characters listed in a group. Groups can be defined in Ashita/Config/MultiSend.xml. See XML Structure file for example. +/ms followme on/off - Enables or disables followme. When followme is enabled, any characters with follow enabled will follow the player who last activated followme. +/ms follow on/off - Enables or disables follow. Any character with follow enabled with follow the player who last activated followme. +/ms ignoreself on/off - When enabled, any command that would effect the local character is not executed by the local character. +/ms debug on/off - When enabled, all received commands are printed to log and any character with followme updated will spam log with position updates. For debug purposes, obviously. +/ms reload - Will reload groups xml. +/ms help - Print an ingame reference. + +You may use [t] [me] or [p0] [p1] [p2] [p3] [p4] [p5] to replace the <> equivalents, if you wish to resolve them on local character. This will replace them with the appropriate server IDs, allowing the command to resolve on receiving character. + +Example: Typing /ms send /ma "Fire IV" [t] will make all characters with multisend loaded cast Fire IV on the character who typed the command's target, rather than their own current target. \ No newline at end of file diff --git a/release/docs/XML Structure.xml b/release/docs/XML Structure.xml new file mode 100644 index 0000000..3820e71 --- /dev/null +++ b/release/docs/XML Structure.xml @@ -0,0 +1,10 @@ + + + Avesta + Stanlee + + + Deadpool + Wolverine + + \ No newline at end of file diff --git a/release/plugins/MultiSend.dll b/release/plugins/MultiSend.dll new file mode 100644 index 0000000..1bb6b2c Binary files /dev/null and b/release/plugins/MultiSend.dll differ diff --git a/src/MultiSend.sln b/src/MultiSend.sln new file mode 100644 index 0000000..f786c16 --- /dev/null +++ b/src/MultiSend.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MultiSend", "MultiSend\MultiSend.vcxproj", "{3C74E1A4-E44B-4523-85DC-9A94AC2D6457}" +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 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Debug|x64.ActiveCfg = Debug|x64 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Debug|x64.Build.0 = Debug|x64 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Debug|x86.ActiveCfg = Debug|Win32 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Debug|x86.Build.0 = Debug|Win32 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Release|x64.ActiveCfg = Release|x64 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Release|x64.Build.0 = Release|x64 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Release|x86.ActiveCfg = Release|Win32 + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/MultiSend/Commands.cpp b/src/MultiSend/Commands.cpp new file mode 100644 index 0000000..65a50d4 --- /dev/null +++ b/src/MultiSend/Commands.cpp @@ -0,0 +1,241 @@ +#include "MultiSend.h" + +bool MultiSend::HandleCommand(const char* command, int32_t type) +{ + std::string arg; + const char* com = command + GetArg(command, &arg); + if ((_stricmp(arg.c_str(), "/ms")) + && (_stricmp(arg.c_str(), "/multisend"))) + { + return false; + } + + if (strlen(com) < 2) return true; + com++; + com += GetArg(com, &arg); + + + if (_stricmp(arg.c_str(), "send") == 0) + { + if (strlen(com) < 2) return true; + SendCommand(0xFFFF0000, com + 1); + return true; + } + + if (_stricmp(arg.c_str(), "sendto") == 0) + { + if (strlen(com) < 2) + { + m_AshitaCore->GetChatManager()->Write("MultiSend: Invalid command."); + return true; + } + com++; + com += GetArg(com, &arg); + + Claim(&(MMF_Pointer->Name.ProcessID), 0); + for (int x = 0; x < 100; x++) + { + if (MMF_Pointer->Name.Names[x].Active) + { + if (_stricmp((const char*)&(MMF_Pointer->Name.Names[x].Name), arg.c_str()) == 0) + { + SendCommand(MMF_Pointer->Name.Names[x].Process, com + 1); + InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); + return true; + } + } + } + + InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); + m_AshitaCore->GetChatManager()->Write("MultiSend: Character not found."); + } + + if (_stricmp(arg.c_str(), "sendgroup") == 0) + { + if (strlen(com) < 3) + { + m_AshitaCore->GetChatManager()->Write("MultiSend: Invalid command."); + return true; + } + com++; + com += GetArg(com, &arg); + + for (std::map::iterator it = m_Group.Map.begin(); it != m_Group.Map.end(); it++) + { + if (_stricmp((*it).second.c_str(), arg.c_str()) == 0) + { + SendCommand(0x8FFF0000 + (*it).first, com + 1); + return true; + } + } + + m_AshitaCore->GetChatManager()->Write("MultiSend: Group not found."); + return true; + } + + if (_stricmp(arg.c_str(), "followme") == 0) + { + if (strlen(com) < 2) return true; + com++; + com += GetArg(com, &arg); + + if (_stricmp(arg.c_str(), "on") == 0) + { + InterlockedExchange(&(MMF_Pointer->Follow.FollowID), GetCurrentProcessId()); + if (!Zoning) + { + Following = false; + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + uint32_t mPosX = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); + uint32_t mPosZ = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); + MMF_Pointer->Follow.PosX = mPosX; + MMF_Pointer->Follow.PosZ = mPosZ; + MMF_Pointer->Follow.Follow = 1; + } + else + { + Following = false; + MMF_Pointer->Follow.Follow = 3; + } + m_AshitaCore->GetChatManager()->Write("MultiSend: Followme enabled."); + } + + else if (_stricmp(arg.c_str(), "off") == 0) + { + InterlockedExchange(&(MMF_Pointer->Follow.FollowID), 0); + MMF_Pointer->Follow.Follow = 0; + m_AshitaCore->GetChatManager()->Write("MultiSend: Followme disabled."); + } + + else + { + m_AshitaCore->GetChatManager()->Writef("MultiSend: Followme is currently %s.", (MMF_Pointer->Follow.FollowID == GetCurrentProcessId()) ? "enabled" : "disabled"); + } + + return true; + } + + if (_stricmp(arg.c_str(), "follow") == 0) + { + if (strlen(com) < 2) return true; + com++; + com += GetArg(com, &arg); + + if (_stricmp(arg.c_str(), "on") == 0) + { + FollowEnabled = true; + m_AshitaCore->GetChatManager()->Write("MultiSend: Follow enabled. Note that if someone is not currently using followme, no movement will occur."); + } + + else if (_stricmp(arg.c_str(), "off") == 0) + { + FollowEnabled = false; + m_AshitaCore->GetChatManager()->Write("MultiSend: Follow disabled."); + } + + else + { + m_AshitaCore->GetChatManager()->Writef("MultiSend: Follow is currently %s.", FollowEnabled ? "enabled" : "disabled"); + } + + return true; + } + + if (_stricmp(arg.c_str(), "ignoreself") == 0) + { + if (strlen(com) < 2) return true; + com++; + com += GetArg(com, &arg); + + if (_stricmp(arg.c_str(), "on") == 0) + { + IgnoreSelf = true; + m_AshitaCore->GetChatManager()->Write("MultiSend: Now ignoring self-published commands."); + } + + else if (_stricmp(arg.c_str(), "off") == 0) + { + IgnoreSelf = false; + m_AshitaCore->GetChatManager()->Write("MultiSend: Now accepting self-published commands."); + } + + else + { + m_AshitaCore->GetChatManager()->Writef("MultiSend: Ignore self is currently %s.", IgnoreSelf ? "enabled" : "disabled"); + } + return true; + } + + if (_stricmp(arg.c_str(), "debug") == 0) + { + if (strlen(com) < 2) return true; + com++; + com += GetArg(com, &arg); + + if (_stricmp(arg.c_str(), "on") == 0) + { + IgnoreSelf = true; + m_AshitaCore->GetChatManager()->Write("MultiSend: Debug prints enabled."); + } + + else if (_stricmp(arg.c_str(), "off") == 0) + { + IgnoreSelf = false; + m_AshitaCore->GetChatManager()->Write("MultiSend: Debug prints disabled."); + } + + else + { + m_AshitaCore->GetChatManager()->Writef("MultiSend: Debug prints currently %s.", _Debug ? "enabled" : "disabled"); + } + + return true; + } + + if (_stricmp(arg.c_str(), "reload") == 0) + { + LoadGroups(); + return true; + } + + if (_stricmp(arg.c_str(), "help") == 0) + { + m_AshitaCore->GetChatManager()->Write("Multisend Command Listing"); + m_AshitaCore->GetChatManager()->Write("/ms send [command] - Sends [command] to all characters with multisend loaded."); + m_AshitaCore->GetChatManager()->Write("/ms sendto [char name] [command] - Sends [command] to all characters with multisend loaded named [char name]."); + m_AshitaCore->GetChatManager()->Write("/ms sendgroup [group name] [command] - Sends [command] to all characters with multisend loaded that are defined within the group [group name]."); + m_AshitaCore->GetChatManager()->Write("/ms follow on/off - When enabled, the current character will obey followme."); + m_AshitaCore->GetChatManager()->Write("/ms followme on/off - When enabled, all characters with follow enabled will follow this character. Only one character can have this active at a time, if you activate it on a second the first will deactivate automatically."); + m_AshitaCore->GetChatManager()->Write("/ms reload - Reloads group file without reloading MultiSend."); + m_AshitaCore->GetChatManager()->Write("/ms ignoreself on/off - When enabled, send and sendgroup commands sent by this character will not execute on this character."); + m_AshitaCore->GetChatManager()->Write("/ms debug on/off - When enabled, debug prints will be visible."); + return true; + } + + return true; +} + +uint32_t MultiSend::GetArg(const char* text, std::string* buffer) +{ + std::string working(text); + + if (working[0] == '"') + { + size_t second = working.substr(1).find_first_of('"'); + if (second != string::npos) + { + *buffer = working.substr(1, second); + return second + 1; + } + } + + size_t space = working.find_first_of(' '); + if (space != string::npos) + { + *buffer = working.substr(0, space); + return space; + } + + *buffer = string(text); + return strlen(text); +} \ No newline at end of file diff --git a/src/MultiSend/Exports.def b/src/MultiSend/Exports.def new file mode 100644 index 0000000..f04134d --- /dev/null +++ b/src/MultiSend/Exports.def @@ -0,0 +1,5 @@ +LIBRARY "MultiSend" +EXPORTS + GetInterfaceVersion + CreatePluginInfo + CreatePlugin \ No newline at end of file diff --git a/src/MultiSend/Follow.cpp b/src/MultiSend/Follow.cpp new file mode 100644 index 0000000..121a6c9 --- /dev/null +++ b/src/MultiSend/Follow.cpp @@ -0,0 +1,43 @@ +#include "MultiSend.h" + +bool MultiSend::Direct3DInitialize(IDirect3DDevice8* lpDevice) +{ + this->m_Direct3DDevice = lpDevice; + return true; +} + +/** +* @brief Direct3D prerender call to allow this plugin to prepare for rendering. +* +* @note This will only be called if you returned true in Direct3DInitialize! +*/ +void MultiSend::Direct3DPreRender(void) +{ + if (FollowEnabled) + { + if (Following) + { + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + float MyX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex); + float MyZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex); + double Distance = sqrt(pow(PositionX - MyX, 2) + pow(PositionZ - MyZ, 2)); + + if (Distance > 0.4f) + { + StructPointer->DirX = PositionX - MyX; + StructPointer->DirY = 0; + StructPointer->DirZ = PositionZ - MyZ; + StructPointer->Autorun = 1; + } + else + { + StructPointer->Autorun = 0; + } + } + else if (StopFollow) + { + StructPointer->Autorun = 0; + StopFollow = false; + } + } +} \ No newline at end of file diff --git a/src/MultiSend/Groups.cpp b/src/MultiSend/Groups.cpp new file mode 100644 index 0000000..0b97005 --- /dev/null +++ b/src/MultiSend/Groups.cpp @@ -0,0 +1,126 @@ +#include "MultiSend.h" +#include +#include +using namespace rapidxml; + +bool MultiSend::MatchID(uint32_t ID) +{ + if ((ID & 0xFFFF0000) == 0xFFFF0000) + { + return true; + } + if ((ID & 0x8FFF0000) == 0x8FFF0000) + { + uint16_t GroupID = (ID & 0x0000FFFF); + return (std::find(Groups.begin(), Groups.end(), GroupID) != Groups.end()); + } + return (::GetCurrentProcessId() == ID); +} + +void MultiSend::LoadGroups() +{ + m_Group.Map.clear(); + Groups.clear(); + string Path = m_AshitaCore->GetAshitaInstallPathA(); + Path += "config\\MultiSend.xml"; + + std::ifstream Reader(Path, ios::in | ios::binary | ios::ate); + if (Reader.is_open()) + { + Reader.seekg(0, ios::end); + uint32_t Size = (uint32_t)Reader.tellg(); + + m_Group.rawdata = new char[Size + 1]; + Reader.seekg(0, ios::beg); + Reader.read(m_Group.rawdata, Size); + Reader.close(); + m_Group.rawdata[Size] = '\0'; + try + { + m_Group.doc.parse<0>(m_Group.rawdata); + m_AshitaCore->GetChatManager()->Write("MultiSend: Groups loaded."); + m_Group.Loaded = true; + } + catch (...) + { + m_AshitaCore->GetChatManager()->Write("MultiSend: Groups failed to parse."); + m_Group.doc.clear(); + m_Group.Loaded = false; + delete m_Group.rawdata; + } + } + else + { + if (m_Group.Loaded) + { + m_Group.doc.clear(); + delete m_Group.rawdata; + m_Group.Loaded = false; + } + m_AshitaCore->GetChatManager()->Write("MultiSend: No groups file found."); + } + + if (m_Group.Loaded) + { + ReadXML(); + MatchGroups(); + } +} + +void MultiSend::ReadXML() +{ + m_Group.Map.clear(); + + xml_node<> *Node = m_Group.doc.first_node(); + if (_stricmp(Node->name(), "multisend") == 0) Node = Node->first_node(); + while (Node) + { + if (_stricmp(Node->name(), "group") == 0) + { + xml_attribute<> *Attr = Node->first_attribute("index"); + if (Attr) + { + xml_attribute<> *Attr2 = Node->first_attribute("name"); + if (Attr2) + { + m_Group.Map.insert(std::make_pair((uint16_t)atoi(Attr->value()), std::string(Attr2->value()))); + } + } + } + Node = Node->next_sibling(); + } +} + +void MultiSend::MatchGroups() +{ + Groups.clear(); + if (CurrentName.length() < 3) return; + if (!m_Group.Loaded) return; + + xml_node<> *Node = m_Group.doc.first_node(); + if (_stricmp(Node->name(), "multisend") == 0) Node = Node->first_node(); + while (Node) + { + if (_stricmp(Node->name(), "group") == 0) + { + xml_attribute<> *Attr = Node->first_attribute("index"); + if (Attr) + { + xml_attribute<> *Attr2 = Node->first_attribute("name"); + if (Attr2) + { + uint16_t Index = (uint16_t)atoi(Attr->value()); + for (xml_node<> *SubNode = Node->first_node("char"); SubNode != NULL; SubNode = SubNode->next_sibling("char")) + { + if (_stricmp(CurrentName.c_str(), SubNode->value()) == 0) + { + Groups.push_back(Index); + break; + } + } + } + } + } + Node = Node->next_sibling(); + } +} \ No newline at end of file diff --git a/src/MultiSend/IO.cpp b/src/MultiSend/IO.cpp new file mode 100644 index 0000000..cbe8bde --- /dev/null +++ b/src/MultiSend/IO.cpp @@ -0,0 +1,147 @@ +#include "MultiSend.h" + +bool MultiSend::ReadCommand() +{ + if (MMF_Pointer->Command.Command[Position].Active) + { + if (MatchID(MMF_Pointer->Command.Command[Position].Targets)) + { + if ((!IgnoreSelf) + || (GetCurrentProcessId() != MMF_Pointer->Command.Command[Position].SendProcess)) + { + const char* text = new char[248]; + memset((void*)text, 0, 248); + memcpy((void*)text, &(MMF_Pointer->Command.Command[Position].Command), 248); + if (_Debug) + { + m_AshitaCore->GetChatManager()->Writef("Sending command: %s", text); + } + m_AshitaCore->GetChatManager()->QueueCommand(text, 0); + delete text; + } + } + Position++; + if (Position == 100) Position = 0; + return true; + } + return false; +} + +void MultiSend::SetFollow(bool Active) +{ + if (Active) + { + Following = false; + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + uint32_t mPosX = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); + uint32_t mPosZ = (uint32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); + MMF_Pointer->Follow.PosX = mPosX; + MMF_Pointer->Follow.PosZ = mPosZ; + MMF_Pointer->Follow.Follow = 1; + } + else + { + MMF_Pointer->Follow.Follow = 0; + } +} + +void MultiSend::SendCommand(uint32_t ID, const char* Command) +{ + char* Text = new char[248]; + memset((void*)Text, 0, 248); + memcpy(Text, Command, strlen(Command)); + SubValues(Text); + + Claim(&(MMF_Pointer->Command.ProcessID), 0); + + int NextPosition = MMF_Pointer->Command.Position + 1; + if (NextPosition == 100) NextPosition = 0; + + MMF_Pointer->Command.Command[NextPosition].Active = false; + + memset(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), 0, 248); + if (strlen(Text) > 247) + { + memcpy(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), Text, 247); + } + else + { + memcpy(&(MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Command), Text, strlen(Text)); + } + if (_Debug) + { + m_AshitaCore->GetChatManager()->Writef("Publishing position %d : %s", MMF_Pointer->Command.Position, Text); + } + MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].SendProcess = GetCurrentProcessId(); + MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Targets = ID; + MMF_Pointer->Command.Command[MMF_Pointer->Command.Position].Active = true; + MMF_Pointer->Command.Position = NextPosition; + InterlockedExchange(&(MMF_Pointer->Command.ProcessID), 0); + delete Text; +} + +void MultiSend::UpdateName(std::string Name) +{ + Claim(&(MMF_Pointer->Name.ProcessID), 0xFFFF0000); + bool Written = false; + for (int x = 0; x < 100; x++) + { + if (!MMF_Pointer->Name.Names[x].Active) + { + if (!Written) + { + memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); + MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); + MMF_Pointer->Name.Names[x].Active = 1; + Written = true; + } + continue; + } + + else if (MMF_Pointer->Name.Names[x].Process == GetCurrentProcessId()) + { + memset(&(MMF_Pointer->Name.Names[x]), 0, sizeof(MMF_Name_Single)); + if (!Written) + { + memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); + MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); + MMF_Pointer->Name.Names[x].Active = 1; + Written = true; + } + } + + else if (strcmp((const char*)&(MMF_Pointer->Name.Names[x].Name), Name.c_str()) == 0) + { + memset(&(MMF_Pointer->Name.Names[x]), 0, sizeof(MMF_Name_Single)); + if (!Written) + { + memcpy(&(MMF_Pointer->Name.Names[x].Name), Name.c_str(), strlen(Name.c_str()) + 1); + MMF_Pointer->Name.Names[x].Process = GetCurrentProcessId(); + MMF_Pointer->Name.Names[x].Active = 1; + Written = true; + } + } + } + CurrentName = Name; + MatchGroups(); + InterlockedExchange(&(MMF_Pointer->Name.ProcessID), 0); +} + +void MultiSend::Claim(uint32_t* Target, uint32_t Mod) +{ + uint32_t val = GetCurrentProcessId() + Mod; + int FailCount = 0; + InterlockedCompareExchange(Target, val, 0); + + while (*Target != val) + { + FailCount++; + if (FailCount == 50) + { + InterlockedExchange(Target, val); + return; + } + ::Sleep(1); + InterlockedCompareExchange(Target, val, 0); + } +} \ No newline at end of file diff --git a/src/MultiSend/Main.cpp b/src/MultiSend/Main.cpp new file mode 100644 index 0000000..e55fe9a --- /dev/null +++ b/src/MultiSend/Main.cpp @@ -0,0 +1,126 @@ +#include "MultiSend.h" +#pragma comment(lib, "psapi.lib") +#include +#include + +MultiSend::MultiSend(void) + : m_AshitaCore(NULL) + , m_PluginId(0) + , m_Direct3DDevice(NULL) +{ } +MultiSend::~MultiSend(void) +{ } + +plugininfo_t* g_PluginInfo = NULL; + +plugininfo_t MultiSend::GetPluginInfo(void) +{ + return (*g_PluginInfo); +} + +bool MultiSend::Initialize(IAshitaCore* core, ILogManager* log, uint32_t id) +{ + this->m_AshitaCore = core; + this->m_PluginId = id; + this->m_LogManager = log; + + LoadGroups(); + + MODULEINFO mod = { 0 }; + if (!::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandle("FFXiMain.dll"), &mod, sizeof(MODULEINFO))) + return false; + + unsigned char* Pointer = (unsigned char*)Ashita::Memory::FindPattern((uintptr_t)mod.lpBaseOfDll, (uintptr_t)mod.SizeOfImage, + "8BCFE8????FFFF8B0D????????E8????????8BE885ED750CB9", + 0, 0); + if (Pointer == NULL) return false; + + Pointer += 25; + StructPointer = (sFollow*)(*((DWORD*)Pointer)); + + //create a handle to the MMF, size matches the struct we're using for it + HANDLE hMapFile = CreateFileMapping( + INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, // maximum object size (high-order DWORD) + sizeof(MMF_Global), // maximum object size (low-order DWORD) + "FFXI_MultiSend"); + + bool made = (GetLastError() == 0); + + if (hMapFile == NULL) + { + throw exception("Could not open or create MMF."); + } + + + MMF_Pointer = (MMF_Global*)MapViewOfFile(hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof(MMF_Global)); + if (MMF_Pointer == NULL) + { + CloseHandle(hMapFile); + throw exception("Could not map MMF."); + } + + if (made) + { + memset(MMF_Pointer, 0, sizeof(MMF_Global)); + } + Position = MMF_Pointer->Command.Position; + + FollowEnabled = true; + ZoneExtra = false; + IgnoreSelf = false; + + CurrentName = ""; + if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) + { + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + UpdateName(std::string(m_AshitaCore->GetDataManager()->GetEntity()->GetName(myindex))); + } + + _Debug = false; + + this->Start(); + + return true; +} + +void MultiSend::Release(void) +{ + this->Stop(); + if (MMF_Pointer) + { + UnmapViewOfFile((LPCVOID)MMF_Pointer); + } + if (hMapFile) + { + CloseHandle(hMapFile); + } +} + +__declspec(dllexport) double __stdcall GetInterfaceVersion(void) +{ + return ASHITA_INTERFACE_VERSION; +} + +__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* lpBuffer) +{ + g_PluginInfo = lpBuffer; + + strcpy_s(g_PluginInfo->Name, sizeof(g_PluginInfo->Name), "MultiSend"); + strcpy_s(g_PluginInfo->Author, sizeof(g_PluginInfo->Author), "Thorny"); + + g_PluginInfo->InterfaceVersion = ASHITA_INTERFACE_VERSION; + g_PluginInfo->PluginVersion = 1.00f; + g_PluginInfo->Priority = 0; +} + +__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void) +{ + return (IPlugin*)new MultiSend(); +} diff --git a/src/MultiSend/MultiSend.h b/src/MultiSend/MultiSend.h new file mode 100644 index 0000000..32c025f --- /dev/null +++ b/src/MultiSend/MultiSend.h @@ -0,0 +1,109 @@ +#ifndef __ASHITA_MMF_H_INCLUDED__ +#define __ASHITA_MMF_H_INCLUDED__ + +#if defined (_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif +#define SAFE_DELETE(p) if(p) { delete p; p = NULL; } + +#include "C:\Ashita 3\Plugins\ADK\Ashita.h" +#include "Structs.h" +#include "..\..\pluginheaders\Utilities.h" +#include "..\..\pluginheaders\rapidxml.hpp" +#include +#include +#include + +using namespace std; + + +extern plugininfo_t* g_PluginInfo; + +struct GroupData +{ + bool Loaded; + char* rawdata; + rapidxml::xml_document<> doc; + std::map Map; +}; + +struct sFollow +{ // 42 bytes + unsigned int _unknown_0_4; // 0- 3 Looks like a pointer + unsigned short TargetIndex; // 4- 5+2 + unsigned int TargetID; // 8-11 + float DirX; // 12-15 + float DirY; // 16-19 + float DirZ; // 20-23 + unsigned char _unknown_24_27[4]; // 24-27 + unsigned int _unknown_28_31; // 28-31 Same as _unknown_0_4 + unsigned int FollowIndex; // 32-33+2 + unsigned int FollowID; // 36-39 Once set will overwrite DirX, DirZ and DirY with directional values + unsigned char _zero_40_40; // 40-40 + unsigned char Autorun; // 41-41 +}; + +class MultiSend : IPlugin, Ashita::Threading::AS_Thread +{ + IAshitaCore* m_AshitaCore; + ILogManager* m_LogManager; + DWORD m_PluginId; + IDirect3DDevice8* m_Direct3DDevice; + sFollow* StructPointer; + GroupData m_Group; + + HANDLE hMapFile; + MMF_Global* MMF_Pointer; + + uint32_t Position; + volatile bool ZoneExtra; + volatile bool Following; + volatile float PositionX; + volatile float PositionZ; + std::list Groups; + + std::string CurrentName; + + uint32_t Zoning; //0 = not zoning, 1 = waiting to zone, 2 = zoned and waiting to finish + volatile bool FollowEnabled; + volatile bool StopFollow; + volatile bool IgnoreSelf; + + volatile bool _Debug; + +public: + MultiSend(void); + virtual ~MultiSend(void); + +public: + plugininfo_t GetPluginInfo(void); + +public: + bool Initialize(IAshitaCore* core, ILogManager* log, uint32_t id); + void Release(void); + bool Direct3DInitialize(IDirect3DDevice8* lpDevice); + void Direct3DPreRender(void); + bool HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) override; + bool HandleCommand(const char* command, int32_t type); + uint32_t ThreadEntry(void) override; + + bool MatchID(uint32_t ID); + bool ReadCommand(); + void SetFollow(bool Active); + void SendCommand(uint32_t ID, const char* Command); + void SubValues(char* Input); + void UpdateName(std::string Name); + void Claim(uint32_t* Target, uint32_t Mod); + + void LoadGroups(); + void ReadXML(); + void MatchGroups(); + + uint32_t GetArg(const char* text, std::string* buffer); +}; + +__declspec(dllexport) double __stdcall GetInterfaceVersion(void); +__declspec(dllexport) void __stdcall CreatePluginInfo(plugininfo_t* lpBuffer); +__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void); + +#endif // __ASHITA_MMF_H_INCLUDED__ diff --git a/src/MultiSend/MultiSend.vcxproj b/src/MultiSend/MultiSend.vcxproj new file mode 100644 index 0000000..e427e45 --- /dev/null +++ b/src/MultiSend/MultiSend.vcxproj @@ -0,0 +1,156 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {3C74E1A4-E44B-4523-85DC-9A94AC2D6457} + MultiSend + + + + Application + true + v140 + MultiByte + + + DynamicLibrary + false + v140_xp + true + MultiByte + + + Application + true + v140 + MultiByte + + + DynamicLibrary + false + v140_xp + true + MultiByte + + + + + + + + + + + + + + + + + + + + + ..\..\bin\ + + + ..\..\bin\ + + + + Level3 + Disabled + true + + + Exports.def + + + + + Level3 + Disabled + true + + + Exports.def + + + + + Level3 + Full + false + true + true + true + true + Async + + + true + true + Exports.def + + + copy "..\..\bin\MultiSend.dll" "C:\Ashita 3\plugins\MultiSend.dll" + + + + + Level3 + Full + false + true + true + true + true + Async + + + true + true + Exports.def + + + copy "..\..\bin\MultiSend.dll" "C:\Ashita 3\plugins\MultiSend.dll" + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MultiSend/MultiSend.vcxproj.filters b/src/MultiSend/MultiSend.vcxproj.filters new file mode 100644 index 0000000..10142f1 --- /dev/null +++ b/src/MultiSend/MultiSend.vcxproj.filters @@ -0,0 +1,56 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;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 + + + + + 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/src/MultiSend/MultiSend.vcxproj.user b/src/MultiSend/MultiSend.vcxproj.user new file mode 100644 index 0000000..6fb136b --- /dev/null +++ b/src/MultiSend/MultiSend.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/MultiSend/Name.cpp b/src/MultiSend/Name.cpp new file mode 100644 index 0000000..e56c9ab --- /dev/null +++ b/src/MultiSend/Name.cpp @@ -0,0 +1,19 @@ +#include "MultiSend.h" + + +bool MultiSend::HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) +{ + if (id == 0x00A) + { + if (strcmp(CurrentName.c_str(), (const char*)data + 0x84)) + { + UpdateName(std::string((const char*)data + 0x84)); + } + } + if (id == 0x00B) + { + Zoning = 1; + } + + return false; +} \ No newline at end of file diff --git a/src/MultiSend/Structs.h b/src/MultiSend/Structs.h new file mode 100644 index 0000000..47f8aa1 --- /dev/null +++ b/src/MultiSend/Structs.h @@ -0,0 +1,49 @@ +#ifndef _STRUCTS_H_ +#define _STRUCTS_H_ + +#include + +struct MMF_Name_Single +{ + uint32_t Process; + uint8_t Name[64]; + uint8_t Active; +}; + +struct MMF_Name +{ + uint32_t ProcessID; + MMF_Name_Single Names[100]; +}; + +struct MMF_ICommand_Single +{ + uint8_t Active; + uint32_t Targets; + uint32_t SendProcess; + uint8_t Command[248]; +}; + +struct MMF_ICommand +{ + uint32_t ProcessID; + uint32_t Position; + MMF_ICommand_Single Command[100]; +}; + +struct MMF_IFollow +{ + uint16_t Zone; + uint32_t FollowID; + uint32_t Follow; + int32_t PosX; + int32_t PosZ; +}; + +struct MMF_Global +{ + MMF_Name Name; + MMF_ICommand Command; + MMF_IFollow Follow; +}; +#endif \ No newline at end of file diff --git a/src/MultiSend/Substitute.cpp b/src/MultiSend/Substitute.cpp new file mode 100644 index 0000000..063210a --- /dev/null +++ b/src/MultiSend/Substitute.cpp @@ -0,0 +1,63 @@ +#include "MultiSend.h" + +void MultiSend::SubValues(char* Input) +{ + std::string Working(Input); + + size_t find = Working.find("[me]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(0))); + } + + find = Working.find("[p0]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(0))); + } + + find = Working.find("[p1]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(1))); + } + + find = Working.find("[p2]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(2))); + } + + find = Working.find("[p3]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(3))); + } + + find = Working.find("[p4]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(4))); + } + + find = Working.find("[p5]"); + if (find != string::npos) + { + Working.replace(find, 4, std::to_string(m_AshitaCore->GetDataManager()->GetParty()->GetMemberServerId(5))); + } + + find = Working.find("[t]"); + if (find != string::npos) + { + unsigned int TargetIndex = m_AshitaCore->GetDataManager()->GetTarget()->GetTargetIndex(); + if (m_AshitaCore->GetDataManager()->GetTarget()->GetSubTargetActive()) + { + TargetIndex = m_AshitaCore->GetDataManager()->GetTarget()->GetSubTargetIndex(); + } + Working.replace(find, 3, std::to_string(m_AshitaCore->GetDataManager()->GetEntity()->GetServerId(TargetIndex))); + } + + const char* base = Working.c_str(); + memset(Input, 0, 248); + memcpy(Input, base, strlen(base)); +} \ No newline at end of file diff --git a/src/MultiSend/Thread.cpp b/src/MultiSend/Thread.cpp new file mode 100644 index 0000000..46b612b --- /dev/null +++ b/src/MultiSend/Thread.cpp @@ -0,0 +1,85 @@ +#include "MultiSend.h" + +uint32_t MultiSend::ThreadEntry(void) +{ + while (!this->IsTerminated()) + { + if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) + { + if (Zoning == 2) Zoning = 0; + } + else if (Zoning == 1) + { + Zoning = 2; + } + + if (MMF_Pointer->Follow.Follow) + { + if (((MMF_Global*)MMF_Pointer)->Follow.FollowID == GetCurrentProcessId()) + { + if (!Zoning) + { + Following = false; + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + int32_t mPosX = (int32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex) * 100); + int32_t mPosZ = (int32_t)floor(m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex) * 100); + MMF_Pointer->Follow.PosX = mPosX; + MMF_Pointer->Follow.PosZ = mPosZ; + MMF_Pointer->Follow.Follow = 1; + MMF_Pointer->Follow.Zone = m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0); + } + else + { + Following = false; + MMF_Pointer->Follow.Follow = 2; + } + } + else if (MMF_Pointer->Follow.Follow != 3) + { + if (m_AshitaCore->GetDataManager()->GetParty()->GetMemberActive(0)) + { + if (MMF_Pointer->Follow.Follow == 2) + { + if (!ZoneExtra) + { + Following = false; + uint16_t myindex = m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0); + float MyX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(myindex); + float MyZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(myindex); + PositionX = (float)MMF_Pointer->Follow.PosX / 100.0f; + PositionZ = (float)MMF_Pointer->Follow.PosZ / 100.0f; + float Scale = 1.0f + (8.0f / sqrt(pow(PositionX - MyX, 2) + pow(PositionZ - MyZ, 2))); + PositionX = (PositionX * Scale) + MyX; + PositionZ = (PositionZ * Scale) + MyZ; + ZoneExtra = true; + Following = true; + } + } + else + { + ZoneExtra = false; + PositionX = (float)MMF_Pointer->Follow.PosX / 100.0f; + PositionZ = (float)MMF_Pointer->Follow.PosZ / 100.0f; + Following = true; + } + } + } + else + { + if (Following) StopFollow = true; + Following = false; + } + } + else + { + if (Following) StopFollow = true; + Following = false; + } + + while (ReadCommand()) {} + + ::Sleep(1); + } + return 0; +} +