Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,18 @@ Makefile
.ninja_log
*.ninja

deploy/
# Per-deploy state and large game assets stay untracked, but
# `deploy/system/*.json` (decoded MST data the server reads at boot)
# must be committed — without it, fresh checkouts can't run the server.
deploy/gme.sqlite
deploy/log/
deploy/game_content/
deploy/config.json
config.json

# IDA Pro audit tooling — kept off the public repo because the binary
# pseudocode dumps are private reverse-engineering artifacts.
tools/ida/
vcpkg_installed/
.idea
.vscode
Expand All @@ -490,3 +500,11 @@ generated/
# Project-specific
CONTEXT.md
out/

# Local dev tooling + docs — not for upstream
tools/
*.md

# Local-only debug CLI (dev tool, kept out of upstream)
standalone_frontend/DebugCli.cpp
standalone_frontend/DebugCli.hpp
75 changes: 75 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,82 @@ find_package(Drogon CONFIG REQUIRED)
find_package(unofficial-sqlite3 CONFIG REQUIRED)
find_package(glaze CONFIG REQUIRED) # c++ JSON abstraction

# glaze's vcpkg port pins /GL (whole-program opt) and /LTCG on its imported
# target for Release/MinSizeRel. Those flags propagate through gimuserver to
# every consumer, and MSVC's LTCG code-gen pass chokes on the coroutine
# HANDLEF bodies (UserInfo.cpp, GachaAction.cpp) with C4737 -> LNK1257.
# Drop the LTCG flags so Release links cleanly. We give up cross-TU inlining
# from glaze; the tradeoff is the build actually completing.
if (MSVC AND TARGET glaze::glaze)
set_property(TARGET glaze::glaze PROPERTY INTERFACE_COMPILE_OPTIONS
"/Zc:preprocessor;/permissive-;/Zc:lambda")
set_property(TARGET glaze::glaze PROPERTY INTERFACE_LINK_OPTIONS
"$<$<CONFIG:Release>:/INCREMENTAL:NO>;$<$<CONFIG:MinSizeRel>:/INCREMENTAL:NO>")
endif()

add_subdirectory(packet-generator/assets/runtime/cpp)

# KDL -> C++ codegen. The packet-generator Rust CLI compiles assets/all.kdl
# into a single flat header (all.hpp) that gimuserver includes. The first
# build compiles the Rust generator itself (one-time, a few minutes); every
# subsequent build only re-runs it when a .kdl file has changed.
find_program(CARGO_EXECUTABLE cargo
DOC "Rust/Cargo toolchain, required to build packet-generator"
)
if (NOT CARGO_EXECUTABLE)
message(FATAL_ERROR
"cargo not found on PATH. Install Rust from https://rustup.rs/ "
"and re-run CMake configure."
)
endif()

set(PKGEN_SRC_DIR "${CMAKE_SOURCE_DIR}/packet-generator")
set(PKGEN_ENTRY "assets/all.kdl")
# App.hpp includes <gimuserver/packets/all.hpp>, so emit directly into the
# source tree under gimuserver/packets/. That directory has a .gitignore
# (*.hpp) that keeps the generated header out of version control.
set(PKGEN_OUT_DIR "${CMAKE_SOURCE_DIR}/gimuserver/packets")
set(PKGEN_OUT_HEADER "${PKGEN_OUT_DIR}/all.hpp")

file(GLOB_RECURSE KDL_SCHEMAS CONFIGURE_DEPENDS
"${PKGEN_SRC_DIR}/assets/*.kdl"
)

file(MAKE_DIRECTORY "${PKGEN_OUT_DIR}")

# The archive schema (assets/archive.kdl) generates a second header consumed
# by gimuserver/archive/*Archiver; gimuserver/archive/.gitignore keeps it out
# of version control just like packets/all.hpp.
set(PKGEN_ARCHIVE_ENTRY "assets/archive.kdl")
set(PKGEN_ARCHIVE_OUT_DIR "${CMAKE_SOURCE_DIR}/gimuserver/archive")
set(PKGEN_ARCHIVE_OUT_HEADER "${PKGEN_ARCHIVE_OUT_DIR}/archive.hpp")

add_custom_command(
OUTPUT "${PKGEN_OUT_HEADER}" "${PKGEN_ARCHIVE_OUT_HEADER}"
COMMAND "${CARGO_EXECUTABLE}" run --release --
generate --cxx --glaze
-i "${PKGEN_ENTRY}"
-o "${PKGEN_OUT_DIR}"
COMMAND "${CARGO_EXECUTABLE}" run --release --
generate --cxx --glaze
-i "${PKGEN_ARCHIVE_ENTRY}"
-o "${PKGEN_ARCHIVE_OUT_DIR}"
WORKING_DIRECTORY "${PKGEN_SRC_DIR}"
DEPENDS ${KDL_SCHEMAS}
COMMENT "Regenerating C++ packet headers from KDL schemas"
VERBATIM
)

add_custom_target(pkgen_generate ALL
DEPENDS "${PKGEN_OUT_HEADER}" "${PKGEN_ARCHIVE_OUT_HEADER}"
SOURCES ${KDL_SCHEMAS}
)

message(STATUS
"packet-generator: first build compiles the Rust generator (~2-5 min); "
"subsequent builds only re-run it when .kdl files change."
)

add_subdirectory(gimuserver)

if (STANDALONE)
Expand Down
52 changes: 42 additions & 10 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
{
"name": "default-debug",
"displayName": "Config Debug",
"description": "General default debug configuration",
"description": "General default debug configuration (single-config generators only)",
"inherits": "default-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
Expand All @@ -26,7 +26,7 @@
{
"name": "default-release",
"displayName": "Config Release",
"description": "General default release configuration",
"description": "General default release configuration (single-config generators only)",
"inherits": "default-base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
Expand All @@ -35,10 +35,11 @@
},
{
"name": "debug-win64",
"displayName": "Development config for Windows (64-bit)",
"inherits": "default-debug",
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"displayName": "Development config for Windows (Ninja Multi-Config)",
"description": "Multi-config (Debug + Release) build for Windows x64. Must be run from an x64 Native Tools environment (VsDevCmd.bat -arch=amd64 -host_arch=amd64) so the x64-hosted cl.exe is on PATH. Do NOT set CMAKE_C/CXX_COMPILER here — let CMake find the compiler from the environment so it inherits the correct host/target architecture.",
"inherits": "default-base",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
Expand Down Expand Up @@ -78,10 +79,15 @@
},
{
"name": "release-win32",
"displayName": "Deployment for Brave Frontier for Windows",
"inherits": "default-release",
"generator": "Visual Studio 17 2022",
"architecture": "x64",
"displayName": "Deployment for Brave Frontier for Windows (APPX)",
"description": "Multi-config build producing the APPX-embedded server. Requires running from a Developer Command Prompt for Visual Studio.",
"inherits": "default-base",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"architecture": {
"value": "x64",
"strategy": "external"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
Expand All @@ -98,5 +104,31 @@
}
}
}
],
"buildPresets": [
{
"name": "debug-win64-debug",
"configurePreset": "debug-win64",
"configuration": "Debug"
},
{
"name": "debug-win64-release",
"configurePreset": "debug-win64",
"configuration": "Release"
},
{
"name": "release-win32-debug",
"configurePreset": "release-win32",
"configuration": "Debug"
},
{
"name": "release-win32-release",
"configurePreset": "release-win32",
"configuration": "Release"
},
{
"name": "debug-lnx64",
"configurePreset": "debug-lnx64"
}
]
}
5 changes: 4 additions & 1 deletion deploy/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@
"initial_free_gems": 5,
"initial_zel": 10000000,
"initial_karma": 10000000,
"initial_bcoins": 0
"initial_bcoins": 0,
// Client render-loop cap delivered to the offline-proxy via
// GET /offline_mod/fps_cap. 0 disables the client-side cap.
"fps_cap": 60
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions game_frontend/bootstrap_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
#include <Windows.h>
#include "gamefrontend.h"

extern "C" void __stdcall __declspec(dllexport) OfflineMod_startup(void)
// MSVC requires __declspec(dllexport) BEFORE the return type, not after
// __stdcall. The previous declaration order ("extern \"C\" void __stdcall
// __declspec(dllexport) OfflineMod_startup") produced
// error C2059: syntax error: '__declspec(dllexport)'
// on the first real release-win32 build (the only preset that defines
// __APPX__ and reaches this file). The debug-win64 preset doesn't define
// __APPX__, so the #ifdef'd block had never been compiled before.
extern "C" __declspec(dllexport) void __stdcall OfflineMod_startup(void)
{
// TODO: populate
}
}

#endif
1 change: 1 addition & 0 deletions gimuserver/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <gimuserver/controller/AccountController.hpp>
#include <gimuserver/controller/BfWebController.hpp>
#include <gimuserver/controller/GmeController.hpp>
#include <gimuserver/controller/OfflineModController.hpp>
// utility
#include <gimuserver/utils/Drogon.hpp>
#include <gimuserver/utils/Macros.hpp>
11 changes: 8 additions & 3 deletions gimuserver/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
file(GLOB_RECURSE SRC "*.cpp" "*.hpp")
source_group(TREE "${CMAKE_CURRENT_LIST_DIR}" FILES ${SRC})
add_library(gimuserver STATIC ${SRC})
target_link_libraries(gimuserver PUBLIC

# The generated header (packets/all.hpp) is produced by pkgen_generate,
# declared at the project root. Everything in gimuserver transitively
# depends on it via App.hpp, so pin the build-order edge here.
add_dependencies(gimuserver pkgen_generate)

target_link_libraries(gimuserver PUBLIC
# third-party libraries
Drogon::Drogon
cryptopp::cryptopp
Expand All @@ -11,10 +17,9 @@ target_link_libraries(gimuserver PUBLIC
pkgen_cpp
)

target_include_directories(gimuserver
target_include_directories(gimuserver
PUBLIC
../
${CMAKE_CURRENT_BINARY_DIR}/generated/
PRIVATE
.
)
Expand Down
19 changes: 19 additions & 0 deletions gimuserver/controller/OfflineModController.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "App.hpp"
#include "OfflineModController.hpp"

using namespace drogon;

void OfflineModController::HandleFpsCap(const HttpRequestPtr& rq,
std::function<void(const HttpResponsePtr&)>&& callback)
{
(void)rq;
const auto cap = theServer()->cache().serverConfig().fpsCap;

auto resp = HttpResponse::newHttpResponse();
resp->setContentTypeCode(ContentType::CT_TEXT_PLAIN);
resp->setStatusCode(k200OK);
resp->setBody(std::to_string(cap));
resp->setCloseConnection(true);

callback(resp);
}
26 changes: 26 additions & 0 deletions gimuserver/controller/OfflineModController.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

/*!
* Endpoints consumed by the offline-proxy libcurl shim (not by the BF
* client itself). These deliver server-owned configuration that the
* client-side hooks need at startup.
*
* <p>URL prefix: /offline_mod/
*/
class OfflineModController : public drogon::HttpController<OfflineModController>
{
public:
/*!
* Returns the desired client render-loop cap as a bare integer in
* the response body (e.g. "60"). Read by the offline-proxy FPS hook
* on its first MyRender call; falls back to the proxy's compile-time
* default if this endpoint is unreachable.
* @param[in] rq HTTP request
* @param[in] callback Callback to send the response
*/
void HandleFpsCap(const drogon::HttpRequestPtr& rq, std::function<void(const drogon::HttpResponsePtr&)>&& callback);

METHOD_LIST_BEGIN
ADD_METHOD_TO(OfflineModController::HandleFpsCap, "/offline_mod/fps_cap", drogon::Get);
METHOD_LIST_END
};
1 change: 1 addition & 0 deletions gimuserver/drogon/GimuServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class GimuServer final : public drogon::Plugin<GimuServer>
}

private:

/*!
* DLC error file.
*/
Expand Down
7 changes: 7 additions & 0 deletions gimuserver/drogon/ServerConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,11 @@ struct ServerConfig
* Initial brave coins.
*/
uint32_t initialBraveCoins;

/*!
* Frame-rate cap delivered to the offline-proxy client at startup. 0
* disables the client-side cap. Read from plugins[0].config.server.fps_cap
* in deploy/config.json; defaults to 60 if absent.
*/
uint32_t fpsCap;
};
2 changes: 2 additions & 0 deletions gimuserver/gme/handlers/BadgeInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

HANDLEF(BadgeInfo)
{
// Use BadgeInfoResp (the wrapper): it carries the "h23iRjGN" dispatch key the
// client requires (see decompfrontier/server PR #23).
::BadgeInfoResp resp{};
std::string buffer{};
const auto& ec = glz::write_json(resp, buffer);
Expand Down
3 changes: 3 additions & 0 deletions gimuserver/gme/handlers/GmeControllerHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ static GmeHandler getHandler(std::string_view cmd)
REGISTER("ynB7X5P9", UpdateInfoLight, "7kH9NXwC");
REGISTER("cTZ3W2JG", UserInfo, "ScJx6ywWEb0A3njT");


}
}

Expand Down Expand Up @@ -150,6 +151,7 @@ drogon::Task<GmeAction> GmeController::Handle(drogon::SessionPtr session, const
catch (const drogon::orm::DrogonDbException& ex)
{
LOG_ERROR << "Handler error " << header.id << " (" << handler.name << ") database exception: " << ex.base().what();
logReq << "EXCEPTION (db): " << ex.base().what() << "\n";
GmeError err{};
err.cmd = GmeErrorCommand::Close;
err.flag = GmeErrorFlags::IsInError;
Expand All @@ -159,6 +161,7 @@ drogon::Task<GmeAction> GmeController::Handle(drogon::SessionPtr session, const
catch (const std::exception& ex)
{
LOG_ERROR << "Handler error " << header.id << " (" << handler.name << ") exception: " << ex.what();
logReq << "EXCEPTION (std): " << ex.what() << "\n";
GmeError err{};
err.cmd = GmeErrorCommand::Close;
err.flag = GmeErrorFlags::IsInError;
Expand Down
21 changes: 21 additions & 0 deletions launch.vs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"version": "0.2.1",
"configurations": [
{
"type": "native",
"name": "gimuserverw Debug",
"project": "CMakeLists.txt",
"projectTarget": "gimuserverw.exe (standalone_frontend\\gimuserverw.exe)",
"args": [ "${workspaceRoot}\\deploy\\config.json" ],
"env": {}
},
{
"type": "native",
"name": "gimuserverw Release",
"project": "CMakeLists.txt",
"projectTarget": "gimuserverw.exe (standalone_frontend\\gimuserverw.exe)",
"args": [ "${workspaceRoot}\\deploy\\config.json" ],
"env": {}
}
]
}
Loading