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
};