Created
November 24, 2017 00:30
-
-
Save Liareth/3fd7d008fff3676d32153a3abd3fab3b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Chat.hpp" | |
#include "API/CAppManager.hpp" | |
#include "API/CExoString.hpp" | |
#include "API/CNWSClient.hpp" | |
#include "API/CNWSDungeonMaster.hpp" | |
#include "API/CNWSMessage.hpp" | |
#include "API/CNWSPlayer.hpp" | |
#include "API/Constants.hpp" | |
#include "API/CServerExoApp.hpp" | |
#include "API/CVirtualMachine.hpp" | |
#include "API/Functions.hpp" | |
#include "API/Globals.hpp" | |
#include "Services/Config/Config.hpp" | |
#include "Services/Hooks/Hooks.hpp" | |
#include "Services/Log/Log.hpp" | |
#include "ViewPtr.hpp" | |
using namespace NWNXLib; | |
static ViewPtr<Chat::Chat> g_plugin; | |
NWNX_PLUGIN_ENTRY Plugin::Info* PluginInfo() | |
{ | |
return new Plugin::Info | |
{ | |
"Chat", | |
"Allows chat events to be captured, skipped, and manual chat messages to be dispatched.", | |
"Scholar Midnight", | |
"liarethnwn@gmail.com", | |
1, | |
true | |
}; | |
} | |
NWNX_PLUGIN_ENTRY Plugin* PluginLoad(Plugin::CreateParams params) | |
{ | |
g_plugin = new Chat::Chat(params); | |
return g_plugin; | |
} | |
using namespace NWNXLib::API; | |
using namespace NWNXLib::Services; | |
namespace Chat { | |
Chat::Chat(const Plugin::CreateParams& params) | |
: Plugin(params), m_skipMessage(false), m_depth(0) | |
{ | |
m_chatScript = GetServices()->m_config->Get<std::string>("CHAT_SCRIPT", ""); | |
GetServices()->m_events->RegisterEvent("SEND_MESSAGE", std::bind(&Chat::OnSendMessage, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("REGISTER_CHAT_SCRIPT", std::bind(&Chat::OnRegisterChatScript, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("SKIP_MESSAGE", std::bind(&Chat::OnSkipMessage, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("GET_CHANNEL", std::bind(&Chat::OnGetChannel, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("GET_MESSAGE", std::bind(&Chat::OnGetMessage, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("GET_SENDER", std::bind(&Chat::OnGetSender, this, std::placeholders::_1)); | |
GetServices()->m_events->RegisterEvent("GET_TARGET", std::bind(&Chat::OnGetTarget, this, std::placeholders::_1)); | |
GetServices()->m_hooks->RequestExclusiveHook<Functions::CNWSMessage__SendServerToPlayerChatMessage>(&Chat::SendServerToPlayerChatMessage); | |
m_hook = GetServices()->m_hooks->FindHookByAddress(Functions::CNWSMessage__SendServerToPlayerChatMessage); | |
} | |
Chat::~Chat() | |
{ | |
} | |
void Chat::SendServerToPlayerChatMessage(CNWSMessage* thisPtr, ChatChannel channel, Types::ObjectID sender, | |
CExoString message, Types::PlayerID target, CExoString* tellName) | |
{ | |
Chat& plugin = *g_plugin; | |
if (plugin.m_depth == 0 && !plugin.m_chatScript.empty()) | |
{ | |
plugin.m_activeChannel = channel; | |
plugin.m_activeMessage = message.m_sString ? std::string(message.m_sString) : ""; | |
plugin.m_activeSenderObjectId = sender; | |
CNWSClient* client = (*Globals::g_pAppManager)->m_pServerExoApp->GetClientObjectByPlayerId(target, 0); | |
plugin.m_activeTargetObjectId = client ? static_cast<CNWSPlayer*>(client)->m_oidPCObject : Constants::OBJECT_INVALID; | |
CExoString script = plugin.m_chatScript.c_str(); | |
++plugin.m_depth; | |
(*Globals::g_pVirtualMachine)->RunScript(&script, sender, 1); | |
--plugin.m_depth; | |
} | |
plugin.GetServices()->m_log->Debug("%s chat message. Channel: '%i', Message: '%s', Sender (ObjID): '0x%08x', Target (PlayerID): '0x%08x'", | |
plugin.m_skipMessage ? "Skipped" : "Sent", channel, message.m_sString, sender, target); | |
if (!plugin.m_skipMessage) | |
{ | |
plugin.m_hook->CallOriginal<void>(thisPtr, channel, sender, message, target, tellName); | |
} | |
if (plugin.m_depth == 0) | |
{ | |
plugin.m_skipMessage = false; | |
} | |
} | |
Events::ArgumentStack Chat::OnSendMessage(Events::ArgumentStack&& args) | |
{ | |
const auto channel = static_cast<ChatChannel>(Events::ExtractArgument<int32_t>(args)); | |
const auto message = Events::ExtractArgument<std::string>(args); | |
const auto speaker = Events::ExtractArgument<Types::ObjectID>(args); | |
const auto target = Events::ExtractArgument<Types::ObjectID>(args); | |
const bool hasManualPlayerId = target != Constants::OBJECT_INVALID; | |
const Types::PlayerID playerId = hasManualPlayerId ? | |
(*Globals::g_pAppManager)->m_pServerExoApp->GetPlayerIDByGameObjectID(target) : | |
Constants::PLAYERID_ALL_CLIENTS; | |
Events::ArgumentStack stack; | |
if (playerId != Constants::PLAYERID_INVALIDID) | |
{ | |
bool sentMessage = false; | |
CNWSMessage* messageDispatch = static_cast<CNWSMessage*>((*Globals::g_pAppManager)->m_pServerExoApp->GetNWSMessage()); | |
if (hasManualPlayerId && (channel != ChatChannel::PLAYER_TELL || channel != ChatChannel::DM_TELL)) | |
{ | |
// We have a custom target but we're not in the tell channel. We have to call the functions directly. | |
switch (channel) | |
{ | |
case ChatChannel::PLAYER_TALK: | |
messageDispatch->SendServerToPlayerChat_Talk(playerId, speaker, message.c_str()); | |
sentMessage = true; | |
break; | |
case ChatChannel::PLAYER_SHOUT: | |
messageDispatch->SendServerToPlayerChat_Shout(playerId, speaker, message.c_str()); | |
sentMessage = true; | |
break; | |
case ChatChannel::PLAYER_WHISPER: | |
messageDispatch->SendServerToPlayerChat_Whisper(playerId, speaker, message.c_str()); | |
sentMessage = true; | |
break; | |
default: | |
break; | |
} | |
} | |
if (!sentMessage) | |
{ | |
messageDispatch->SendServerToPlayerChatMessage(static_cast<uint8_t>(channel), speaker, message.c_str(), playerId, nullptr); | |
} | |
Events::InsertArgument(stack, 1); | |
} | |
else | |
{ | |
Events::InsertArgument(stack, 0); | |
} | |
return stack; | |
} | |
Events::ArgumentStack Chat::OnRegisterChatScript(Events::ArgumentStack&& args) | |
{ | |
m_chatScript = Events::ExtractArgument<std::string>(args); | |
return Events::ArgumentStack(); | |
} | |
Events::ArgumentStack Chat::OnSkipMessage(Events::ArgumentStack&&) | |
{ | |
m_skipMessage = true; | |
return Events::ArgumentStack(); | |
} | |
Events::ArgumentStack Chat::OnGetChannel(Events::ArgumentStack&&) | |
{ | |
Events::ArgumentStack stack; | |
Events::InsertArgument(stack, static_cast<int32_t>(m_activeChannel)); | |
return stack; | |
} | |
Events::ArgumentStack Chat::OnGetMessage(Events::ArgumentStack&&) | |
{ | |
Events::ArgumentStack stack; | |
Events::InsertArgument(stack, m_activeMessage); | |
return stack; | |
} | |
Events::ArgumentStack Chat::OnGetSender(Events::ArgumentStack&&) | |
{ | |
Events::ArgumentStack stack; | |
Events::InsertArgument(stack, m_activeSenderObjectId); | |
return stack; | |
} | |
Events::ArgumentStack Chat::OnGetTarget(Events::ArgumentStack&&) | |
{ | |
Events::ArgumentStack stack; | |
Events::InsertArgument(stack, m_activeTargetObjectId); | |
return stack; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "API/Types.hpp" | |
#include "Common.hpp" | |
#include "Plugin.hpp" | |
#include "Services/Events/Events.hpp" | |
namespace NWNXLib { namespace Hooking { class FunctionHook; } } | |
namespace Chat { | |
enum class ChatChannel | |
{ | |
PLAYER_TALK = 1, | |
PLAYER_SHOUT = 2, | |
PLAYER_WHISPER = 3, | |
PLAYER_TELL = 4, | |
SERVER_MSG = 5, | |
PLAYER_PARTY = 6, | |
PLAYER_DM = 14, | |
DM_TALK = 17, | |
DM_SHOUT = 18, | |
DM_WHISPER = 19, | |
DM_TELL = 20, | |
DM_PARTY = 22, | |
DM_DM = 30 | |
}; | |
class Chat : public NWNXLib::Plugin | |
{ | |
public: | |
Chat(const Plugin::CreateParams& params); | |
virtual ~Chat(); | |
private: | |
NWNXLib::Hooking::FunctionHook* m_hook; | |
ChatChannel m_activeChannel; | |
std::string m_activeMessage; | |
NWNXLib::API::Types::ObjectID m_activeSenderObjectId; | |
NWNXLib::API::Types::ObjectID m_activeTargetObjectId; | |
std::string m_chatScript; | |
bool m_skipMessage; | |
uint32_t m_depth; | |
static void SendServerToPlayerChatMessage(NWNXLib::API::CNWSMessage* thisPtr, ChatChannel channel, NWNXLib::API::Types::ObjectID sender, | |
NWNXLib::API::CExoString message, NWNXLib::API::Types::ObjectID target, NWNXLib::API::CExoString* tellName); | |
NWNXLib::Services::Events::ArgumentStack OnSendMessage(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnRegisterChatScript(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnSkipMessage(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnGetChannel(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnGetMessage(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnGetSender(NWNXLib::Services::Events::ArgumentStack&& args); | |
NWNXLib::Services::Events::ArgumentStack OnGetTarget(NWNXLib::Services::Events::ArgumentStack&& args); | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// These following functions should be called by NWNX plugin developers, who should expose | |
// their own, more friendly headers. | |
// | |
// For example, this following function would wrap a call which passes three parameters, | |
// receives three back, and constructs a vector frm the result. | |
// | |
// vector GetVectorFromCoords(float x, float y, float z) | |
// { | |
// string pluginName = "NWNX_TestPlugin"; | |
// string funcName = "GiveMeBackTheSameValues"; | |
// | |
// // Note the inverse argument push order. | |
// // C++-side, arguments will be consumed from right to left. | |
// NWNX_PushArgumentFloat(pluginName, funcName, z); | |
// NWNX_PushArgumentFloat(pluginName, funcName, y); | |
// NWNX_PushArgumentFloat(pluginName, funcName, x); | |
// | |
// // This calls the function, which will prepare the return values. | |
// NWNX_CallFunction(pluginName, funcName); | |
// | |
// // C++-side pushes the return values in reverse order so we can consume them naturally here. | |
// float _x = NWNX_GetReturnValueFloat(pluginName, funcName); | |
// float _y = NWNX_GetReturnValueFloat(pluginName, funcName); | |
// float _z = NWNX_GetReturnValueFloat(pluginName, funcName); | |
// | |
// return vector(_x, _y, _z); | |
// } | |
void NWNX_CallFunction(string pluginName, string functionName); | |
void NWNX_PushArgumentInt(string pluginName, string functionName, int value); | |
void NWNX_PushArgumentFloat(string pluginName, string functionName, float value); | |
void NWNX_PushArgumentObject(string pluginName, string functionName, object value); | |
void NWNX_PushArgumentString(string pluginName, string functionName, string value); | |
int NWNX_GetReturnValueInt(string pluginName, string functionName); | |
float NWNX_GetReturnValueFloat(string pluginName, string functionName); | |
object NWNX_GetReturnValueObject(string pluginName, string functionName); | |
string NWNX_GetReturnValueString(string pluginName, string functionName); | |
void NWNX_INTERNAL_CallFunction(string pluginName, string functionName); | |
void NWNX_INTERNAL_PushArgument(string pluginName, string functionName, string value); | |
string NWNX_INTERNAL_GetReturnValueString(string pluginName, string functionName, string type); | |
object NWNX_INTERNAL_GetReturnValueObject(string pluginName, string functionName, string type); | |
void NWNX_CallFunction(string pluginName, string functionName) | |
{ | |
NWNX_INTERNAL_CallFunction(pluginName, functionName); | |
} | |
void NWNX_PushArgumentInt(string pluginName, string functionName, int value) | |
{ | |
NWNX_INTERNAL_PushArgument(pluginName, functionName, "0 " + IntToString(value)); | |
} | |
void NWNX_PushArgumentFloat(string pluginName, string functionName, float value) | |
{ | |
NWNX_INTERNAL_PushArgument(pluginName, functionName, "1 " + FloatToString(value)); | |
} | |
void NWNX_PushArgumentObject(string pluginName, string functionName, object value) | |
{ | |
NWNX_INTERNAL_PushArgument(pluginName, functionName, "2 " + ObjectToString(value)); | |
} | |
void NWNX_PushArgumentString(string pluginName, string functionName, string value) | |
{ | |
NWNX_INTERNAL_PushArgument(pluginName, functionName, "3 " + value); | |
} | |
int NWNX_GetReturnValueInt(string pluginName, string functionName) | |
{ | |
return StringToInt(NWNX_INTERNAL_GetReturnValueString(pluginName, functionName, "0 ")); | |
} | |
float NWNX_GetReturnValueFloat(string pluginName, string functionName) | |
{ | |
return StringToFloat(NWNX_INTERNAL_GetReturnValueString(pluginName, functionName, "1 ")); | |
} | |
object NWNX_GetReturnValueObject(string pluginName, string functionName) | |
{ | |
return NWNX_INTERNAL_GetReturnValueObject(pluginName, functionName, "2 "); | |
} | |
string NWNX_GetReturnValueString(string pluginName, string functionName) | |
{ | |
return NWNX_INTERNAL_GetReturnValueString(pluginName, functionName, "3 "); | |
} | |
void NWNX_INTERNAL_CallFunction(string pluginName, string functionName) | |
{ | |
SetLocalString(GetModule(), "NWNX!CALL_FUNCTION!" + pluginName + "!" + functionName, "1"); | |
} | |
void NWNX_INTERNAL_PushArgument(string pluginName, string functionName, string value) | |
{ | |
SetLocalString(GetModule(), "NWNX!PUSH_ARGUMENT!" + pluginName + "!" + functionName, value); | |
} | |
string NWNX_INTERNAL_GetReturnValueString(string pluginName, string functionName, string type) | |
{ | |
return GetLocalString(GetModule(), "NWNX!GET_RETURN_VALUE!" + pluginName + "!" + functionName + "!" + type); | |
} | |
object NWNX_INTERNAL_GetReturnValueObject(string pluginName, string functionName, string type) | |
{ | |
return GetLocalObject(GetModule(), "NWNX!GET_RETURN_VALUE!" + pluginName + "!" + functionName + "!" + type); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "nwnx" | |
const int NWNX_CHAT_CHANNEL_PLAYER_TALK = 1; | |
const int NWNX_CHAT_CHANNEL_PLAYER_SHOUT = 2; | |
const int NWNX_CHAT_CHANNEL_PLAYER_WHISPER = 3; | |
const int NWNX_CHAT_CHANNEL_PLAYER_TELL = 4; | |
const int NWNX_CHAT_CHANNEL_SERVER_MSG = 5; | |
const int NWNX_CHAT_CHANNEL_PLAYER_PARTY = 6; | |
const int NWNX_CHAT_CHANNEL_PLAYER_DM = 14; | |
const int NWNX_CHAT_CHANNEL_DM_TALK = 17; | |
const int NWNX_CHAT_CHANNEL_DM_SHOUT = 18; | |
const int NWNX_CHAT_CHANNEL_DM_WHISPER = 19; | |
const int NWNX_CHAT_CHANNEL_DM_TELL = 20; | |
const int NWNX_CHAT_CHANNEL_DM_PARTY = 22; | |
const int NWNX_CHAT_CHANNEL_DM_DM = 30; | |
// Sends a chat message. Channel is a NWNX_* constant. | |
// If no target is provided, then it broadcasts to all eligible targets. | |
// Returns TRUE if successful, FALSE otherwise. | |
int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID); | |
// Registers the script which receives all chat messages. | |
// If a script was previously registered, this one will take over. | |
void NWNX_Chat_RegisterChatScript(string script); | |
// Skips the message. | |
// Must be called from an chat or system script handler. | |
void NWNX_Chat_SkipMessage(); | |
// Gets the channel. Channel is a NWNX_* constant. | |
// Must be called from an chat or system script handler. | |
int NWNX_Chat_GetChannel(); | |
// Gets the message. | |
// Must be called from an chat or system script handler. | |
string NWNX_Chat_GetMessage(); | |
// Gets the sender. | |
// Must be called from an chat or system script handler. | |
object NWNX_Chat_GetSender(); | |
// Gets the target. May be OBJECT_INVALID if no target. | |
// Must be called from an chat or system script handler. | |
object NWNX_Chat_GetTarget(); | |
int NWNX_Chat_SendMessage(int channel, string message, object sender = OBJECT_SELF, object target = OBJECT_INVALID) | |
{ | |
NWNX_PushArgumentObject("NWNX_Chat", "SEND_MESSAGE", target); | |
NWNX_PushArgumentObject("NWNX_Chat", "SEND_MESSAGE", sender); | |
NWNX_PushArgumentString("NWNX_Chat", "SEND_MESSAGE", message); | |
NWNX_PushArgumentInt("NWNX_Chat", "SEND_MESSAGE", channel); | |
NWNX_CallFunction("NWNX_Chat", "SEND_MESSAGE"); | |
return NWNX_GetReturnValueInt("NWNX_Chat", "SEND_MESSAGE"); | |
} | |
void NWNX_Chat_RegisterChatScript(string script) | |
{ | |
NWNX_PushArgumentString("NWNX_Chat", "REGISTER_CHAT_SCRIPT", script); | |
NWNX_CallFunction("NWNX_Chat", "REGISTER_CHAT_SCRIPT"); | |
} | |
void NWNX_Chat_SkipMessage() | |
{ | |
NWNX_CallFunction("NWNX_Chat", "SKIP_MESSAGE"); | |
} | |
int NWNX_Chat_GetChannel() | |
{ | |
NWNX_CallFunction("NWNX_Chat", "GET_CHANNEL"); | |
return NWNX_GetReturnValueInt("NWNX_Chat", "GET_CHANNEL"); | |
} | |
string NWNX_Chat_GetMessage() | |
{ | |
NWNX_CallFunction("NWNX_Chat", "GET_MESSAGE"); | |
return NWNX_GetReturnValueString("NWNX_Chat", "GET_MESSAGE"); | |
} | |
object NWNX_Chat_GetSender() | |
{ | |
NWNX_CallFunction("NWNX_Chat", "GET_SENDER"); | |
return NWNX_GetReturnValueObject("NWNX_Chat", "GET_SENDER"); | |
} | |
object NWNX_Chat_GetTarget() | |
{ | |
NWNX_CallFunction("NWNX_Chat", "GET_TARGET"); | |
return NWNX_GetReturnValueObject("NWNX_Chat", "GET_TARGET"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment