Skip to content

Commit

Permalink
feat: Add embedded updater
Browse files Browse the repository at this point in the history
shdwmtr committed Dec 31, 2024
1 parent 900b7eb commit 24de43b
Showing 12 changed files with 397 additions and 151 deletions.
6 changes: 3 additions & 3 deletions src/api/executor.cc
Original file line number Diff line number Diff line change
@@ -117,11 +117,11 @@ PyObject* CallFrontendMethod(PyObject* self, PyObject* args, PyObject* kwargs)
return JavaScript::EvaluateFromSocket(
// Check the the frontend code is actually loaded aside from SteamUI
fmt::format(
"if (typeof MillenniumFrontEndError === 'undefined') {{ class MillenniumFrontEndError extends Error {{ constructor(message) {{ super(message); this.name = 'MillenniumFrontEndError'; }} }} }}"
"if (typeof PLUGIN_LIST === 'undefined' || !PLUGIN_LIST?.['{}']) throw new MillenniumFrontEndError('frontend not loaded yet!');\n\n{}",
"if (typeof window !== 'undefined' && typeof window.MillenniumFrontEndError === 'undefined') {{ window.MillenniumFrontEndError = class MillenniumFrontEndError extends Error {{ constructor(message) {{ super(message); this.name = 'MillenniumFrontEndError'; }} }} }}"
"if (typeof PLUGIN_LIST === 'undefined' || !PLUGIN_LIST?.['{}']) throw new window.MillenniumFrontEndError('frontend not loaded yet!');\n\n{}",
pluginName,
script
)
)
);
}

5 changes: 2 additions & 3 deletions src/core/co_initialize/co_stub.cc
Original file line number Diff line number Diff line change
@@ -274,8 +274,7 @@ void OnBackendLoad(uint16_t ftpPort, uint16_t ipcPort)

