diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 108b865c55..db7c498586 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -138,6 +138,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pfCharacter/pfConfirmationMgr.h" #include "pfCharacter/pfMarkerMgr.h" #include "pfConsole/pfConsole.h" +#include "pfConsole/pfRemoteConsole.h" #include "pfConsole/pfConsoleDirSrc.h" #include "pfConsoleCore/pfConsoleEngine.h" #if defined(PLASMA_PIPELINE_DX) @@ -1389,6 +1390,12 @@ bool plClient::StartInit() fConsole->RegisterAs( kConsoleObject_KEY ); // fixedKey from plFixedKey.h fConsole->Init( fConsoleEngine ); +#ifndef PLASMA_EXTERNAL_RELEASE + fRemoteConsole = new pfRemoteConsole(); + fRemoteConsole->RegisterAs( kRemoteConsoleObject_KEY ); + fRemoteConsole->Init(); +#endif + /// Init the font cache fFontCache = new plFontCache(); diff --git a/Sources/Plasma/Apps/plClient/plClient.h b/Sources/Plasma/Apps/plClient/plClient.h index a0d744e051..2c328af20e 100644 --- a/Sources/Plasma/Apps/plClient/plClient.h +++ b/Sources/Plasma/Apps/plClient/plClient.h @@ -65,6 +65,7 @@ class plInputController; class plSceneObject; class pfConsoleEngine; class pfConsole; +class pfRemoteConsole; class plAudioSystem; class plVirtualCam1; class plKey; @@ -122,6 +123,7 @@ class plClient : public hsKeyedObject pfConsoleEngine* fConsoleEngine; pfConsole* fConsole; + pfRemoteConsole* fRemoteConsole; bool fDone; bool fWindowActive; diff --git a/Sources/Plasma/FeatureLib/pfConsole/CMakeLists.txt b/Sources/Plasma/FeatureLib/pfConsole/CMakeLists.txt index 8a41a42418..a8908cb04a 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/CMakeLists.txt +++ b/Sources/Plasma/FeatureLib/pfConsole/CMakeLists.txt @@ -7,6 +7,7 @@ set(pfConsole_SOURCES pfConsoleDirSrc.cpp pfDispatchLog.cpp pfGameConsoleCommands.cpp + pfRemoteConsole.cpp ) set(pfConsole_HEADERS @@ -14,6 +15,7 @@ set(pfConsole_HEADERS pfConsoleCreatable.h pfConsoleDirSrc.h pfDispatchLog.h + pfRemoteConsole.h ) plasma_library(pfConsole diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h index 6f955eac3c..20a61b52f4 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h +++ b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCreatable.h @@ -48,4 +48,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pfConsole.h" REGISTER_CREATABLE(pfConsole); +#include "pfRemoteConsole.h" +REGISTER_CREATABLE(pfRemoteConsole); + #endif // pfConsoleCreatable_inc diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.cpp new file mode 100644 index 0000000000..c022156704 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.cpp @@ -0,0 +1,206 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include +#include + +#include "pfRemoteConsole.h" +#include "pfMessage/pfRemoteConsoleMsg.h" +#include "plgDispatch.h" +#include "plStatusLog/plStatusLog.h" + +#include + +void handlePipeServer(); +void SendFullBuf(HANDLE pipe, const char* buf, size_t buflen); + +pfRemoteConsole::~pfRemoteConsole() +{ + plgDispatch::Dispatch()->UnRegisterForExactType(pfRemoteConsoleMsg::Index(), GetKey()); +} + +void pfRemoteConsole::Init() { + plStatusLog::AddLineSF("plasmadbg.log", "Starting pfRemoteConsole"); + fThread = std::thread(handlePipeServer); + plgDispatch::Dispatch()->RegisterForExactType(pfRemoteConsoleMsg::Index(), GetKey()); +} + +bool pfRemoteConsole::MsgReceive( plMessage *msg ) +{ + plStatusLog::AddLineSF("plasmadbg.log", "plRemoteConsole got msg"); + pfRemoteConsoleMsg *cmd = pfRemoteConsoleMsg::ConvertNoRef( msg ); + if (cmd) { + plStatusLog::AddLineSF("plasmadbg.log", "Msg is pfRemoteConsoleMsg"); + PyObject* mymod = PythonInterface::FindModule("__main__"); + PythonInterface::RunStringInteractive(cmd->GetCommand().c_str(), mymod); + std::string output; + // get the messages + PythonInterface::getOutputAndReset(&output); + cmd->GetOutput().get()->fOutputData = ST::string::from_std_string(output); + { + std::lock_guard lk(cmd->GetOutput().get()->fOutputDataSetLock); + cmd->GetOutput().get()->fOutputDataSet = true; + } + cmd->GetOutput().get()->fCondvar.notify_one(); + + return true; + } + return hsKeyedObject::MsgReceive(msg); +} + +bool canRun(ST::string& script) { + return script.contains("\n"); +} + +void handlePipeServer() { + SetThreadDescription( + GetCurrentThread(), + L"PipeServer!" + ); + + // First, initialize the socket + DWORD pid = GetCurrentProcessId(); + ST::string pipename = ST::format(R"(\\.\pipe\URU-PYTHON-{})", pid); + + HANDLE pipe = CreateNamedPipeW( + pipename.to_std_wstring().c_str(), + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + // 1MiB + 1 * 1024 * 1024, + 1 * 1024 * 1024, + 0, + nullptr + ); + if (pipe == nullptr) { + // TODO: Log an error. + return; + } + + if (!ConnectNamedPipe(pipe, nullptr) && GetLastError() != ERROR_PIPE_CONNECTED) { + DWORD err = GetLastError(); + // TODO: Log an error + CloseHandle(pipe); + return; + } + + + char buf; + + ST::string curScript; + + SendFullBuf(pipe, ">>> ", 4); + + bool isMultiline = false; + while (true) { + DWORD readSize; + if (!ReadFile(pipe, &buf, sizeof(buf), &readSize, nullptr)) { + DWORD err = GetLastError(); + CloseHandle(pipe); + // TODO: Log the error. + return; + } + + if (readSize == 0) { + // TODO: Log that pipe is closed. + CloseHandle(pipe); + return; + } + + if (buf != '\r') { + SendFullBuf(pipe, &buf, 1); + } else { + SendFullBuf(pipe, "\r\n", 2); + } + + // Turn our string into ST + ST::string readData(&buf, readSize); + + readData = readData.replace("\r", "\n"); + curScript += readData; + + if (buf != '\r') { + continue; + } + + if (!isMultiline && curScript.ends_with(":\n")) { + isMultiline = true; + SendFullBuf(pipe, "... ", 4); + continue; + } + + if (isMultiline && !curScript.ends_with("\n\n")) { + SendFullBuf(pipe, "... ", 4); + continue; + } + + // Send message. + pfRemoteConsoleMsg *cMsg = new pfRemoteConsoleMsg(curScript); + std::shared_ptr output(cMsg->GetOutput()); + + cMsg->Send(nullptr, true); + + // Wait for answer. + { + std::unique_lock lk(output->fOutputDataSetLock); + output->fCondvar.wait(lk, [output] {return output->fOutputDataSet;}); + } + + ST::string finalOutput = output.get()->fOutputData.replace("\n", "\r\n"); + + SendFullBuf(pipe, finalOutput.c_str(), finalOutput.size()); + curScript.clear(); + isMultiline = false; + + SendFullBuf(pipe, ">>> ", 4); + } +} + +void SendFullBuf(HANDLE pipe, const char* buf, size_t buflen) { + const char* endbuf = buf + buflen; + DWORD writeSize; + while (buf < endbuf) { + WriteFile(pipe, buf, endbuf - buf, &writeSize, nullptr); + buf += writeSize; + } +} \ No newline at end of file diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.h b/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.h new file mode 100644 index 0000000000..30ce3cf7ac --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfConsole/pfRemoteConsole.h @@ -0,0 +1,71 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef pfRemoteConsole_inc +#define pfRemoteConsole_inc + +#include + +#include "HeadSpin.h" + +#include "pnKeyedObject/hsKeyedObject.h" + +class pfRemoteConsole : public hsKeyedObject +{ +public: + pfRemoteConsole() {} + ~pfRemoteConsole(); + + CLASSNAME_REGISTER(pfRemoteConsole); + GETINTERFACE_ANY(pfRemoteConsole, plReceiver); + + bool MsgReceive(plMessage* msg) override; + + void Init(); + +private: + std::thread fThread; + + std::string fOutput; +}; + +#endif // pfRemoteConsole_inc diff --git a/Sources/Plasma/FeatureLib/pfMessage/CMakeLists.txt b/Sources/Plasma/FeatureLib/pfMessage/CMakeLists.txt index f69c592bb5..5120844ac3 100644 --- a/Sources/Plasma/FeatureLib/pfMessage/CMakeLists.txt +++ b/Sources/Plasma/FeatureLib/pfMessage/CMakeLists.txt @@ -19,6 +19,7 @@ set(pfMessage_HEADERS pfMarkerMsg.h pfMessageCreatable.h pfMovieEventMsg.h + pfRemoteConsoleMsg.h plArmatureEffectMsg.h plClothingMsg.h ) diff --git a/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h b/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h index 28e9aadb99..c4cd9895f2 100644 --- a/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h +++ b/Sources/Plasma/FeatureLib/pfMessage/pfMessageCreatable.h @@ -94,4 +94,7 @@ REGISTER_CREATABLE(pfMarkerMsg); #include "pfMovieEventMsg.h" REGISTER_CREATABLE(pfMovieEventMsg); +#include "pfRemoteConsoleMsg.h" +REGISTER_NONCREATABLE(pfRemoteConsoleMsg); + #endif //pfMessageCreatable_inc diff --git a/Sources/Plasma/FeatureLib/pfMessage/pfRemoteConsoleMsg.h b/Sources/Plasma/FeatureLib/pfMessage/pfRemoteConsoleMsg.h new file mode 100644 index 0000000000..a2cae93d72 --- /dev/null +++ b/Sources/Plasma/FeatureLib/pfMessage/pfRemoteConsoleMsg.h @@ -0,0 +1,92 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _pfRemoteConsoleMsg_h +#define _pfRemoteConsoleMsg_h + +#include +#include "pnMessage/plMessage.h" +#include +#include + +class pfRemoteConsoleMsgOutput { +public: + ST::string fOutputData; + // Ideally we'd use a binary semaphore, but that requires c++20... + std::condition_variable fCondvar; + + std::mutex fOutputDataSetLock; + bool fOutputDataSet; +}; + +class pfRemoteConsoleMsg : public plMessage +{ +protected: + ST::string fCmd; + std::shared_ptr fOutput; + +public: + pfRemoteConsoleMsg() : fCmd(), fOutput() {} + pfRemoteConsoleMsg(const ST::string& cmd) : fCmd(cmd), fOutput(new pfRemoteConsoleMsgOutput()) { + SetBCastFlag(plMessage::kBCastByExactType); + } + + CLASSNAME_REGISTER(pfRemoteConsoleMsg); + GETINTERFACE_ANY(pfRemoteConsoleMsg, plMessage); + + void Read(hsStream* stream, hsResMgr* mgr) override { + FATAL("wtf are you doing???"); + } + void Write(hsStream* stream, hsResMgr* mgr) override { + FATAL("wtf are you doing???"); + } + + ST::string GetCommand() const { + return this->fCmd; + } + + std::shared_ptr GetOutput() const { + return this->fOutput; + } +}; + +#endif // _pfRemoteConsoleMsg_h diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index f9ef29986f..2902ca7c07 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -369,6 +369,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration), CLASS_INDEX(plPXSubWorld), CLASS_INDEX(pfConfirmationMgr), + CLASS_INDEX(pfRemoteConsole), //--------------------------------------------------------- // Keyed objects above this line, unkeyed (such as messages) below.. //--------------------------------------------------------- @@ -959,6 +960,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plSDLModifierStateMsg), CLASS_INDEX(plConfirmationMsg), CLASS_INDEX(plLocalizedConfirmationMsg), + CLASS_INDEX(pfRemoteConsoleMsg), CLASS_INDEX_LIST_END #endif // plCreatableIndex_inc diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp index 4ca9f8f5e0..19bab9cfcb 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.cpp @@ -114,6 +114,7 @@ static constexpr plKeySeed SeedList[] = { { kAgeLoader_KEY, CLASS_INDEX_SCOPED( plAgeLoader), "kAgeLoader_KEY", }, { kBuiltIn3rdPersonCamera_KEY, CLASS_INDEX_SCOPED( plCameraModifier1 ), "kBuiltIn3rdPersonCamera_KEY", }, { kConfirmationMgr_KEY, CLASS_INDEX_SCOPED( pfConfirmationMgr ), "kConfirmationMgr_KEY", }, + { kRemoteConsoleObject_KEY, CLASS_INDEX_SCOPED( pfRemoteConsole ), "kRemoteConsoleObject_KEY", }, { kLast_Fixed_KEY, CLASS_INDEX_SCOPED( plSceneObject ), "kLast_Fixed_KEY", } }; diff --git a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h index 803d9b2948..86c919347d 100644 --- a/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h +++ b/Sources/Plasma/NucleusLib/pnKeyedObject/plFixedKey.h @@ -86,6 +86,7 @@ enum plFixedKeyId kAgeLoader_KEY, kBuiltIn3rdPersonCamera_KEY, kConfirmationMgr_KEY, + kRemoteConsoleObject_KEY, kLast_Fixed_KEY };