Project layout
Style guide
Runtime bundle
App module
Core module
Minecraft module
UI module
Bus
FAQ / Troubleshooting
Links
Tools
- Copy
config.ini.exampletoconfig.iniand adjust background, window size, update endpoints, and paths. - Language packs live in
lang/; assets inresource/img/.
If the build generates tests, you can run them via CTest:
ctest --test-dir build --build-config ReleaseWhat a complete runtime directory should contain:
config.ini(optional; auto-generated on first run if missing).langs/with language JSON files. Copy from the repositorylangs/into your working directory so translations load.themes/containing theme JSON (sample themes included).img/loading.giffor the loading page. You can copy the provided asset fromresource/img/(free for commercial use) or replace with your own.- If you build with dynamic linking, copy the required runtime libraries (
.dllon Windows) alongside the executable.
src/— launcher code (UI pages, windows, dialogs, logic)include/— public headerslang/— localization JSON filesresource/— images/icons used by the UItools/— helper scripts/toolsdoc/— documentation
- Use CamelCase naming convention: For type definitions, use an uppercase initial letter, variables and functions use a lowercase initial letter. Specifically:
void func();
std::string myName;
struct Name {} name;
namespace neko {};-
Use 4-space indentation per level. Source files should use UTF-8 encoding.
-
In this project, use double quotes
""to include headers from within the project, and angle brackets<>for external headers. This helps quickly distinguish between internal and external headers. -
Organize
#includestatements into categories, such as:
// Neko Modules
#include <neko/network/network.hpp>
// NekoLc project
#include "neko/function/lang.hpp"
#include "neko/function/info.hpp"
// STL
#include <string>
#include <iostream>
// Qt Modules
#include <QtWidgets/QLabel>
// Qt small category: Button-related
#include <QtWidgets/QButtonGroup>
#include <QtWidgets/QPushButton>
// Other
#include <curl/curl.h>-
Use
#pragma onceat the top of header files. Avoid introducingusing namespacein the global scope within header files. -
Always include curly braces
{}forfororwhileloops.
Lifecycle, configuration, localization, and app metadata.
- Key headers:
app.hpp,appinit.hpp,clientConfig.hpp,configManager.hpp,lang.hpp,appinfo.hpp,api.hpp,nekoLc.hpp. - Boot and config access:
neko::app::init::initialize(); // logging, language, config, network bootstrap
auto cfg = neko::bus::config::getClientConfig();
neko::bus::config::updateClientConfig([](neko::ClientConfig &c) {
c.main.lang = "en";
});
neko::bus::config::save(neko::app::getConfigFileName());
const auto title = neko::lang::tr(neko::lang::keys::maintenance::category,
neko::lang::keys::maintenance::title);- Config is INI-based (SimpleIni). Localizations are UTF-8 JSON; all access is thread-safe through the config bus.
Launcher services for update/maintenance, process launch, remote config, feedback/auth, and poster downloads.
- Key headers:
update.hpp,maintenance.hpp,launcher.hpp,launcherProcess.hpp,remoteConfig.hpp,auth.hpp,feedback.hpp,downloadPoster.hpp. - Typical update flow:
// One-shot auto update (publishes UI progress events, may exit on maintenance)
neko::core::update::autoUpdate();
// Manual steps
if (auto raw = neko::core::update::checkUpdate()) {
auto resp = neko::core::update::parseUpdate(*raw);
neko::core::update::update(resp);
}- Update and maintenance emit bus events consumed by the UI loading page and notice dialogs.
Minecraft-specific auth, download sources, installation, and launch wiring.
- Key headers:
installMinecraft.hpp,downloadSource.hpp,launcherMinecraft.hpp,authMinecraft.hpp,minecraftSubscribe.hpp. - Install/download (blocking; keep off the UI thread):
// Download all assets for a version into the target directory
neko::minecraft::setupMinecraftDownloads(neko::minecraft::DownloadSource::Official,
"1.20.1",
versionJson,
"./.minecraft");
// Or do a full install with defaults
neko::minecraft::installMinecraft("./.minecraft", "1.20.1");- Build and launch:
neko::minecraft::LauncherMinecraftConfig launchCfg{
.minecraftFolder = "./.minecraft",
.targetVersion = "1.20.1",
.javaPath = "java",
.playerName = "Steve",
.uuid = "...",
.accessToken = "...",
};
auto cmd = neko::minecraft::getLauncherMinecraftCommand(launchCfg);
// Or execute and hook callbacks
neko::minecraft::launcherMinecraft(clientConfig,
[](){ /* onStart */ },
[](int code){ /* onExit */ });- Authlib injector fields live in
LauncherMinecraftConfig::authlib. Exceptions surface for network/file/parse errors; progress should be surfaced via bus events.
Goal: emit UI actions from any thread/process code while keeping UI work on the Qt main thread. Wire it once, then publish events.
- During UI startup, point the dispatcher at the main window and subscribe the UI to the bus:
ui::UiEventDispatcher::setNekoWindow(&window);
ui::subscribeToUiEvent();- If you hold a direct
NekoWindow*, you can also call the instance methods below instead of publishing events.
- Message type:
neko::ui::NoticeMsg(title,message, optionalposterPath,buttonText,callback,autoClose,defaultButtonIndex). - Fire via bus:
neko::ui::NoticeMsg notice{
.title = "Update finished",
.message = "All files are up to date.",
.buttonText = {"OK", "Open folder"},
.callback = [](neko::uint32 index) {
if (index == 1) { /* open folder */ }
}
};
bus::event::publish<neko::event::ShowNoticeEvent>(notice);- Default behavior: if
buttonTextis empty, a single OK button is shown.autoClose(ms) will trigger the callback withdefaultButtonIndex.
- Message type:
neko::ui::InputMsg(title,message, optionalposterPath,lineTextplaceholders,callback<bool confirmed>). - Show and collect input:
neko::ui::InputMsg login{
.title = "Login",
.message = "Enter account and password",
.lineText = {"Account", "Password"},
.callback = [](bool confirmed) {
auto *win = neko::ui::UiEventDispatcher::getNekoWindow();
if (!confirmed || !win) { return; }
const auto lines = win->getLines(); // mirrors input order
win->hideInput(); // close the dialog manually
// use lines[0] (account), lines[1] (password)
}
};
bus::event::publish<neko::event::ShowInputEvent>(login);- Notes: placeholders auto-fill if
lineTextis empty. The dialog does not auto-hide; callHideInputEventorNekoWindow::hideInput()after you process the data.
- Message type:
neko::ui::LoadingMsgwithtype(OnlyRaw,Text,Progress,All),processtext, optionalh1/h2/message,posterPath,loadingIconPath,speed,progressVal,progressMax. - Show and drive progress:
bus::event::publish<neko::event::ShowLoadingEvent>(neko::ui::LoadingMsg{
.type = neko::ui::LoadingMsg::Type::All,
.h1 = "Preparing",
.h2 = "Step 1/3",
.message = "Fetching metadata...",
.progressVal = 0,
.progressMax = 100,
});
bus::event::publish<neko::event::LoadingStatusChangedEvent>({.statusMessage = "Downloading..."});
bus::event::publish<neko::event::LoadingValueChangedEvent>({.progressValue = 40});- Direct calls if you have the window:
window.showLoading(msg),window.setLoadingValueD(value),window.setLoadingStatusD(text). - The loading GIF defaults to
img/loading.gif; override withloadingIconPathand adjust animation speed viaspeed(percent).
- Switch pages through the bus:
bus::event::publish<neko::event::CurrentPageChangeEvent>({.page = ui::Page::home}); - To return to home after launch start: publish
LaunchStartedEvent(UI subscribes automatically). - Notice dialog cleanup helpers exist (
resetNoticeStateD,resetNoticeButtonsD) but are wired internally byNekoWindow.
NekoBus is essentially a singleton-wrapped bus that lets you access and manipulate the bus object via static methods without directly calling the singleton. This reduces global state and makes the code easier to test and maintain.
-
CMake cannot find Qt/Boost
- Set
NEKO_LC_LIBRARY_PATHto the CMake package roots (semicolon-separated on Windows). Example:
$env:NEKO_LC_LIBRARY_PATH = "C:/Qt/6.6.3/mingw_64/lib/cmake;C:/local/boost_1_83_0/lib/cmake/Boost" cmake -S . -B build -DNEKO_LC_LIBRARY_PATH="$env:NEKO_LC_LIBRARY_PATH" cmake --build build --config Release
- Ensure the path points to the directories containing
Qt6Config.cmakeorBoostConfig.cmake/boost-config.cmake. If using vcpkg, use its installed package cmake folders.
- Set
-
Qt GIF loading looks broken in loading page
- Verify
img/loading.gifexists in the runtime working directory; override withloadingIconPathinLoadingMsgif you deploy a different asset.
- Verify
-
Input dialog does not close after callback
- The dialog stays open by design; call
HideInputEventorNekoWindow::hideInput()after you consumegetLines().
- The dialog stays open by design; call
Update: Update tool, which should be distributed with the client. It is used to perform Core updates. It replaces the NekoCore files and then restarts NekoLc.
cmake --build build --target NekoLcUpdateTool --config Release