Files
InstanceBrick--nightcap/src/runtime/runtime_config_bootstrap.cc
T
komaruworld 035c7fe1d4 Import theme from roblox and fix x11
vkAcquireNextImageKHR(): A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully. (VK_SUBOPTIMAL_KHR) (1000001003)
2026-08-31 21:42:35 +03:00

329 lines
11 KiB
C++

#include "runtime/runtime_config_bootstrap.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <string>
#include <vector>
namespace mocktail {
namespace runtime {
namespace {
constexpr std::string_view kDefaultRuntimeConfig =
R"yaml(# Mocktail configuration.
# The file is created once with mode 0600. Mocktail never replaces your edits.
# Integer: configuration schema version. Only version 1 is supported.
version: 1
# One-line device preset. Short aliases pc, mobile, and console are accepted.
# Presets: pc-windows-11, mobile-pixel-7, console-ps5.
# Use the mapping below for a custom profile.
device: pc-windows-11
# Detailed alternative (replace the scalar device line above):
# device:
# type: mobile
# platform: Android
# name: Google Pixel 9 Pro
# manufacturer: Google
# model: Pixel 9 Pro
# brand: google
# code: komodo
# sku: pixel-9-pro
# soc_model: Google Tensor G4
# touch: true
# mouse: false
# keyboard: false
runtime:
# Boolean (default: false): start without a visible SDL window.
headless: false
# String (optional): use a specific x86_64 libroblox.so instead of the
# active verified payload.
# roblox_library: /absolute/path/to/libroblox.so
appearance:
# String (default: roblox): use Roblox's saved account theme. Supported
# overrides: dark, light, or system (follow the desktop color scheme).
theme: roblox
graphics:
# String (default: direct-vulkan): graphics backend. Use opengl on hardware
# without a working Vulkan driver; OpenGL ES 3.0 or newer is required.
# Recommended values: direct-vulkan, opengl, system, angle-vulkan.
backend: direct-vulkan
# Optional string or integer: Mocktail frame-rate override. Leave commented
# (or use -1) to let Roblox Basic Settings own the cap. Supported overrides:
# display, unlimited, or any positive integer; fixed values are forwarded to
# DFIntTaskSchedulerTargetFps without a whitelist.
# frame_rate_limit: -1
# Optional presentation synchronization override: auto, on, or off.
# vsync: off
performance:
# Boolean (default: false): size Roblox scheduler/render queues from every
# physical CPU core. A place's Lua/main thread can still remain serial.
multithreaded_rendering: false
# String (default: throughput): `throughput` makes every physical core
# available to Roblox worker pools; `latency` preserves Roblox-managed pool
# sizes and coalesces midphase work. Supported: auto, latency, throughput.
physics_worker_mode: throughput
# Integer MiB (default: 0): hard RAM cap for the Mocktail process tree.
# 0 disables the cap. cgroup v2 also disables swap; other Linux systems use
# an RSS+swap watchdog. 6144 is a conservative starting point for 32 GiB RAM.
memory_limit_mb: 0
# String (default: auto): request Feral GameMode when its host daemon and
# client library are available. Supported values: auto, on, off.
gamemode: auto
audio:
# String (default: default): `default` follows the host system output.
# To pin output, copy an exact SDL device name printed during startup.
output_device: default
# String (default: default): input device name, `id:<number>` printed during
# startup, or `disabled`. IDs disambiguate duplicate names but may change
# between boots; prefer the exact device name when it is unique.
input_device: default
integrations:
discord_rpc:
# Boolean (default: false): publish Mocktail activity to Discord Desktop.
# This never signs in to Discord and never reads an account token.
enabled: false
# Boolean (default: true): show the current Roblox experience name.
show_place_name: true
# Boolean (default: true): show how long the current session has run.
show_elapsed_time: true
join:
# Boolean (default: true): let friends open the current experience. When
# Roblox provides a public server ID, the button targets that server.
enabled: true
# Boolean (default: true): never expose private or reserved joins.
public_servers_only: true
# button_label: Join Server
# English defaults. Uncomment only the lines you want to customize.
# text:
# browsing: Browsing experiences
# joining: Joining an experience
# playing: "{place_name}"
# state: Playing Roblox
# unknown_place: Unknown experience
# Advanced: overrides the application bundled by the package maintainer.
# application_id: 123456789012345678
window:
# Integer (default: 1280): initial window width in logical desktop units.
width: 1280
# Integer (default: 720): initial window height in logical desktop units.
height: 720
# String (default: Roblox): window title.
title: Roblox
# Boolean (default: false): render at physical display-pixel density instead
# of the logical desktop resolution. Enable only for sharper high-DPI output.
high_dpi: false
network:
# Boolean (default: false): follow the host HTTP/SOCKS5 proxy and
# automatically use its current port. Do not combine with a fixed proxy.
use_system_proxy: false
# Optional fixed HTTP proxy. Uncomment both values together. The host must
# not contain a scheme such as http://.
# String (optional, default: disabled): proxy host name or IP address.
# proxy_host: 127.0.0.1
# Integer (optional, default: disabled): proxy TCP port from 1 to 65535.
# proxy_port: 8080
# Absolute path (optional): custom PEM CA bundle. Mocktail reads this file
# directly and does not replace it during Roblox payload updates.
# ca_bundle: /home/user/.config/mocktail/cacert.pem
updates:
# Boolean (default: true): check the latest Roblox version, derive an exact
# HostAbi profile, run two isolated canaries with the selected graphics
# backend, and promote only on success. An existing current payload is
# preserved when probation fails.
automatic: true
# String (default: apk-pure): direct x86_64 APK provider selection. Both
# accepted values resolve the provider chain, which currently starts with
# APKPure. Supported values: auto, apk-pure.
source: apk-pure
# Reserved for desktop update integrations. `mocktail_updater` itself never
# launches another process after changing the active payload.
launch_after_update: false
)yaml";
std::string ErrorMessage(std::string_view action, int error_number) {
return std::string(action) + ": " + std::strerror(error_number);
}
bool WriteAll(int file, std::string_view contents, std::string* error) {
const char* next = contents.data();
std::size_t remaining = contents.size();
while (remaining != 0) {
const ssize_t written = write(file, next, remaining);
if (written < 0) {
if (errno == EINTR) {
continue;
}
*error = ErrorMessage("cannot write first-run configuration", errno);
return false;
}
next += written;
remaining -= static_cast<std::size_t>(written);
}
return true;
}
bool InspectExistingConfig(const std::filesystem::path& config_file,
bool* exists, std::string* error) {
struct stat metadata = {};
if (lstat(config_file.c_str(), &metadata) != 0) {
if (errno == ENOENT) {
*exists = false;
return true;
}
*error = ErrorMessage("cannot inspect first-run configuration", errno);
return false;
}
*exists = true;
if (!S_ISREG(metadata.st_mode)) {
*error = S_ISLNK(metadata.st_mode)
? "refusing symlink runtime configuration"
: "runtime configuration must be a regular file";
return false;
}
return true;
}
} // namespace
std::string_view DefaultRuntimeConfigYaml() { return kDefaultRuntimeConfig; }
RuntimeConfigBootstrapResult EnsureRuntimeConfigFile(
const std::filesystem::path& config_file) {
RuntimeConfigBootstrapResult result;
if (config_file.empty() || config_file.filename().empty()) {
result.error = "first-run configuration path must name a file";
return result;
}
bool exists = false;
if (!InspectExistingConfig(config_file, &exists, &result.error)) {
return result;
}
if (exists) {
return result;
}
std::filesystem::path parent = config_file.parent_path();
if (parent.empty()) {
parent = ".";
}
std::error_code filesystem_error;
const bool parent_created =
std::filesystem::create_directories(parent, filesystem_error);
if (filesystem_error) {
result.error = "cannot create configuration directory " + parent.string() +
": " + filesystem_error.message();
return result;
}
if (parent_created) {
const int parent_directory =
open(parent.c_str(), O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW);
if (parent_directory < 0 || fchmod(parent_directory, S_IRWXU) != 0) {
const int operation_error = errno;
if (parent_directory >= 0) {
(void)close(parent_directory);
}
result.error = ErrorMessage("cannot secure configuration directory",
operation_error);
return result;
}
if (close(parent_directory) != 0) {
result.error =
ErrorMessage("cannot close configuration directory", errno);
return result;
}
}
std::string temporary_pattern =
(parent / ("." + config_file.filename().string() + ".tmp.XXXXXX"))
.string();
std::vector<char> temporary_path(temporary_pattern.begin(),
temporary_pattern.end());
temporary_path.push_back('\0');
const int temporary_file = mkstemp(temporary_path.data());
if (temporary_file < 0) {
result.error = ErrorMessage("cannot create temporary configuration", errno);
return result;
}
const auto discard_temporary = [&temporary_path]() {
(void)unlink(temporary_path.data());
};
if (fcntl(temporary_file, F_SETFD, FD_CLOEXEC) != 0 ||
fchmod(temporary_file, S_IRUSR | S_IWUSR) != 0) {
const int operation_error = errno;
(void)close(temporary_file);
discard_temporary();
result.error =
ErrorMessage("cannot secure temporary configuration", operation_error);
return result;
}
if (!WriteAll(temporary_file, kDefaultRuntimeConfig, &result.error)) {
(void)close(temporary_file);
discard_temporary();
return result;
}
if (fsync(temporary_file) != 0) {
const int operation_error = errno;
(void)close(temporary_file);
discard_temporary();
result.error = ErrorMessage("cannot synchronize first-run configuration",
operation_error);
return result;
}
if (close(temporary_file) != 0) {
const int operation_error = errno;
discard_temporary();
result.error =
ErrorMessage("cannot close first-run configuration", operation_error);
return result;
}
if (link(temporary_path.data(), config_file.c_str()) != 0) {
const int publish_error = errno;
discard_temporary();
if (publish_error == EEXIST) {
bool raced_entry_exists = false;
if (!InspectExistingConfig(config_file, &raced_entry_exists,
&result.error)) {
return result;
}
if (!raced_entry_exists) {
result.error = "runtime configuration disappeared during creation";
}
return result;
}
result.error =
ErrorMessage("cannot publish first-run configuration", publish_error);
return result;
}
result.status = RuntimeConfigBootstrapStatus::kCreated;
if (unlink(temporary_path.data()) != 0) {
result.error = ErrorMessage(
"configuration was created but its temporary link cannot be removed",
errno);
}
return result;
}
} // namespace runtime
} // namespace mocktail