diff --git a/MQ2Nav.sln b/MQ2Nav.sln
index bcbdbd19..70eb888d 100644
--- a/MQ2Nav.sln
+++ b/MQ2Nav.sln
@@ -20,6 +20,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imgui", "..\..\src\imgui\im
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zone-utilities", "dependencies\zone-utilities\zone-utilities.vcxproj", "{200FB60C-6C01-48A7-886A-8E3683EB21BC}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "routing", "..\..\src\routing\routing.vcxproj", "{6CE4F8D6-1709-47C5-9297-1619BBC4A71E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -80,6 +82,14 @@ Global
{200FB60C-6C01-48A7-886A-8E3683EB21BC}.Release|Win32.Build.0 = Release|Win32
{200FB60C-6C01-48A7-886A-8E3683EB21BC}.Release|x64.ActiveCfg = Release|x64
{200FB60C-6C01-48A7-886A-8E3683EB21BC}.Release|x64.Build.0 = Release|x64
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Debug|Win32.Build.0 = Debug|Win32
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Debug|x64.ActiveCfg = Debug|x64
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Debug|x64.Build.0 = Debug|x64
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Release|Win32.ActiveCfg = Release|Win32
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Release|Win32.Build.0 = Release|Win32
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Release|x64.ActiveCfg = Release|x64
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -93,6 +103,7 @@ Global
{A219F273-6B9D-4284-84F4-368539F0A9CF} = {1C00A77D-99DC-4366-8569-E75827AAF729}
{1777E251-0F50-496A-B8C5-EC7F41A0B186} = {2884B755-835B-49DA-9E3F-E34AD401A5B9}
{200FB60C-6C01-48A7-886A-8E3683EB21BC} = {2884B755-835B-49DA-9E3F-E34AD401A5B9}
+ {6CE4F8D6-1709-47C5-9297-1619BBC4A71E} = {2884B755-835B-49DA-9E3F-E34AD401A5B9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1498B1E1-4805-4819-A304-0BE8FDA62FD8}
diff --git a/common/MQ2Nav_Common.vcxproj b/common/MQ2Nav_Common.vcxproj
index 82bdcba0..e755fdda 100644
--- a/common/MQ2Nav_Common.vcxproj
+++ b/common/MQ2Nav_Common.vcxproj
@@ -145,6 +145,7 @@
+
@@ -154,6 +155,7 @@
+
4244;4256
@@ -161,6 +163,7 @@
+
Document
diff --git a/common/MQ2Nav_Common.vcxproj.filters b/common/MQ2Nav_Common.vcxproj.filters
index 877d0699..3c916cb5 100644
--- a/common/MQ2Nav_Common.vcxproj.filters
+++ b/common/MQ2Nav_Common.vcxproj.filters
@@ -51,6 +51,9 @@
Header Files
+
+ Proto Files\Generated Files
+
@@ -74,11 +77,17 @@
Source Files
+
+ Proto Files\Generated Files
+
Proto Files
+
+ Proto Files
+
diff --git a/common/proto/NavMeshClient.proto b/common/proto/NavMeshClient.proto
new file mode 100644
index 00000000..05fa6878
--- /dev/null
+++ b/common/proto/NavMeshClient.proto
@@ -0,0 +1,28 @@
+syntax = "proto3";
+
+package nav.client;
+
+import "NavMeshFile.proto";
+
+message UpdateTransform
+{
+ string shortzone = 1;
+ nav.vector3 position = 2;
+ float heading = 3;
+
+ string character_info = 10;
+}
+
+message DropClient
+{
+ string character_info = 1;
+}
+
+message ClientMessage
+{
+ oneof message
+ {
+ UpdateTransform transform = 1;
+ DropClient drop = 2;
+ }
+}
diff --git a/meshgen/Actor.cpp b/meshgen/Actor.cpp
new file mode 100644
index 00000000..c5e32554
--- /dev/null
+++ b/meshgen/Actor.cpp
@@ -0,0 +1,90 @@
+//
+// Actor.cpp
+//
+
+#include "Application.h"
+
+#include "routing/Routing.h"
+#include "common/proto/NavMeshClient.pb.h"
+
+using namespace mq;
+
+mq::ProtoPipeClient* s_pipeClient = nullptr; // { mq::MQ2_PIPE_SERVER_PATH };
+
+class PipeEventsHandler : public NamedPipeEvents
+{
+public:
+ PipeEventsHandler() {}
+
+ virtual void OnIncomingMessage(PipeMessagePtr&& message) override
+ {
+ switch (message->GetMessageId())
+ {
+ case MQMessageId::MSG_ROUTE:
+ {
+ auto envelope = ProtoMessage::Parse(message);
+ if (envelope.has_payload())
+ {
+ nav::client::ClientMessage client_message;
+ client_message.ParseFromString(envelope.payload());
+
+ switch (client_message.message_case())
+ {
+ case nav::client::ClientMessage::kTransform:
+ ClientInformation::UpdateTransform(client_message.transform());
+ break;
+ case nav::client::ClientMessage::kDrop:
+ ClientInformation::DropCharacter(client_message.drop().character_info());
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ case MQMessageId::MSG_IDENTIFICATION:
+ {
+ // the launcher wants identification
+ proto::routing::Identification id;
+ id.set_name("meshgen");
+ id.set_pid(GetCurrentProcessId());
+ s_pipeClient->SendProtoMessage(MQMessageId::MSG_IDENTIFICATION, id);
+ break;
+ }
+
+ default: break;
+ }
+ }
+
+ virtual void OnClientConnected() override
+ {
+ // Send an ID message to the launcher
+ proto::routing::Identification id;
+ id.set_name("meshgen");
+ id.set_pid(GetCurrentProcessId());
+ s_pipeClient->SendProtoMessage(MQMessageId::MSG_IDENTIFICATION, id);
+ }
+};
+
+void StopPipeClient()
+{
+ s_pipeClient->Stop();
+
+ delete s_pipeClient;
+ s_pipeClient = nullptr;
+}
+
+void StartPipeClient()
+{
+ s_pipeClient = new mq::ProtoPipeClient(mq::MQ2_PIPE_SERVER_PATH);
+ s_pipeClient->SetHandler(std::make_shared());
+ s_pipeClient->Start();
+ ::atexit(StopPipeClient);
+}
+
+void ProcessPipeClient()
+{
+ if (s_pipeClient != nullptr)
+ s_pipeClient->Process();
+}
diff --git a/meshgen/Application.cpp b/meshgen/Application.cpp
index 202f73d3..5b1f3a4d 100644
--- a/meshgen/Application.cpp
+++ b/meshgen/Application.cpp
@@ -13,6 +13,7 @@
#include "meshgen/imgui/imgui_impl_opengl2.h"
#include "meshgen/imgui/imgui_impl_sdl.h"
#include "common/Utilities.h"
+#include "common/proto/NavMeshClient.pb.h"
#include "imgui/ImGuiUtils.h"
@@ -41,6 +42,8 @@ namespace fs = std::filesystem;
static const int32_t MAX_LOG_MESSAGES = 1000;
+static ClientInformation s_clientInformation;
+
static bool IsKeyboardBlocked() {
return ImGui::GetIO().WantCaptureKeyboard || ImGui::GetIO().WantTextInput;
}
@@ -296,6 +299,7 @@ int Application::RunMainLoop()
m_raye = glm::unProject(glm::vec3{ m_m.x, m_m.y, 1.0f }, m_model, m_proj, m_view);
DispatchCallbacks();
+ ProcessPipeClient();
// Handle input events.
HandleEvents();
@@ -773,6 +777,20 @@ void Application::RenderInterface()
m_cam.z = camPos[0];
}
+ if (!s_clientInformation.m_transforms.empty())
+ {
+ ImGui::Separator();
+ ImGui::Text("Connected Character Information:");
+ for (const auto& [charInfo, xform] : s_clientInformation.m_transforms)
+ {
+ ImGui::Text(
+ "[%s] %s x: %.2f y: %.2f z: %.2f h: %.2f",
+ charInfo.c_str(), xform.shortzone().c_str(),
+ xform.position().x(), xform.position().y(),
+ xform.position().z(), xform.heading());
+ }
+ }
+
if (m_geom)
{
auto* loader = m_geom->getMeshLoader();
@@ -1427,3 +1445,31 @@ void ImportExportSettingsDialog::Show(bool* open /* = nullptr */)
m_firstShow = false;
}
+
+ClientInformation::ClientInformation()
+{
+ StartPipeClient();
+}
+
+ClientInformation::~ClientInformation()
+{
+ StopPipeClient();
+}
+
+void ClientInformation::UpdateTransform(const nav::client::UpdateTransform& transform)
+{
+ std::string characterInfo = transform.character_info();
+
+ auto it = s_clientInformation.m_transforms.find(transform.character_info());
+ if (it != s_clientInformation.m_transforms.end())
+ it->second = transform;
+ else
+ s_clientInformation.m_transforms.emplace(characterInfo, transform);
+}
+
+void ClientInformation::DropCharacter(const std::string& characterInfo)
+{
+ auto it = s_clientInformation.m_transforms.find(characterInfo);
+ if (it != s_clientInformation.m_transforms.end())
+ s_clientInformation.m_transforms.erase(it);
+}
diff --git a/meshgen/Application.h b/meshgen/Application.h
index a71c4456..6fb83630 100644
--- a/meshgen/Application.h
+++ b/meshgen/Application.h
@@ -332,3 +332,28 @@ class ImportExportSettingsDialog
PersistedDataFields m_fields = PersistedDataFields::All
& ~PersistedDataFields::MeshTiles;
};
+
+//----------------------------------------------------------------------------
+
+namespace nav::client {
+class UpdateTransform;
+}
+
+class ClientInformation
+{
+ friend class Application;
+
+public:
+ ClientInformation();
+ ~ClientInformation();
+
+ static void UpdateTransform(const nav::client::UpdateTransform& transform);
+ static void DropCharacter(const std::string& characterInfo);
+
+private:
+ std::unordered_map m_transforms;
+};
+
+void StopPipeClient();
+void StartPipeClient();
+void ProcessPipeClient();
diff --git a/meshgen/MeshGenerator.vcxproj b/meshgen/MeshGenerator.vcxproj
index b8ed34b9..c793f1e0 100644
--- a/meshgen/MeshGenerator.vcxproj
+++ b/meshgen/MeshGenerator.vcxproj
@@ -32,6 +32,7 @@
Create
Create
+
@@ -61,6 +62,9 @@
{1777e251-0f50-496a-b8c5-ec7f41a0b186}
+
+ {6ce4f8d6-1709-47c5-9297-1619bbc4a71e}
+
{45e99b43-f47b-4aad-ac88-f95bf467f463}
diff --git a/meshgen/MeshGenerator.vcxproj.filters b/meshgen/MeshGenerator.vcxproj.filters
index de442118..fcf8e516 100644
--- a/meshgen/MeshGenerator.vcxproj.filters
+++ b/meshgen/MeshGenerator.vcxproj.filters
@@ -86,6 +86,9 @@
Source Files\tools
+
+ Source Files
+
diff --git a/plugin/MQ2Nav.vcxproj b/plugin/MQ2Nav.vcxproj
index ae698c03..cccf4cf4 100644
--- a/plugin/MQ2Nav.vcxproj
+++ b/plugin/MQ2Nav.vcxproj
@@ -140,6 +140,7 @@
+
Create
@@ -167,6 +168,7 @@
+
diff --git a/plugin/MQ2Nav.vcxproj.filters b/plugin/MQ2Nav.vcxproj.filters
index 55242210..70fcb403 100644
--- a/plugin/MQ2Nav.vcxproj.filters
+++ b/plugin/MQ2Nav.vcxproj.filters
@@ -69,6 +69,9 @@
Source Files
+
+ Source Files
+
@@ -140,6 +143,9 @@
Header Files
+
+ Header Files
+
diff --git a/plugin/MQ2Navigation.cpp b/plugin/MQ2Navigation.cpp
index 9daa1713..a1df0341 100644
--- a/plugin/MQ2Navigation.cpp
+++ b/plugin/MQ2Navigation.cpp
@@ -16,6 +16,7 @@
#include "plugin/NavMeshRenderer.h"
#include "plugin/RenderHandler.h"
#include "plugin/SwitchHandler.h"
+#include "plugin/MeshgenHandler.h"
#include "plugin/UiController.h"
#include "plugin/Utilities.h"
#include "plugin/Waypoints.h"
@@ -562,6 +563,7 @@ void MQ2NavigationPlugin::Plugin_Initialize()
AddModule();
AddModule();
AddModule();
+ AddModule();
for (const auto& m : m_modules)
{
diff --git a/plugin/MeshgenHandler.cpp b/plugin/MeshgenHandler.cpp
new file mode 100644
index 00000000..92706105
--- /dev/null
+++ b/plugin/MeshgenHandler.cpp
@@ -0,0 +1,86 @@
+//
+// MeshgenHandler.cpp
+//
+
+#include "pch.h"
+#include "MeshgenHandler.h"
+
+#include "common/proto/NavMeshClient.pb.h"
+
+#include
+#include
+
+#include
+
+void MeshgenHandler::Initialize()
+{
+ if (pLocalPC != nullptr)
+ {
+ m_characterInfo = fmt::format("{}_{}", GetServerShortName(), pLocalPC->Name);
+ to_lower(m_characterInfo);
+ }
+}
+
+void MeshgenHandler::Shutdown()
+{
+ postoffice::Address address;
+ address.Name = "meshgen";
+
+ nav::client::DropClient drop;
+ drop.set_character_info(m_characterInfo);
+
+ nav::client::ClientMessage message;
+ *message.mutable_drop() = drop;
+
+ postoffice::SendToActor(address, message);
+}
+
+void MeshgenHandler::OnPulse()
+{
+ using clock = std::chrono::system_clock;
+ using namespace std::chrono_literals;
+
+ static clock::time_point last_update = clock::now();
+ static constexpr clock::duration update_interval = 1s;
+
+ if (!m_characterInfo.empty() && pZoneInfo != nullptr && pLocalPC != nullptr)
+ {
+ auto now = clock::now();
+ if (now >= last_update + update_interval)
+ {
+ nav::client::UpdateTransform transform;
+ transform.set_character_info(m_characterInfo);
+ transform.set_shortzone(pZoneInfo->ShortName);
+
+ nav::vector3& position = *transform.mutable_position();
+ position.set_x(pLocalPlayer->X);
+ position.set_y(pLocalPlayer->Y);
+ position.set_z(pLocalPlayer->Z);
+
+ transform.set_heading(pLocalPlayer->Heading);
+
+ postoffice::Address address;
+ address.Name = "meshgen";
+
+ nav::client::ClientMessage message;
+ *message.mutable_transform() = transform;
+
+ postoffice::SendToActor(address, message);
+
+ last_update = now;
+ }
+ }
+}
+
+void MeshgenHandler::SetGameState(int gameState)
+{
+ if (pLocalPC != nullptr)
+ {
+ m_characterInfo = fmt::format("{}_{}", GetServerShortName(), pLocalPC->Name);
+ to_lower(m_characterInfo);
+ }
+ else
+ {
+ m_characterInfo.clear();
+ }
+}
diff --git a/plugin/MeshgenHandler.h b/plugin/MeshgenHandler.h
new file mode 100644
index 00000000..c2475dd9
--- /dev/null
+++ b/plugin/MeshgenHandler.h
@@ -0,0 +1,23 @@
+//
+// MeshgenHandler.h
+//
+
+#pragma once
+
+#include "common/NavModule.h"
+
+#include
+
+class MeshgenHandler : public NavModule
+{
+public:
+ void Initialize() override;
+ void Shutdown() override;
+
+ void OnPulse() override;
+
+ void SetGameState(int gameState) override;
+
+private:
+ std::string m_characterInfo;
+};
\ No newline at end of file