From 6ecbb3f09c27cbc26f5c417a92c170dbf25e6793 Mon Sep 17 00:00:00 2001 From: mih4n Date: Fri, 17 Jul 2026 23:49:08 +0300 Subject: [PATCH 1/7] update --- .dockerignore | 61 +++++++++++++++++++++++++++++ .github/workflows/publishDocker.yml | 41 +++++++++++++++++++ Dockerfile | 27 +++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/publishDocker.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..83ca85b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,61 @@ +.git/ +.github/ + +# Build directories +build/ +Build/ +build-*/ + +# CMake generated files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +Makefile +install_manifest.txt +compile_commands.json + +# Compiled artifacts +*.o +*.obj +*.lo +*.slo +*.d +*.gch +*.pch +*.ilk +*.pdb +*.so +*.dylib +*.dll +*.so.* +*.lai +*.la +*.a +*.lib +*.exe +*.out +*.app + +# Temporary files +*.tmp +*.log +*.bak +*.swp +*.dwo + +# vcpkg +vcpkg_installed/ + +# IDE +.vs/ +CMakeSettings.json + +# Runtime/output +bin/ +.cache/ +test/ +proxy_settings.jsonc + +# Docker +Dockerfile +.dockerignore diff --git a/.github/workflows/publishDocker.yml b/.github/workflows/publishDocker.yml new file mode 100644 index 0000000..a93452c --- /dev/null +++ b/.github/workflows/publishDocker.yml @@ -0,0 +1,41 @@ +name: Publish Docker image + +on: + release: + types: [published] + + push: + branches: [ "main", "development"] + +jobs: + push_to_registries: + name: Push Docker image to multiple registries + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Check out the repo + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ghcr.io/${{ github.repository }} + + - name: Build and push Docker images + uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + with: + context: . + push: true + file: . + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ef30779 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM gcc:15 AS builder + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + cmake \ + git \ + libssl-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY . /app + +RUN cmake -B build -DCMAKE_BUILD_TYPE=Release && \ + cmake --build build --config Release -j$(nproc) + +FROM debian:trixie-slim + +WORKDIR /app + +COPY --from=builder /app/bin/ProxyPass /app/ProxyPass + +EXPOSE 19132/udp + +ENTRYPOINT ["/app/ProxyPass"] From 75645f16a2a1975b0976077030761ee6ab923663 Mon Sep 17 00:00:00 2001 From: mih4n Date: Fri, 17 Jul 2026 23:50:43 +0300 Subject: [PATCH 2/7] test --- .github/workflows/publishDocker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publishDocker.yml b/.github/workflows/publishDocker.yml index a93452c..a6b63b8 100644 --- a/.github/workflows/publishDocker.yml +++ b/.github/workflows/publishDocker.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Log in to the Container registry - uses: docker/login-action@v2 + uses: docker/login-action@v6 with: registry: ghcr.io username: ${{ github.actor }} @@ -32,7 +32,7 @@ jobs: images: ghcr.io/${{ github.repository }} - name: Build and push Docker images - uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 + uses: docker/build-push-action@6 with: context: . push: true From 3715ab444d6c9e439f02d1c730b7e73f8e328a6b Mon Sep 17 00:00:00 2001 From: mih4n Date: Fri, 17 Jul 2026 23:52:52 +0300 Subject: [PATCH 3/7] test --- .github/workflows/publishDocker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publishDocker.yml b/.github/workflows/publishDocker.yml index a6b63b8..4184609 100644 --- a/.github/workflows/publishDocker.yml +++ b/.github/workflows/publishDocker.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@v4 - name: Log in to the Container registry - uses: docker/login-action@v6 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -32,10 +32,10 @@ jobs: images: ghcr.io/${{ github.repository }} - name: Build and push Docker images - uses: docker/build-push-action@6 + uses: docker/build-push-action@v6 with: context: . push: true - file: . + file: ./Dockerfile tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file From 11f3c439d3dc7d556624efb182ba202c0b8afb16 Mon Sep 17 00:00:00 2001 From: mih4n Date: Fri, 17 Jul 2026 23:53:57 +0300 Subject: [PATCH 4/7] fix --- .github/workflows/publishDocker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publishDocker.yml b/.github/workflows/publishDocker.yml index 4184609..e756659 100644 --- a/.github/workflows/publishDocker.yml +++ b/.github/workflows/publishDocker.yml @@ -23,7 +23,7 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ secrets.GHCR_TOKEN }} + password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta From 80d78abfde8a195bde0f8af7df8d16ce312183a4 Mon Sep 17 00:00:00 2001 From: mih4n Date: Sat, 18 Jul 2026 16:23:24 +0300 Subject: [PATCH 5/7] fix: made it possible to fetch minecraft service from container --- .gitignore | 1 + Dockerfile | 10 ++++++++-- flake.lock | 27 +++++++++++++++++++++++++++ flake.nix | 44 ++++++++++++++++++++++++++++++++++++++++++++ shell.nix | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 shell.nix diff --git a/.gitignore b/.gitignore index 6ccecd0..11d86d8 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,4 @@ vcpkg_installed/ proxy_settings.jsonc /.vs /CMakeSettings.json +.vscode diff --git a/Dockerfile b/Dockerfile index ef30779..0679816 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,13 @@ COPY . /app RUN cmake -B build -DCMAKE_BUILD_TYPE=Release && \ cmake --build build --config Release -j$(nproc) -FROM debian:trixie-slim +FROM debian:trixie-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* WORKDIR /app @@ -24,4 +30,4 @@ COPY --from=builder /app/bin/ProxyPass /app/ProxyPass EXPOSE 19132/udp -ENTRYPOINT ["/app/ProxyPass"] +CMD ["/app/ProxyPass"] diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5b993c3 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1784120854, + "narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..4bea6b3 --- /dev/null +++ b/flake.nix @@ -0,0 +1,44 @@ +{ + description = "ProxyPass build environment"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs }: + let + system = "x86_64-linux"; + pkgs = import nixpkgs { inherit system; }; + opensslStatic = pkgs.openssl.override { static = true; }; + opensslCombined = pkgs.symlinkJoin { + name = "openssl-combined"; + paths = [ opensslStatic.out opensslStatic.dev ]; + }; + in + { + devShells.${system}.default = pkgs.mkShell { + buildInputs = with pkgs; [ + gcc15 + cmake + ninja + openssl + git + pkg-config + glibc.static + ]; + + OPENSSL_ROOT_DIR = "${opensslCombined}"; + + shellHook = '' + echo "ProxyPass build environment" + echo "GCC: $(gcc --version | head -n1)" + echo "CMake: $(cmake --version | head -n1)" + echo "Ninja: $(ninja --version)" + echo "" + echo "Build with:" + echo " CC=gcc CXX=g++ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release" + echo " cmake --build build --config Release --parallel" + ''; + }; + }; +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..2111886 --- /dev/null +++ b/shell.nix @@ -0,0 +1,32 @@ +{ pkgs ? import {} }: + +let + opensslCombined = pkgs.symlinkJoin { + name = "openssl-combined"; + paths = [ pkgs.openssl.out pkgs.openssl.dev ]; + }; +in + +pkgs.mkShell { + buildInputs = with pkgs; [ + gcc15 + cmake + ninja + openssl + git + pkg-config + ]; + + OPENSSL_ROOT_DIR = "${opensslCombined}"; + + shellHook = '' + echo "ProxyPass build environment" + echo "GCC: $(gcc --version | head -n1)" + echo "CMake: $(cmake --version | head -n1)" + echo "Ninja: $(ninja --version)" + echo "" + echo "Build with:" + echo " CC=gcc CXX=g++ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release" + echo " cmake --build build --config Release --parallel" + ''; +} From 6953aebc5b02778a5714fe89b1c94f28ca1b8d04 Mon Sep 17 00:00:00 2001 From: mih4n Date: Sat, 18 Jul 2026 22:50:08 +0300 Subject: [PATCH 6/7] fix: proxy stop in a container --- .dockerignore | 1 + Dockerfile | 2 +- docker-compose.yml | 8 +++++++ src/ProxyPass.cpp | 60 ++++++++++++++++++++++++++++++++++++---------- src/ProxyPass.hpp | 8 +++++++ 5 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore index 83ca85b..5d33771 100644 --- a/.dockerignore +++ b/.dockerignore @@ -59,3 +59,4 @@ proxy_settings.jsonc # Docker Dockerfile .dockerignore +docker-compose.yml diff --git a/Dockerfile b/Dockerfile index 0679816..ffa7054 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,4 +30,4 @@ COPY --from=builder /app/bin/ProxyPass /app/ProxyPass EXPOSE 19132/udp -CMD ["/app/ProxyPass"] +ENTRYPOINT ["/app/ProxyPass"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4bf534f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +services: + proxy: + container_name: SurvivalProxy + restart: unless-stopped + image: proxy + build: . + ports: + - 19134:19132/udp \ No newline at end of file diff --git a/src/ProxyPass.cpp b/src/ProxyPass.cpp index 74f39f2..eb18737 100644 --- a/src/ProxyPass.cpp +++ b/src/ProxyPass.cpp @@ -16,8 +16,12 @@ #include "ProxyPass.hpp" #include "Logger.hpp" +#include +#include +#include #include #include +#include #include #include #include @@ -30,6 +34,16 @@ namespace sculk { +namespace { + std::atomic gProxyPassInstance{nullptr}; + + void proxyPassSignalHandler(int /*signum*/) { + if (auto* instance = gProxyPassInstance.load(std::memory_order_acquire)) { + instance->requestStop(); + } + } +} + void ProxyPass::initConsole() { #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); @@ -50,6 +64,8 @@ void ProxyPass::initConsole() { } #endif std::ios::sync_with_stdio(false); + setvbuf(stdout, nullptr, _IOLBF, 0); + setvbuf(stderr, nullptr, _IOLBF, 0); std::print("\033]0;ProxyPass\007"); } @@ -613,22 +629,40 @@ void ProxyPass::shutdown() { mThreadPool.reset(); } +void ProxyPass::requestStop() { + if (!mShouldStop.exchange(true)) { + std::lock_guard lock(mStopMutex); + mStopCv.notify_all(); + } +} + void ProxyPass::waitForStop() { - std::string command{}; - while (true) { - if (!(std::cin >> command)) { - break; - } - if (command == "stop") { - shutdown(); - getLogger().info("Proxy server stopped."); - break; + gProxyPassInstance.store(this, std::memory_order_release); + std::signal(SIGINT, proxyPassSignalHandler); + std::signal(SIGTERM, proxyPassSignalHandler); + + std::thread inputThread([this]() { + std::string command{}; + while (std::cin >> command) { + if (command == "stop") { + requestStop(); + break; + } + getLogger().error( + "Unknown command: {}. Please check that the command exists and that you have permission to use it.", + command + ); } - getLogger().error( - "Unknown command: {}. Please check that the command exists and that you have permission to use it.", - command - ); + }); + inputThread.detach(); + + { + std::unique_lock lock(mStopMutex); + mStopCv.wait(lock, [this] { return mShouldStop.load(); }); } + + shutdown(); + getLogger().info("Proxy server stopped."); } } // namespace sculk diff --git a/src/ProxyPass.hpp b/src/ProxyPass.hpp index e36394d..70a2ec0 100644 --- a/src/ProxyPass.hpp +++ b/src/ProxyPass.hpp @@ -18,6 +18,9 @@ #include "Logger.hpp" #include "ProxyBridge.hpp" #include "ProxySettings.hpp" +#include +#include +#include #include #include #include @@ -39,6 +42,9 @@ class ProxyPass { protocol::PemKeyPair mProxyServerKeyPair{}; std::unique_ptr mThreadPool{}; std::unique_ptr mProxyServer{}; + std::mutex mStopMutex{}; + std::condition_variable mStopCv{}; + std::atomic mShouldStop{false}; public: ProxyPass(); @@ -50,6 +56,8 @@ class ProxyPass { void shutdown(); + void requestStop(); + void waitForStop(); static void initConsole(); From cdf3f781f70ddd3f246a434ce352990ab88383a2 Mon Sep 17 00:00:00 2001 From: mih4n Date: Sun, 19 Jul 2026 16:10:40 +0300 Subject: [PATCH 7/7] update --- src/Main.cpp | 1 + src/ProxyPass.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++---- src/ProxyPass.hpp | 2 ++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/Main.cpp b/src/Main.cpp index d4641fb..d3a0d39 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -18,6 +18,7 @@ #include "ProxyPass.hpp" int main() { + sculk::ProxyPass::setWorkingDirectory(); sculk::ProxyPass::initConsole(); auto proxyPass = sculk::ProxyPass(); if (!proxyPass.start()) { diff --git a/src/ProxyPass.cpp b/src/ProxyPass.cpp index eb18737..b8aea79 100644 --- a/src/ProxyPass.cpp +++ b/src/ProxyPass.cpp @@ -17,11 +17,18 @@ #include "ProxyPass.hpp" #include "Logger.hpp" #include +#include #include #include +#include #include #include #include + +#ifndef _WIN32 +#include +#endif + #include #include #include @@ -44,6 +51,36 @@ namespace { } } +void ProxyPass::setWorkingDirectory() { + std::filesystem::path exePath; +#ifdef _WIN32 + wchar_t buffer[MAX_PATH]; + if (GetModuleFileNameW(nullptr, buffer, MAX_PATH) != 0) { + exePath = buffer; + } +#else + char buffer[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len != -1) { + buffer[len] = '\0'; + exePath = buffer; + } +#endif + if (exePath.empty()) { + return; + } + + auto dir = exePath.parent_path(); + for (int i = 0; i < 4 && !dir.empty(); ++i) { + if (std::filesystem::exists(dir / "proxy_settings.jsonc")) { + std::filesystem::current_path(dir); + return; + } + dir = dir.parent_path(); + } + std::filesystem::current_path(exePath.parent_path()); +} + void ProxyPass::initConsole() { #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); @@ -428,6 +465,13 @@ void ProxyPass::handleFirstClientPacket( return; } + getLogger().info( + "Upstream connection to {}:{} established for player: {}.", + mSettings.upstream_host, + mSettings.upstream_port, + currentBridge->mConnectionRequest.getXboxLiveName() + ); + if (!currentBridge->mClientReady.load(std::memory_order_acquire)) { return; } @@ -451,7 +495,9 @@ void ProxyPass::handleFirstClientPacket( } getLogger().info( - "Failed to connect to upstream server for player: {}.", + "Upstream connection to {}:{} failed for player: {}.", + mSettings.upstream_host, + mSettings.upstream_port, currentBridge->mConnectionRequest.getXboxLiveName() ); getLogger().info( @@ -479,10 +525,17 @@ void ProxyPass::handleFirstClientPacket( return disconnectClient(guid, "Failed to initialize proxy bridge", protocol::DisconnectFailReason::Unknown); } - if (bridge->mProxyClient.connect(mSettings.upstream_host, mSettings.upstream_port) - != protocol::ClientNetworkSystem::ConnectionResult::ConnectionAttemptStarted) [[unlikely]] { + getLogger().info( + "Connecting to upstream server at {}:{} for player: {}.", + mSettings.upstream_host, + mSettings.upstream_port, + bridge->mConnectionRequest.getXboxLiveName() + ); + auto connectResult = bridge->mProxyClient.connect(mSettings.upstream_host, mSettings.upstream_port); + if (connectResult != protocol::ClientNetworkSystem::ConnectionResult::ConnectionAttemptStarted) [[unlikely]] { getLogger().info( - "Failed to connect to upstream server for player: {}.", + "Failed to start upstream connection (result: {}) for player: {}.", + std::to_underlying(connectResult), bridge->mConnectionRequest.getXboxLiveName() ); getLogger().info( diff --git a/src/ProxyPass.hpp b/src/ProxyPass.hpp index 70a2ec0..2e8552c 100644 --- a/src/ProxyPass.hpp +++ b/src/ProxyPass.hpp @@ -60,6 +60,8 @@ class ProxyPass { void waitForStop(); + static void setWorkingDirectory(); + static void initConsole(); private: