Commit 04fd752c authored by Red Bot's avatar Red Bot

MQ2Discord (again)

parent b0a0fc6d
#pragma once
#include "SafeQueue.h"
#include <string>
#include <thread>
#include <atomic>
// Queue for messages to be sent to discord channel
extern SafeQueue<std::string> toDiscord;
// Queue for messages to be displayed in chat
extern SafeQueue<std::string> fromDiscord;
extern std::thread * g_pThread;
extern std::atomic_bool bRun;
extern std::atomic_bool bFinished;
void DiscordThread(std::string token, std::string channelId, std::string controlUserId, std::string connectMsg);
#include "DiscordClient.h"
#include <nlohmann\json.hpp>
#include <fstream>
using nlohmann::json;
// Sleepy discord library is compiled with default struct align, MQ2 needs 4 byte
// Even with this pragma push/pop, having headers for both included in the same cpp causes stack corruption :(
// So this file contains MQ2 stuff, other file contains discord stuff
#pragma pack(push, 4)
#include "../MQ2Plugin.h"
#pragma pack(pop)
PreSetup("MQ2Discord");
json config;
unsigned int __stdcall MQ2DataVariableLookup(char * VarName, char * Value, size_t ValueLen);
Blech blechAllow('#', '|', MQ2DataVariableLookup);
Blech blechBlock('#', '|', MQ2DataVariableLookup);
std::map<std::string, unsigned int> eventsAllow;
std::map<std::string, unsigned int> eventsBlock;
// MQ2Main isn't nice enough to export this
unsigned int __stdcall MQ2DataVariableLookup(char * VarName, char * Value, size_t ValueLen)
{
strcpy_s(Value, ValueLen, VarName);
if (!pLocalPlayer)
return strlen(Value);
return strlen(ParseMacroParameter((PSPAWNINFO)pLocalPlayer, Value, ValueLen));
}
void __stdcall BlechEvent(unsigned int ID, void * pData, PBLECHVALUE pValues)
{
}
void Connect(std::string connectMsg)
{
if (g_pThread)
{
WriteChatf("[MQ2Discord] Already connected");
return;
}
std::string token = config["token"].get<std::string>();
std::string channel = config["channel"].get<std::string>();
std::string allowedUser = config["user"].get<std::string>();
if (token == "")
{
WriteChatf("[MQ2Discord] No token configured");
return;
}
if (channel == "")
{
WriteChatf("[MQ2Discord] No channel configured");
return;
}
if (allowedUser == "")
{
WriteChatf("[MQ2Discord] No user configured");
return;
}
// Set run flag to true and start thread
bRun = true;
bFinished = false;
g_pThread = new std::thread(DiscordThread, token, channel, allowedUser, connectMsg);
}
void Disconnect()
{
if (!g_pThread)
{
WriteChatf("[MQ2Discord] Not connected");
return;
}
// Set run flag to false and wait for thread to stop
bRun = false;
g_pThread->join();
g_pThread = nullptr;
return;
}
void SaveConfig(PCHAR szServer, PCHAR szCharName)
{
std::ofstream configFile(std::string(gszINIPath) + "\\MQ2Discord_" + szServer + "_" + szCharName + ".json");
if (configFile.is_open())
configFile << std::setw(4) << config << std::endl;
}
void LoadConfig(PCHAR szServer, PCHAR szCharName)
{
std::ifstream configFile(std::string(gszINIPath) + "\\MQ2Discord_" + szServer + "_" + szCharName + ".json");
if (configFile.is_open())
{
configFile >> config;
configFile.close();
WriteChatf("[MQ2Discord] Loaded config");
}
else
{
WriteChatf("[MQ2Discord] Failed to load config");
}
// Set some defaults & save
if (config["token"] == nullptr)
config["token"] = "";
if (config["channel"] == nullptr)
config["channel"] = "";
if (config["user"] == nullptr)
config["user"] = "";
if (config["allow"] == nullptr)
config["allow"] = { "[MQ2Discord]#*#", "[MQ2]#*#" };
if (config["block"] == nullptr)
config["block"] = json::array();
if (config["autoconnect"] == nullptr)
config["autoconnect"] = true;
SaveConfig(szServer, szCharName);
// Load events
blechAllow.Reset();
blechBlock.Reset();
eventsAllow.clear();
eventsBlock.clear();
for each (auto jEvent in config["allow"])
eventsAllow[jEvent.get<std::string>()] = blechAllow.AddEvent(jEvent.get<std::string>().c_str(), BlechEvent);
for each (auto jEvent in config["block"])
eventsBlock[jEvent.get<std::string>()] = blechBlock.AddEvent(jEvent.get<std::string>().c_str(), BlechEvent);
}
VOID DiscordCmd(PSPAWNINFO pChar, PCHAR szLine)
{
// I wonder how many std::strings I create unnecessarily in this function...
char buffer[MAX_STRING] = { 0 };
GetArg(buffer, szLine, 1);
if (!_stricmp(buffer, "connect"))
{
Connect(std::string("Connected: ") + pChar->Name);
return;
}
if (!_stricmp(buffer, "disconnect"))
{
return Disconnect();
}
if (!_stricmp(buffer, "autoconnect"))
{
char * setting = GetNextArg(szLine, 1);
if (setting && *setting && !_stricmp(setting, "on"))
{
config["autoconnect"] = true;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
else if (setting && *setting && !_stricmp(setting, "off"))
{
config["autoconnect"] = false;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
WriteChatf("[MQ2Discord] Autoconnect: %s", config["autoconnect"].get<bool>() ? "true" : "false");
return;
}
if (!_stricmp(buffer, "allow"))
{
char * szEvent = GetNextArg(szLine, 1);
if (szEvent && *szEvent)
{
if (!eventsAllow.count(szEvent))
{
// Add event if it doesn't exist. Goes in 3 places, the config, blech, and our map of text to blech id (for later removal)
eventsAllow[szEvent] = blechAllow.AddEvent(szEvent, BlechEvent);
config["allow"] += szEvent;
WriteChatf("[MQ2Discord] Added allow event: %s", szEvent);
}
else
{
// Remove it if it does
blechAllow.RemoveEvent(eventsAllow[szEvent]);
eventsAllow.erase(szEvent);
for (int i = 0; i < config["allow"].size(); i++) // No nice remove method on json array, and I couldn't get std::find working
if (config["allow"][i] == szEvent)
{
config["allow"].erase(i);
break;
}
WriteChatf("[MQ2Discord] Removed allow event: %s", szEvent);
}
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
else
{
WriteChatf("[MQ2Discord] Allowing the following events:");
for each (auto kvp in eventsAllow)
{
WriteChatf("[MQ2Discord] %s", kvp.first.c_str());
}
}
return;
}
if (!_stricmp(buffer, "block"))
{
char * szEvent = GetNextArg(szLine, 1);
if (szEvent && *szEvent)
{
// Add event if it doesn't exist. Goes in 3 places, the config, blech, and our map of text to blech id (for later removal)
if (!eventsBlock.count(szEvent))
{
eventsBlock[szEvent] = blechBlock.AddEvent(szEvent, BlechEvent);
config["block"] += szEvent;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
WriteChatf("[MQ2Discord] Added block event: %s", szEvent);
}
else
{
// Remove it if it does
blechBlock.RemoveEvent(eventsBlock[szEvent]);
eventsBlock.erase(szEvent);
for (int i = 0; i < config["allow"].size(); i++)
if (config["allow"][i] == szEvent)
{
config["allow"].erase(i);
break;
}
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
WriteChatf("[MQ2Discord] Removed block event: %s", szEvent);
}
}
else
{
WriteChatf("[MQ2Discord] Blocking the following events:");
for each (auto kvp in eventsBlock)
{
WriteChatf("[MQ2Discord] %s", kvp.first.c_str());
}
}
return;
}
if (!_stricmp(buffer, "token"))
{
char * szToken = GetNextArg(szLine, 1);
if (szToken && *szToken)
{
config["token"] = szToken;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
WriteChatf("[MQ2Discord] Token: %s", config["token"].get<std::string>().c_str());
return;
}
if (!_stricmp(buffer, "channel"))
{
char * szChannel = GetNextArg(szLine, 1);
if (szChannel && *szChannel)
{
config["channel"] = szChannel;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
WriteChatf("[MQ2Discord] Channel: %s", config["channel"].get<std::string>().c_str());
return;
}
if (!_stricmp(buffer, "user"))
{
char * szUser = GetNextArg(szLine, 1);
if (szUser && *szUser)
{
config["user"] = szUser;
SaveConfig(EQADDR_SERVERNAME, pChar->Name);
}
WriteChatf("[MQ2Discord] User: %s", config["user"].get<std::string>().c_str());
return;
}
WriteChatf("[MQ2Discord] Usage:");
WriteChatf("[MQ2Discord] /discord [connect|disconnect] - Connect or disconnect from discord server");
WriteChatf("[MQ2Discord] /discord autoconnect [on|off] - Set or display autoconnect on load");
WriteChatf("[MQ2Discord] /discord token [<token>] - Set or display token used to authenticate");
WriteChatf("[MQ2Discord] /discord channel [<channel id>] - Set or display channel id to output messages to");
WriteChatf("[MQ2Discord] /discord user [<user id>] - Set or display user id to accept commands from");
WriteChatf("[MQ2Discord] /discord allow|block [<event>] - Toggle or list events (MQ2 syntax) for output in discord");
}
PLUGIN_API VOID InitializePlugin(VOID)
{
AddCommand("/discord", DiscordCmd, 0, 0, 1);
}
PLUGIN_API VOID ShutdownPlugin(VOID)
{
if (g_pThread)
Disconnect();
RemoveCommand("/discord");
}
PLUGIN_API VOID OnPulse(VOID)
{
// Process any received messages
std::string msg;
while (fromDiscord.tryDequeue(msg))
{
WriteChatf("[MQ2Discord] %s", msg.c_str());
if (msg[0] == '/')
DoCommand((PSPAWNINFO)pLocalPlayer, (PCHAR)msg.c_str());
}
// Check our thead - if we want to run, and it's not running, it's disconnected for whatever reason. Reconnect it
if (bRun && bFinished && config["autoconnect"].get<bool>() && GetGameState() == GAMESTATE_INGAME)
{
Disconnect();
Connect(std::string("Reconnected: ") + ((PSPAWNINFO)pLocalPlayer)->Name);
}
}
void EnqueueIfMatch(const PCHAR Line)
{
char buffer[MAX_STRING] = { 0 };
strcpy_s(buffer, Line);
if (bRun.load() && blechAllow.Feed(buffer) && !blechBlock.Feed(buffer))
toDiscord.enqueue(Line);
}
PLUGIN_API DWORD OnWriteChatColor(PCHAR Line, DWORD Color, DWORD Filter)
{
EnqueueIfMatch(Line);
return 0;
}
PLUGIN_API DWORD OnIncomingChat(PCHAR Line, DWORD Color)
{
EnqueueIfMatch(Line);
return 0;
}
PLUGIN_API VOID SetGameState(DWORD GameState)
{
if (GameState == GAMESTATE_INGAME)
{
LoadConfig(EQADDR_SERVERNAME, ((PSPAWNINFO)pLocalPlayer)->Name);
if (config["autoconnect"].get<bool>())
Connect(std::string("Connected: ") + ((PSPAWNINFO)pLocalPlayer)->Name);
}
else
{
Disconnect();
}
}
\ No newline at end of file
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
// https://stackoverflow.com/a/16075550 with tryDequeue method added
#ifndef SAFE_QUEUE
#define SAFE_QUEUE
#include <queue>
#include <mutex>
#include <condition_variable>
// A threadsafe-queue.
template <class T>
class SafeQueue
{
public:
SafeQueue(void)
: q()
, m()
, c()
{}
~SafeQueue(void)
{}
// Add an element to the queue.
void enqueue(T t)
{
std::lock_guard<std::mutex> lock(m);
q.push(t);
c.notify_one();
}
// Get the "front"-element.
// If the queue is empty, wait till a element is avaiable.
T dequeue(void)
{
std::unique_lock<std::mutex> lock(m);
while (q.empty())
{
// release lock as long as the wait and reaquire it afterwards.
c.wait(lock);
}
T val = q.front();
q.pop();
return val;
}
bool tryDequeue(T& val)
{
std::unique_lock<std::mutex> lock(m);
if (q.empty())
return false;
val = q.front();
q.pop();
return true;
}
private:
std::queue<T> q;
mutable std::mutex m;
std::condition_variable c;
};
#endif
From b567ec1b3ea6606479cb115547ec43821ef3aa7f Mon Sep 17 00:00:00 2001
From: Tim <Tim@DESKTOP-IC0KE4E>
Date: Sun, 4 Mar 2018 22:28:32 +1100
Subject: [PATCH] Compiled and added poll
---
examples/hello/example0.cpp | 8 +++++++-
examples/hello/hello.vcxproj | 4 ++--
examples/rock-paper-scissors/rock-paper-scissors.vcxproj | 2 +-
include/sleepy_discord/client.h | 1 +
include/sleepy_discord/websocketpp_websocket.h | 1 +
sleepy_discord/default_functions.cpp | 4 ++++
sleepy_discord/sleepy_discord.vcxproj | 2 +-
sleepy_discord/sleepy_discord.vcxproj.filters | 1 +
sleepy_discord/websocketpp_websocket.cpp | 4 ++++
9 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/examples/hello/example0.cpp b/examples/hello/example0.cpp
index 2c8c370..94afe49 100644
--- a/examples/hello/example0.cpp
+++ b/examples/hello/example0.cpp
@@ -11,7 +11,13 @@ public:
}
};
+using namespace std::chrono_literals;
+
int main() {
myClientClass client("token", 2);
- client.run();
+ while (true)
+ {
+ client.poll();
+ std::this_thread::sleep_for(10ms);
+ }
}
diff --git a/examples/hello/hello.vcxproj b/examples/hello/hello.vcxproj
index 38b88b4..e047abd 100644
--- a/examples/hello/hello.vcxproj
+++ b/examples/hello/hello.vcxproj
@@ -22,7 +22,7 @@
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{62F30952-924E-4567-9EE6-A8B7000368B7}</ProjectGuid>
<RootNamespace>hello</RootNamespace>
- <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
@@ -89,7 +89,7 @@
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
- <AdditionalDependencies>sleepy_discord.lib;libcurl_a.lib;libsslMT.lib;libcryptoMT.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>sleepy_discord.lib;libcurl.lib;libsslMT.lib;libcryptoMT.lib;crypt32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
diff --git a/examples/rock-paper-scissors/rock-paper-scissors.vcxproj b/examples/rock-paper-scissors/rock-paper-scissors.vcxproj
index 768a3d2..4f3c525 100644
--- a/examples/rock-paper-scissors/rock-paper-scissors.vcxproj
+++ b/examples/rock-paper-scissors/rock-paper-scissors.vcxproj
@@ -22,7 +22,7 @@
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{0B499EFB-B39C-4C6A-9661-A46C76F8EE4A}</ProjectGuid>
<RootNamespace>rockpaperscissors</RootNamespace>
- <WindowsTargetPlatformVersion>10.0.14393.0</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
diff --git a/include/sleepy_discord/client.h b/include/sleepy_discord/client.h
index 170d53d..7649960 100644
--- a/include/sleepy_discord/client.h
+++ b/include/sleepy_discord/client.h
@@ -180,6 +180,7 @@ namespace SleepyDiscord {
const bool isRateLimited() { return messagesRemaining <= 0 || request(Get, "gateway").statusCode == TOO_MANY_REQUESTS; }
void quit(); //public function for diconnecting
virtual void run();
+ virtual void poll();
//time
enum AssignmentType : bool {
diff --git a/include/sleepy_discord/websocketpp_websocket.h b/include/sleepy_discord/websocketpp_websocket.h
index 197d8d7..c5bbc9f 100644
--- a/include/sleepy_discord/websocketpp_websocket.h
+++ b/include/sleepy_discord/websocketpp_websocket.h
@@ -24,6 +24,7 @@ namespace SleepyDiscord {
~WebsocketppDiscordClient();
void run();
+ void poll();
Timer schedule(std::function<void()> code, const time_t milliseconds);
protected:
#include "standard_config_header.h"
diff --git a/sleepy_discord/default_functions.cpp b/sleepy_discord/default_functions.cpp
index b0ca380..6655477 100644
--- a/sleepy_discord/default_functions.cpp
+++ b/sleepy_discord/default_functions.cpp
@@ -203,6 +203,10 @@ namespace SleepyDiscord {
}
+ void BaseDiscordClient::poll() {
+
+ }
+
void BaseDiscordClient::onQuit() {
}
diff --git a/sleepy_discord/sleepy_discord.vcxproj b/sleepy_discord/sleepy_discord.vcxproj
index f925fd2..50c0855 100644
--- a/sleepy_discord/sleepy_discord.vcxproj
+++ b/sleepy_discord/sleepy_discord.vcxproj
@@ -43,7 +43,7 @@
<Keyword>Win32Proj</Keyword>
<Platform>Win32</Platform>
<ProjectName>sleepy_discord</ProjectName>
- <WindowsTargetPlatformVersion>10.0.10586.0</WindowsTargetPlatformVersion>
+ <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
diff --git a/sleepy_discord/sleepy_discord.vcxproj.filters b/sleepy_discord/sleepy_discord.vcxproj.filters
index ac01ee5..1cea9d5 100644
--- a/sleepy_discord/sleepy_discord.vcxproj.filters
+++ b/sleepy_discord/sleepy_discord.vcxproj.filters
@@ -95,6 +95,7 @@
<ClCompile Include="..\deps\cpr\cpr\ssl_options.cpp">
<Filter>cpr</Filter>
</ClCompile>
+ <ClCompile Include="asignments_client.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\sleepy_discord\client.h" />
diff --git a/sleepy_discord/websocketpp_websocket.cpp b/sleepy_discord/websocketpp_websocket.cpp
index 6c306b7..2ba0bb0 100644
--- a/sleepy_discord/websocketpp_websocket.cpp
+++ b/sleepy_discord/websocketpp_websocket.cpp
@@ -54,6 +54,10 @@ namespace SleepyDiscord {
this_client.run();
}