You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
594 lines
17 KiB
594 lines
17 KiB
#include "Servo.h" |
|
#include <sstream> |
|
|
|
#pragma comment(lib, "Psapi.lib") |
|
#include <Psapi.h> |
|
|
|
#include <algorithm> |
|
#include <functional> |
|
#include <cctype> |
|
#include <locale> |
|
|
|
// trim from start (in place) |
|
static inline void ltrim(std::string &s) { |
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), |
|
std::not1(std::ptr_fun<int, int>(std::isspace)))); |
|
} |
|
|
|
// trim from end (in place) |
|
static inline void rtrim(std::string &s) { |
|
s.erase(std::find_if(s.rbegin(), s.rend(), |
|
std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end()); |
|
} |
|
|
|
// trim from both ends (in place) |
|
static inline void trim(std::string &s) { |
|
ltrim(s); |
|
rtrim(s); |
|
} |
|
|
|
// trim from start (copying) |
|
static inline std::string ltrimmed(std::string s) { |
|
ltrim(s); |
|
return s; |
|
} |
|
|
|
// trim from end (copying) |
|
static inline std::string rtrimmed(std::string s) { |
|
rtrim(s); |
|
return s; |
|
} |
|
|
|
// trim from both ends (copying) |
|
static inline std::string trimmed(std::string s) { |
|
trim(s); |
|
return s; |
|
} |
|
|
|
|
|
|
|
std::string Servo::StringSub(std::string instring, std::string subin, std::string subout) |
|
{ |
|
if (instring.find(subin) != std::string::npos) |
|
return(instring.replace(instring.find(subin), subin.length(), subout)); |
|
else |
|
return instring; |
|
} |
|
|
|
void Servo::formatCommand(std::string* command) |
|
{ |
|
std::stringstream id; |
|
id << m_AshitaCore->GetDataManager()->GetTarget()->GetTargetServerId(); |
|
|
|
*command = StringSub(*command, "[me]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0)); |
|
*command = StringSub(*command, "[p0]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0)); |
|
*command = StringSub(*command, "[p1]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(1)); |
|
*command = StringSub(*command, "[p2]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(2)); |
|
*command = StringSub(*command, "[p3]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(3)); |
|
*command = StringSub(*command, "[p4]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(4)); |
|
*command = StringSub(*command, "[p5]", m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(5)); |
|
*command = StringSub(*command, "[t]", id.str()); |
|
} |
|
|
|
void Servo::SendCommand(std::string command) |
|
{ |
|
while (!lock.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
|
buffer.push_back(command); |
|
lock.unlock(); |
|
} |
|
|
|
void Servo::WalkTo(float x, float z) |
|
{ |
|
if (zoning) |
|
return; |
|
|
|
auto myX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); |
|
auto myZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); |
|
auto myStatus = m_AshitaCore->GetDataManager()->GetEntity()->GetStatus(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); |
|
float dist = sqrt(pow((myX - x), 2.0f) + pow((myZ - z), 2.0f)); |
|
if (dist > 1.5f && (myStatus == 0 || myStatus == 5)) |
|
{ |
|
if (StructPointer->AutoRun != 1) |
|
StructPointer->AutoRun = 1; |
|
StructPointer->DirectionX = x - myX; |
|
StructPointer->DirectionZ = z - myZ; |
|
StructPointer->DirectionY = 0; |
|
} |
|
else |
|
StructPointer->AutoRun = 0; |
|
} |
|
|
|
void Servo::RecvCommand(std::string* command) |
|
{ |
|
command->erase(); |
|
if (lock.try_lock()) |
|
{ |
|
if (!buffer.empty()) |
|
{ |
|
command->swap(buffer.front()); |
|
buffer.pop_front(); |
|
} |
|
lock.unlock(); |
|
} |
|
} |
|
|
|
void Servo::SetState(ServoMode::Mode mode) |
|
{ |
|
ServoMode::Mode prevState = state; |
|
state = ServoMode::Mode::Standby; |
|
follow = false; |
|
zoning = false; |
|
connected = false; |
|
if (prevState != ServoMode::Mode::Standby) |
|
{ |
|
conn.join(); |
|
pos.join(); |
|
} |
|
while (!buffer.empty()) |
|
buffer.pop_front(); |
|
state = mode; |
|
} |
|
|
|
void Servo::Sync(std::string host) |
|
{ |
|
me = m_AshitaCore->GetDataManager()->GetParty()->GetMemberName(0); |
|
log << Chat::Format::RoyalBlue << "Servo started in client mode, connected to: "; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (host != "") |
|
{ |
|
servAddr = host; |
|
log << servAddr; |
|
} |
|
else |
|
{ |
|
servAddr = ""; |
|
log << "localhost"; |
|
} |
|
lservAddr.unlock(); |
|
|
|
SetState(ServoMode::Mode::Client); |
|
conn.swap(std::thread(&Servo::Client, this)); |
|
pos.swap(std::thread(&Servo::Follow, this)); |
|
log << Chat::Control::flush; |
|
} |
|
|
|
bool Servo::Initialize(IAshitaCore* ashitaCore, ILogManager* logManager, uint32_t dwPluginId) |
|
{ |
|
this->m_AshitaCore = ashitaCore; |
|
this->m_PluginId = dwPluginId; |
|
|
|
// Obtain the base module information.. |
|
MODULEINFO mod = { 0 }; |
|
if (!::GetModuleInformation(::GetCurrentProcess(), ::GetModuleHandle("FFXiMain.dll"), &mod, sizeof(MODULEINFO))) |
|
return false; |
|
|
|
// Cast the data and scan for the pattern.. |
|
std::vector<uint8_t> data((uint8_t*)(uintptr_t)mod.lpBaseOfDll, (uint8_t*)(uintptr_t)mod.lpBaseOfDll + mod.SizeOfImage); |
|
|
|
unsigned char* Pointer = (unsigned char*)Ashita::Memory::FindPattern(std::ref(data), (uintptr_t)mod.lpBaseOfDll, "8BCDE87F7????F8B0D7F7F7F7FE87F7F7F7F8BF885??750CB9", 0, 0); |
|
StructPointer = NULL; |
|
if (Pointer != NULL) |
|
{ |
|
|
|
Pointer += 25; |
|
StructPointer = (auto_follow*)(*((DWORD*)Pointer)); |
|
} |
|
|
|
if (StructPointer) |
|
{ |
|
StructPointer->DirectionX; |
|
StructPointer->DirectionZ; |
|
StructPointer->AutoRun; |
|
} |
|
|
|
cmdParse = new CommandParser; |
|
state = ServoMode::Mode::Standby; |
|
lservAddr.lock(); |
|
servAddr = ""; |
|
lservAddr.unlock(); |
|
me = ""; |
|
follow = false; |
|
zoning = false; |
|
|
|
zContext = zmq::context_t(1); |
|
conn = std::thread(); |
|
pos = std::thread(); |
|
|
|
log.SetCore(m_AshitaCore); |
|
log << Chat::Mode::Echo; |
|
|
|
return 1; |
|
} |
|
|
|
void Servo::Serv() |
|
{ |
|
zmq::socket_t publisher(zContext, ZMQ_PUB); |
|
std::string hostmask = "tcp://"; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (!servAddr.empty()) |
|
hostmask.append(servAddr); |
|
else |
|
hostmask.append("127.0.0.1"); |
|
lservAddr.unlock(); |
|
hostmask.append(":56556"); |
|
publisher.bind(hostmask.c_str()); |
|
|
|
connected = true; |
|
|
|
while (connected) { |
|
while (!lock.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (!buffer.empty()) |
|
{ |
|
auto it = buffer.begin(); |
|
zmq::message_t message(it->length() + 1); |
|
strcpy_s((char*)message.data(), (size_t)(it->length() + 1), it->c_str()); |
|
buffer.pop_front(); |
|
lock.unlock(); |
|
publisher.send(message, ZMQ_DONTWAIT); |
|
} |
|
else |
|
lock.unlock(); |
|
std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
|
} |
|
publisher.close(); |
|
} |
|
|
|
void Servo::ServPos() |
|
{ |
|
zmq::socket_t publisher(zContext, ZMQ_PUB); |
|
std::string hostmask = "tcp://"; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (!servAddr.empty()) |
|
hostmask.append(servAddr); |
|
else |
|
hostmask.append("127.0.0.1"); |
|
lservAddr.unlock(); |
|
hostmask.append(":56557"); |
|
publisher.bind(hostmask.c_str()); |
|
|
|
if (!connected) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
|
|
while (connected) { |
|
if (!zoning && m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)) |
|
{ |
|
auto myX = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalX(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); |
|
auto myZ = m_AshitaCore->GetDataManager()->GetEntity()->GetLocalZ(m_AshitaCore->GetDataManager()->GetParty()->GetMemberTargetIndex(0)); |
|
|
|
std::stringstream ss; |
|
ss << m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0) << " " << myX << " " << myZ; |
|
zmq::message_t message(strlen(ss.str().c_str()) + 1); |
|
strcpy_s((char*)message.data(), strlen(ss.str().c_str()) + 1, ss.str().c_str()); |
|
publisher.send(message, ZMQ_DONTWAIT); |
|
} |
|
std::this_thread::sleep_for(std::chrono::milliseconds(200)); |
|
} |
|
publisher.close(); |
|
} |
|
|
|
void Servo::Client() |
|
{ |
|
std::string host = "tcp://"; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (!servAddr.empty()) |
|
host.append(servAddr); |
|
else |
|
host.append("localhost"); |
|
lservAddr.unlock(); |
|
host.append(":56556"); |
|
zmq::socket_t subscriber(zContext, ZMQ_SUB); |
|
subscriber.connect(host.c_str()); |
|
subscriber.setsockopt(ZMQ_SUBSCRIBE, me.c_str(), me.size()); |
|
for (auto it = groups.begin(); it != groups.end(); ++it) |
|
subscriber.setsockopt(ZMQ_SUBSCRIBE, it->c_str(), it->size()); |
|
subscriber.setsockopt(ZMQ_SUBSCRIBE, "::", 2); |
|
subscriber.setsockopt(ZMQ_SUBSCRIBE, ":SERVOFUNC:", 11); |
|
connected = true; |
|
|
|
while (connected) |
|
{ |
|
zmq::message_t update; |
|
|
|
while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) |
|
{ |
|
if (connected) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
|
else |
|
break; |
|
} |
|
|
|
std::string s(static_cast<char*>(update.data())); |
|
if (!s.empty()) |
|
{ |
|
while (!lock.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
buffer.push_back(s); |
|
lock.unlock(); |
|
} |
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
|
} |
|
subscriber.close(); |
|
} |
|
|
|
void Servo::Follow() |
|
{ |
|
std::string host = "tcp://"; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (!servAddr.empty()) |
|
host.append(servAddr); |
|
else |
|
host.append("localhost"); |
|
lservAddr.unlock(); |
|
host.append(":56557"); |
|
zmq::socket_t subscriber(zContext, ZMQ_SUB); |
|
subscriber.connect(host.c_str()); |
|
subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0); |
|
|
|
if (!connected) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
|
|
while (connected) |
|
{ |
|
zmq::message_t update; |
|
while (follow) |
|
{ |
|
update = zmq::message_t(); |
|
while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) |
|
{ |
|
if (connected) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
else |
|
break; |
|
} |
|
std::stringstream ss(static_cast<char*>(update.data())); |
|
if (!ss.str().empty()) |
|
{ |
|
float WARPX, WARPY; |
|
short zoneid; |
|
ss >> zoneid >> WARPX >> WARPY; |
|
if (zoneid == m_AshitaCore->GetDataManager()->GetParty()->GetMemberZone(0)) |
|
WalkTo(WARPX, WARPY); |
|
else |
|
StructPointer->AutoRun = 0; // If no longer sharing the same zone, stop running (will trigger after the server char has arrived in the new area, so there is some delay) |
|
} |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
} |
|
|
|
while (subscriber.recv(&update, ZMQ_DONTWAIT) == false) |
|
{ |
|
if (connected & !follow) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
else |
|
break; |
|
} |
|
|
|
update.data(); // Discard data when not following. |
|
std::this_thread::sleep_for(std::chrono::milliseconds(500)); |
|
} |
|
subscriber.close(); |
|
} |
|
|
|
void Servo::Release() |
|
{ |
|
SetState(ServoMode::Mode::Standby); |
|
zContext.close(); |
|
delete cmdParse; |
|
} |
|
|
|
plugininfo_t Servo::GetPluginInfo(void) |
|
{ |
|
return (*g_PluginInfo); |
|
} |
|
|
|
bool Servo::HandleCommand(const char* szCommand, int iType) |
|
{ |
|
if (iType == 0) |
|
{ |
|
return false; |
|
} |
|
|
|
char szClean[1024]; |
|
if (iType != -2) |
|
m_AshitaCore->GetChatManager()->ParseAutoTranslate(szCommand, szClean, 1024, false); |
|
else |
|
strncpy_s(szClean, szCommand, 1024); |
|
|
|
std::string arg; |
|
|
|
cmdParse->InputCommand(szClean); |
|
cmdParse->GetFirstCommand(&arg); |
|
|
|
if (arg == "/servo") |
|
{ |
|
cmdParse->GetNextCommand(&arg); |
|
if (arg == "on") |
|
{ |
|
log << Chat::Format::RoyalBlue << "Servo started in server mode, with a hostmask of: "; |
|
while (!lservAddr.try_lock()) |
|
std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
|
if (cmdParse->GetNextCommand(&arg)) |
|
{ |
|
servAddr = arg; |
|
log << servAddr; |
|
} |
|
else |
|
{ |
|
servAddr = ""; |
|
log << "localhost"; |
|
} |
|
lservAddr.unlock(); |
|
|
|
SetState(ServoMode::Mode::Server); |
|
conn.swap(std::thread(&Servo::Serv, this)); |
|
pos.swap(std::thread(&Servo::ServPos, this)); |
|
log << Chat::Control::flush; |
|
} |
|
else if (arg == "sync") |
|
{ |
|
if (!cmdParse->GetNextCommand(&arg)) |
|
arg = ""; |
|
Sync(arg); |
|
} |
|
else if (arg == "off") |
|
{ |
|
SetState(ServoMode::Mode::Standby); |
|
log << Chat::Format::RoyalBlue << "Servo set to standy!" << Chat::Control::flush; |
|
} |
|
else if (arg == "command" || arg == "send") |
|
{ |
|
if (cmdParse->GetRemainingCommands(&arg)) |
|
{ |
|
arg = ":: " + arg; |
|
formatCommand(&arg); |
|
SendCommand(arg); |
|
} |
|
|
|
} |
|
else if (arg == "sendto") |
|
{ |
|
if (cmdParse->GetRemainingCommands(&arg)) |
|
{ |
|
formatCommand(&arg); |
|
SendCommand(arg); |
|
} |
|
|
|
} |
|
else if (arg == "followme") |
|
{ |
|
if (state.load() == ServoMode::Mode::Server) |
|
{ |
|
if (!cmdParse->GetNextCommand(&arg)) |
|
{ |
|
if (arg == "on") |
|
SendCommand(":SERVOFUNC: FOLLOWON"); |
|
else if (arg == "off") |
|
SendCommand(":SERVOFUNC: FOLLOWOFF"); |
|
} |
|
else |
|
SendCommand(":SERVOFUNC: FOLLOW"); |
|
} |
|
} |
|
else if (arg == "follow") |
|
{ |
|
if (state.load() == ServoMode::Mode::Client) |
|
{ |
|
if (!cmdParse->GetNextCommand(&arg)) |
|
{ |
|
if (arg == "on") |
|
follow = true; |
|
else if (arg == "off") |
|
follow = false; |
|
} |
|
else |
|
follow = follow ? false : true; |
|
} |
|
} |
|
else if (arg == "addgroup") |
|
{ |
|
std::string group; |
|
cmdParse->GetNextCommand(&group); |
|
groups.push_back(group); |
|
if (state == ServoMode::Mode::Client) |
|
Sync(servAddr); |
|
} |
|
else if (arg == "delgroup") |
|
{ |
|
bool changed = false; |
|
std::string group; |
|
cmdParse->GetNextCommand(&group); |
|
for (auto it = groups.begin(); it != groups.end();) |
|
{ |
|
if (*it == group) |
|
{ |
|
changed = true; |
|
it = groups.erase(it); |
|
} |
|
else |
|
++it; |
|
} |
|
if (state == ServoMode::Mode::Client && changed) |
|
Sync(servAddr); |
|
} |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool Servo::Direct3DInitialize(IDirect3DDevice8* mDevice) |
|
{ |
|
return true; |
|
} |
|
|
|
void Servo::Direct3DRender() |
|
{ |
|
if (state.load() == ServoMode::Mode::Client) |
|
{ |
|
std::string commandtype; |
|
std::string incommand; |
|
RecvCommand(&incommand); |
|
if (!incommand.empty()) |
|
{ |
|
commandtype = incommand.substr(0, incommand.find_first_of(" ")); |
|
incommand = incommand.substr(incommand.find_first_of(" ") + 1); |
|
if (commandtype != ":SERVOFUNC:") |
|
{ |
|
auto cmd = incommand; |
|
trim(cmd); |
|
m_AshitaCore->GetChatManager()->QueueCommand(cmd.c_str(), (int32_t)Ashita::CommandInputType::Menu); |
|
} |
|
else |
|
{ |
|
if (incommand == "FOLLOWON") |
|
follow = true; |
|
else if (incommand == "FOLLOWOFF") |
|
follow = false; |
|
else if (incommand == "FOLLOW") |
|
follow = follow ? false : true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
bool Servo::HandleOutgoingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) |
|
{ |
|
if (id == 0x5E) |
|
zoning = true; |
|
return false; |
|
} |
|
|
|
bool Servo::HandleIncomingPacket(uint16_t id, uint32_t size, void* data, void* modified, bool blocked) |
|
{ |
|
if (id == 0x0A) |
|
zoning = false; |
|
return false; |
|
} |
|
|
|
__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), "Servo"); |
|
strcpy_s(g_PluginInfo->Author, sizeof(g_PluginInfo->Author), "bluekirby0"); |
|
|
|
g_PluginInfo->InterfaceVersion = ASHITA_INTERFACE_VERSION; |
|
g_PluginInfo->PluginVersion = 3.00f; |
|
g_PluginInfo->Priority = 0; |
|
} |
|
|
|
__declspec(dllexport) IPlugin* __stdcall CreatePlugin(void) |
|
{ |
|
return (IPlugin*)new Servo(); |
|
}
|
|
|