auto socketEmitterThread = std::thread([&]
{
JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg",
[&] (const nlohmann::json& eventMessage, int listenerId)
JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", "OnBackendLoad", [&](const nlohmann::json& eventMessage, std::string listenerId)
{
std::unique_lock<std::mutex> lock(mtx);

@@ -349,7 +348,7 @@ const void CoInitializer::InjectFrontendShims(uint16_t ftpPort, uint16_t ipcPort

Logger.Log("Preparing to inject frontend shims...");

JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", [&](const nlohmann::json& eventMessage, int listenerId)
JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", "InjectFrontendShims", [&](const nlohmann::json& eventMessage, std::string listenerId)
{
std::lock_guard<std::mutex> lock(mtx);

18 changes: 8 additions & 10 deletions src/core/ffi/ffi.h
Original file line number Diff line number Diff line change
@@ -58,15 +58,14 @@ namespace JavaScript {
Types type;
};

using EventHandler = std::function<void(const nlohmann::json& eventMessage, int listenerId)>;
using EventHandler = std::function<void(const nlohmann::json& eventMessage, std::string listenerId)>;

class SharedJSMessageEmitter {
private:
SharedJSMessageEmitter() {}

std::unordered_map<std::string, std::vector<std::pair<int, EventHandler>>> events;
std::unordered_map<std::string, std::vector<std::pair<std::string, EventHandler>>> events;
std::unordered_map<std::string, std::vector<nlohmann::json>> missedMessages; // New data structure for missed messages
int nextListenerId = 0;

public:
SharedJSMessageEmitter(const SharedJSMessageEmitter&) = delete;
@@ -77,22 +76,21 @@ namespace JavaScript {
return InstanceRef;
}

int OnMessage(const std::string& event, EventHandler handler) {
int listenerId = nextListenerId++;
events[event].push_back(std::make_pair(listenerId, handler));
std::string OnMessage(const std::string& event, const std::string name, EventHandler handler) {
events[event].push_back(std::make_pair(name, handler));

// Deliver any missed messages
auto it = missedMessages.find(event);
if (it != missedMessages.end()) {
for (const auto message : it->second) {
handler(message, listenerId);
handler(message, name);
}
missedMessages.erase(it); // Clear missed messages once delivered
}
return listenerId;
return name;
}

void RemoveListener(const std::string& event, int listenerId) {
void RemoveListener(const std::string& event, std::string listenerId) {
auto it = events.find(event);
if (it != events.end()) {
auto& handlers = it->second;
@@ -113,7 +111,7 @@ namespace JavaScript {
try {
handler.second(data, handler.first);
} catch (const std::bad_function_call& e) {
Logger.Warn("Failed to emit message. exception: {}", e.what());
Logger.Warn("Failed to emit message on {}. exception: {}", handler.first, e.what());
}
}
} else {
2 changes: 1 addition & 1 deletion src/core/ffi/javascript.cc
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ const EvalResult ExecuteOnSharedJsContext(std::string javaScriptEval)
throw std::runtime_error("couldn't send message to socket");
}

int listenerId = JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", [&](const nlohmann::json& eventMessage, int listenerId)
std::string listenerId = JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", "ExecuteOnSharedJsContext", [&](const nlohmann::json& eventMessage, std::string listenerId)
{
std::lock_guard<std::mutex> lock(mtx); // Lock mutex for safe access

2 changes: 1 addition & 1 deletion src/core/hooks/csp_bypass.h
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ const void BypassCSP(void)
// The CSP is implemented by the web server and enforced by the web browser.
// The CSP is a set of rules that the web server sends to the web browser to tell it what content is allowed to be loaded

JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", [&] (const nlohmann::json& message, int listenerId)
JavaScript::SharedJSMessageEmitter::InstanceRef().OnMessage("msg", "BypassCSP", [&](const nlohmann::json& message, std::string listenerId)
{
try
{
91 changes: 0 additions & 91 deletions src/git/vcs.h

This file was deleted.

38 changes: 9 additions & 29 deletions src/main.cc
Original file line number Diff line number Diff line change
@@ -15,38 +15,21 @@
#include <signal.h>
#include <cxxabi.h>
#include <pipes/terminal_pipe.h>
#include <git/vcs.h>
#include <api/executor.h>

class Preload
const static void VerifyEnvironment()
{
public:
Preload()
{
this->VerifyEnvironment();
}

~Preload() { }
const auto filePath = SystemIO::GetSteamPath() / ".cef-enable-remote-debugging";

const void VerifyEnvironment()
// Steam's CEF Remote Debugger isn't exposed to port 8080
if (!std::filesystem::exists(filePath))
{
const auto filePath = SystemIO::GetSteamPath() / ".cef-enable-remote-debugging";

// Steam's CEF Remote Debugger isn't exposed to port 8080
if (!std::filesystem::exists(filePath))
{
std::ofstream(filePath).close();
std::ofstream(filePath).close();

Logger.Log("Successfully enabled CEF remote debugging, you can now restart Steam...");
std::exit(1);
}
Logger.Log("Successfully enabled CEF remote debugging, you can now restart Steam...");
std::exit(1);
}

const void Start()
{
CheckForUpdates();
}
};
}

void OnTerminate()
{
@@ -100,10 +83,7 @@ const static void EntryMain()
uint16_t ftpPort = Crow::CreateAsyncServer();

const auto startTime = std::chrono::system_clock::now();
{
std::unique_ptr<Preload> preload = std::make_unique<Preload>();
preload->Start();
}
VerifyEnvironment();

std::shared_ptr<PluginLoader> loader = std::make_shared<PluginLoader>(startTime, ftpPort);
SetPluginLoader(loader);
11 changes: 11 additions & 0 deletions win32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.10)

set(BUILD_SHARED_LIBS OFF)

# Set the project name
project(ShimDll)

@@ -16,9 +18,18 @@ elseif(UNIX AND NOT GITHUB_ACTION_BUILD)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "$ENV{HOME}/.millennium/")
endif()

find_package(CURL REQUIRED) # used for web requests.
find_package(unofficial-minizip CONFIG REQUIRED) # used for extracting zip files

add_compile_definitions(
CURL_STATICLIB
)

# Add the executable
add_library(ShimDll SHARED main.cc)

target_link_libraries(ShimDll CURL::libcurl unofficial::minizip::minizip)

set_target_properties(ShimDll PROPERTIES OUTPUT_NAME "user32")
set_target_properties(ShimDll PROPERTIES PREFIX "")
set_target_properties(ShimDll PROPERTIES NO_EXPORT TRUE)
82 changes: 82 additions & 0 deletions win32/http.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include <curl/curl.h>
#include <fstream>
#include <iostream>
#include <bits/this_thread_sleep.h>

size_t write_file_callback(void* contents, size_t size, size_t nmemb, void* userp) {
std::ofstream* file = static_cast<std::ofstream*>(userp);
size_t totalSize = size * nmemb;
file->write(static_cast<char*>(contents), totalSize);
return totalSize;
}

size_t write_callback(char* ptr, size_t size, size_t nmemb, std::string* data) {
data->append(ptr, size * nmemb);
return size * nmemb;
}

bool download_file(const std::string& url, const std::string& outputFilePath) {
CURL* curl;
CURLcode res;
std::ofstream outputFile;

curl = curl_easy_init();
if (!curl) {
std::cerr << "Failed to initialize CURL." << std::endl;
return false;
}

outputFile.open(outputFilePath, std::ios::binary);
if (!outputFile.is_open()) {
std::cerr << "Failed to open file: " << outputFilePath << std::endl;
curl_easy_cleanup(curl);
return false;
}

curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outputFile);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Millennium/1.0");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);

res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "CURL error: " << curl_easy_strerror(res) << std::endl;
outputFile.close();
curl_easy_cleanup(curl);
return false;
}

// Clean up
outputFile.close();
curl_easy_cleanup(curl);
return true;
}

const std::string get(const char* url, bool retry = true) {
CURL* curl;
CURLcode res;
std::string response;
curl = curl_easy_init();

if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "Millennium/1.0");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Follow redirects

while (true) {
res = curl_easy_perform(curl);

if (!retry || res == CURLE_OK) {
break;
}

std::cerr << "res: " << res << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(3));
}
curl_easy_cleanup(curl);
}
return response;
}
Loading

0 comments on commit 24de43b

Please sign in to comment.