From 0deb8c61c0069e64a33d39291210784fa440cef4 Mon Sep 17 00:00:00 2001 From: Leonard Jonathan Oh Date: Wed, 22 Nov 2023 03:10:37 +0000 Subject: [PATCH] Change (docker): Combine nginx and php to a single image for `ASP` with env var support for configuration In <= v3.2.0, `asp` had separate `nginx` and `php` images. Now: `asp` image containing both `nginx` and `php`, with environment variable support, and entrypoint that sets the correct permissions. Benefits: - Easier to deploy / upgrade. No need to separate `nginx` and `php` containers - Environment variable configuration means no more need to mount config into `asp` container - Entrypoint script sets permissions on volumes. `init-container` should only need to set permissions for `db` volume Notable changes: - Add documentation for upgrading docker images to v3.3.x from prior versions - Update `./docs/examples` - Add tests for production builds for examples in `./docs/examples` Closes #69 --- .github/workflows/ci-master-pr.yml | 83 +++--- .vscode/settings.json | 1 + .vscode/tasks.json | 25 ++ Dockerfile.php => Dockerfile | 37 ++- Dockerfile.nginx | 18 -- README.md | 99 +++++-- config/ASP/armyAbbreviationMap.php | 204 -------------- config/ASP/backendAwards.php | 235 ---------------- config/ASP/config.php | 54 ---- config/ASP/docker-entrypoint.sh | 105 ++++++++ config/ASP/{ => etc}/nginx/nginx.conf | 11 +- config/ASP/php-fpm.d/www.conf | 10 - config/ASP/ranks.php | 253 ------------------ config/ASP/supervisor.conf | 44 +++ config/ASP/tail.sh | 8 + .../ASP/usr/local/etc/php-fpm.d/php-fpm.conf | 33 +++ .../{ => usr/local/etc}/php/conf.d/php.ini | 0 config/bf2/mods/bf2/ai/aidefault-custom.ai | 21 ++ .../mods/bf2/settings/maplist-custom-coop.con | 24 ++ .../mods/bf2/settings/maplist-custom-cq.con | 82 ++++++ .../bf2/settings/serversettings-custom.con | 68 +++++ .../python/bf2/BF2StatisticsConfig-custom.py | 69 +++++ config/db/setup.sql | 142 ++++++++++ docker-compose.build.yml | 15 +- docker-compose.test.yml | 72 ----- docker-compose.yml | 136 +++++----- docs/full-bf2-stack-example/.env | 2 +- docs/full-bf2-stack-example/README.md | 72 ++--- .../config/ASP/armyAbbreviationMap.php | 204 -------------- .../config/ASP/backendAwards.php | 235 ---------------- .../config/ASP/config.php | 54 ---- .../config/ASP/nginx/nginx.conf | 121 --------- .../config/ASP/php-fpm.d/www.conf | 10 - .../config/ASP/php/conf.d/php.ini | 27 -- .../config/ASP/ranks.php | 253 ------------------ .../bf2/settings/serversettings-custom.con | 2 +- .../python/bf2/BF2StatisticsConfig-custom.py | 8 +- .../config/coredns/hosts | 4 +- .../docker-compose.build.yml | 17 ++ .../full-bf2-stack-example/docker-compose.yml | 177 ++++-------- docs/upgrading-docker-images-to-3.3.md | 97 +++++++ test/.env | 1 + test/docker-compose.yml | 35 +++ .../snapshots/dalian_plant_20231107_2211.json | 1 + .../daqing_oilfields_20231107_2212.json | 1 + test/test-endpoints.sh | 47 ++++ test/test-external-dns.sh | 26 ++ test/test-internal-dns.sh | 24 ++ test/test-ready.sh | 18 ++ test/test-routes.sh | 47 ++++ test/test-snapshots.sh | 19 ++ test/test.sh | 81 ++++++ 52 files changed, 1372 insertions(+), 2060 deletions(-) create mode 100644 .vscode/tasks.json rename Dockerfile.php => Dockerfile (52%) delete mode 100644 Dockerfile.nginx delete mode 100644 config/ASP/armyAbbreviationMap.php delete mode 100644 config/ASP/backendAwards.php delete mode 100644 config/ASP/config.php create mode 100644 config/ASP/docker-entrypoint.sh rename config/ASP/{ => etc}/nginx/nginx.conf (96%) delete mode 100644 config/ASP/php-fpm.d/www.conf delete mode 100644 config/ASP/ranks.php create mode 100644 config/ASP/supervisor.conf create mode 100755 config/ASP/tail.sh create mode 100644 config/ASP/usr/local/etc/php-fpm.d/php-fpm.conf rename config/ASP/{ => usr/local/etc}/php/conf.d/php.ini (100%) create mode 100644 config/bf2/mods/bf2/ai/aidefault-custom.ai create mode 100644 config/bf2/mods/bf2/settings/maplist-custom-coop.con create mode 100644 config/bf2/mods/bf2/settings/maplist-custom-cq.con create mode 100644 config/bf2/mods/bf2/settings/serversettings-custom.con create mode 100644 config/bf2/python/bf2/BF2StatisticsConfig-custom.py create mode 100644 config/db/setup.sql delete mode 100644 docker-compose.test.yml delete mode 100644 docs/full-bf2-stack-example/config/ASP/armyAbbreviationMap.php delete mode 100644 docs/full-bf2-stack-example/config/ASP/backendAwards.php delete mode 100644 docs/full-bf2-stack-example/config/ASP/config.php delete mode 100644 docs/full-bf2-stack-example/config/ASP/nginx/nginx.conf delete mode 100644 docs/full-bf2-stack-example/config/ASP/php-fpm.d/www.conf delete mode 100644 docs/full-bf2-stack-example/config/ASP/php/conf.d/php.ini delete mode 100644 docs/full-bf2-stack-example/config/ASP/ranks.php create mode 100644 docs/full-bf2-stack-example/docker-compose.build.yml create mode 100644 docs/upgrading-docker-images-to-3.3.md create mode 100644 test/.env create mode 100644 test/docker-compose.yml create mode 100644 test/snapshots/dalian_plant_20231107_2211.json create mode 100644 test/snapshots/daqing_oilfields_20231107_2212.json create mode 100755 test/test-endpoints.sh create mode 100755 test/test-external-dns.sh create mode 100755 test/test-internal-dns.sh create mode 100755 test/test-ready.sh create mode 100755 test/test-routes.sh create mode 100755 test/test-snapshots.sh create mode 100755 test/test.sh diff --git a/.github/workflows/ci-master-pr.yml b/.github/workflows/ci-master-pr.yml index 38d150b9..15038e90 100644 --- a/.github/workflows/ci-master-pr.yml +++ b/.github/workflows/ci-master-pr.yml @@ -16,32 +16,25 @@ jobs: matrix: testenv: - dev + - prod runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v2 + # This is commented out, so we use the default 'docker' driver instead of the 'docker-container' driver. When using 'docker-container' driver, there appears to be a rate limit on writes on Github CI which causes buildx to fail with error code 17 when it is exporting to cache + # - name: Set up Docker Buildx + # id: buildx + # uses: docker/setup-buildx-action@v2 - - name: Cache Docker layers (nginx) - uses: actions/cache@v3 + - name: Cache Docker layers + uses: actions/cache/restore@v3 # Restore cache but don't save it at end of job with: - path: /tmp/.buildx-cache-nginx - key: ${{ runner.os }}-buildx-nginx-${{ github.sha }} + path: /tmp/.buildx-cache-asp + key: ${{ runner.os }}-buildx-asp-${{ github.sha }} restore-keys: | - ${{ runner.os }}-buildx-nginx- - ${{ runner.os }}-buildx- - - - name: Cache Docker layers (php) - uses: actions/cache@v3 - with: - path: /tmp/.buildx-cache-php - key: ${{ runner.os }}-buildx-php-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-buildx-php- - ${{ runner.os }}-buildx- + ${{ runner.os }}-buildx-asp + ${{ runner.os }}-buildx - name: Print buildx and compose run: | @@ -53,15 +46,22 @@ jobs: if: matrix.testenv == 'dev' run: | set -eux - docker compose -f docker-compose.yml -f docker-compose.build.yml up --build -d - docker compose -f docker-compose.test.yml up + ./test/test.sh dev 1 1 + + - name: Integration test (prod) + if: matrix.testenv == 'prod' + run: | + set -eux + + # Don't publish coredns ports to prevent conflict with system-resolved on github CI + # sed -i '$!N;s@ports:\n - 53:53.*@@;P;D' docker-compose.yml + + # Make coredns listen on localhost only to prevent conflict with system-resolved on github CI + sed -i 's/- 53:53/- 127.0.0.1:53:53/' docs/full-bf2-stack-example/docker-compose.yml + + ./test/test.sh prod 1 1 build: - strategy: - matrix: - variant: - - nginx - - php runs-on: ubuntu-latest steps: - name: Checkout @@ -91,10 +91,9 @@ jobs: - name: Cache Docker layers uses: actions/cache@v3 with: - path: /tmp/.buildx-cache-${{ matrix.variant }} - key: ${{ runner.os }}-buildx-${{ matrix.variant }}-${{ github.sha }} + path: /tmp/.buildx-cache-asp + key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | - ${{ runner.os }}-buildx-${{ matrix.variant }}- ${{ runner.os }}-buildx- # This step generates the docker tags @@ -108,12 +107,12 @@ jobs: # type=ref,event=branch generates tag(s) on branch only. E.g. 'master-', 'master-abc0123-' # type=ref,event=tag generates tag(s) on tags only. E.g. 'v0.0.0-', 'v0.0.0-abc0123-' tags: | - type=ref,suffix=-${{ matrix.variant }},event=pr - type=ref,suffix=-{{sha}}-${{ matrix.variant }},event=pr - type=ref,suffix=-${{ matrix.variant }},event=branch - type=ref,suffix=-{{sha}}-${{ matrix.variant }},event=branch - type=ref,suffix=-${{ matrix.variant }},event=tag - type=ref,suffix=-{{sha}}-${{ matrix.variant }},event=tag + type=ref,suffix=,event=pr + type=ref,suffix=-{{sha}},event=pr + type=ref,suffix=,event=branch + type=ref,suffix=-{{sha}},event=branch + type=ref,suffix=,event=tag + type=ref,suffix=-{{sha}},event=tag # Disable 'latest' tag flavor: | latest=false @@ -131,36 +130,36 @@ jobs: if: github.event_name == 'pull_request' uses: docker/build-push-action@v3 with: - file: Dockerfile.${{ matrix.variant }} + file: Dockerfile context: '.' target: prod platforms: linux/amd64 push: false tags: ${{ steps.meta.outputs.tags }} - cache-from: type=local,src=/tmp/.buildx-cache-${{ matrix.variant }} - cache-to: type=local,dest=/tmp/.buildx-cache-${{ matrix.variant }}-new,mode=max + cache-from: type=local,src=/tmp/.buildx-cache-asp + cache-to: type=local,dest=/tmp/.buildx-cache-asp-new,mode=max - name: Build and push # Run on master and tags if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/') uses: docker/build-push-action@v3 with: - file: Dockerfile.${{ matrix.variant }} + file: Dockerfile context: '.' target: prod platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/s390x push: true tags: ${{ steps.meta.outputs.tags }} - cache-from: type=local,src=/tmp/.buildx-cache-${{ matrix.variant }} - cache-to: type=local,dest=/tmp/.buildx-cache-${{ matrix.variant }}-new,mode=max + cache-from: type=local,src=/tmp/.buildx-cache-asp + cache-to: type=local,dest=/tmp/.buildx-cache-asp-new,mode=max # Temp fix # https://github.com/docker/build-push-action/issues/252 # https://github.com/moby/buildkit/issues/1896 - name: Move cache run: | - rm -rf /tmp/.buildx-cache-${{ matrix.variant }} - mv /tmp/.buildx-cache-${{ matrix.variant }}-new /tmp/.buildx-cache-${{ matrix.variant }} + rm -rf /tmp/.buildx-cache-asp + mv /tmp/.buildx-cache-asp-new /tmp/.buildx-cache-asp update-draft-release: needs: [test, build] diff --git a/.vscode/settings.json b/.vscode/settings.json index b893352b..4b35dfe2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,4 +4,5 @@ "*.ai.add": "bat", "*.con": "ini", }, + // "editor.trimAutoWhitespace": false } diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..58ec8c43 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,25 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Test (dev)", + "type": "shell", + "command": "./test/test.sh dev 1", + "group": "build" + }, + { + "label": "Test (prod)", + "type": "shell", + "command": "./test/test.sh prod 1 1", + "group": "build" + }, + { + "label": "Test (dns)", + "type": "shell", + "command": "./test/test.sh dns", + "group": "build" + }, + ] +} diff --git a/Dockerfile.php b/Dockerfile similarity index 52% rename from Dockerfile.php rename to Dockerfile index bc4071f1..b20a137e 100644 --- a/Dockerfile.php +++ b/Dockerfile @@ -2,14 +2,17 @@ FROM $IMAGE AS build # Set permissions for 'www-data' user -COPY ./src /src -WORKDIR /src +COPY ./src/ASP /src/ASP +WORKDIR /src/ASP RUN chown -R www-data:www-data . \ && find . -type d -exec chmod 750 {} \; \ && find . -type f -exec chmod 640 {} \; FROM $IMAGE AS dev +# Install nginx and supervisor for multi-process container +RUN apk add --no-cache ca-certificates nginx supervisor + # opcache RUN docker-php-ext-install opcache @@ -37,9 +40,33 @@ php -i; \ php -m -# Add default configs -COPY ./config/ASP/php/conf.d/php.ini /usr/local/etc/php/conf.d/php.ini -COPY ./config/ASP/php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf +# Add configs +COPY ./config/ASP/. / +COPY ./src/ASP/system/config /config.sample +RUN chmod +x /docker-entrypoint.sh; +RUN set -eux; \ + chmod +x /docker-entrypoint.sh; \ + chmod +x /tail.sh; \ + # Symlink nginx logs + ln -sfn /dev/stdout /var/log/nginx/access.log; \ + ln -sfn /dev/stderr /var/log/nginx/error.log; \ + # Disable the built-in php-fpm configs, since we're using our own config + mv -v /usr/local/etc/php-fpm.d/docker.conf /usr/local/etc/php-fpm.d/docker.conf.disabled; \ + mv -v /usr/local/etc/php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf.disabled; \ + mv -v /usr/local/etc/php-fpm.d/zz-docker.conf /usr/local/etc/php-fpm.d/zz-docker.conf.disabled; + +# In docker, IPs may be dynamic. This ensures we get access +ENV ADMIN_HOSTS=0.0.0.0/0 +VOLUME /src/ASP/system/backups +VOLUME /src/ASP/system/cache +VOLUME /src/ASP/system/config +VOLUME /src/ASP/system/logs +VOLUME /src/ASP/system/snapshots +EXPOSE 80 +EXPOSE 9000 +WORKDIR /src/ASP +ENTRYPOINT [] +CMD ["/docker-entrypoint.sh"] FROM dev AS prod diff --git a/Dockerfile.nginx b/Dockerfile.nginx deleted file mode 100644 index 5adae2ed..00000000 --- a/Dockerfile.nginx +++ /dev/null @@ -1,18 +0,0 @@ -ARG IMAGE=nginx:1.21-alpine -FROM $IMAGE AS build - -# Set permissions for 'nginx' user -COPY ./src /src -WORKDIR /src -RUN chown -R nginx:nginx . \ - && find . -type d -exec chmod 750 {} \; \ - && find . -type f -exec chmod 640 {} \; - -FROM $IMAGE AS dev - -# Add default configs -COPY config/ASP/nginx/nginx.conf /etc/nginx/nginx.conf - -FROM dev AS prod - -COPY --from=build /src /src diff --git a/README.md b/README.md index 8cac7315..04fde715 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,54 @@ [![github-actions](https://github.com/startersclan/asp/workflows/ci-master-pr/badge.svg)](https://github.com/startersclan/asp/actions) [![github-release](https://img.shields.io/github/v/release/startersclan/asp?style=flat-square)](https://github.com/startersclan/asp/releases/) -[![docker-image-size](https://img.shields.io/docker/image-size/startersclan/asp/master-nginx?label=nginx)](https://hub.docker.com/r/startersclan/asp) -[![docker-image-size](https://img.shields.io/docker/image-size/startersclan/asp/master-php?label=php)](https://hub.docker.com/r/startersclan/asp) +[![docker-image-size](https://img.shields.io/docker/image-size/startersclan/asp/master?label=asp)](https://hub.docker.com/r/startersclan/asp) The new BF2Statistics 3.0 ASP, currently in public Beta. The GameSpy server to match is over at https://github.com/BF2Statistics/BattleSpy -## Usage +## Usage (docker) ```sh -docker pull startersclan/asp:3.2.0-nginx -docker pull startersclan/asp:3.2.0-php +docker run --rm -it -p 80:80 -e DB_HOST=db -e DB_PORT=3306 -e DB_NAME=bf2stats -e DB_USER=root -e DB_PASS=ascent startersclan/asp:3.2.0 ``` See [this](docs/full-bf2-stack-example) example showing how to deploy [Battlefield 2 1.5 server](https://github.com/startersclan/docker-bf2), [PRMasterserver](https://github.com/startersclan/PRMasterServer) as the master server, and `ASP` as the stats web server, using `docker-compose`. +### Upgrading + +See instructions to upgrade to v3.3.0 [here](docs/upgrading-docker-images-to-3.3.md). + ## Development +To use Docker Compose v2, use `docker compose` instead of `docker-compose`. + ```sh -# Start +# 1. Start docker-compose up --build # ASP available at http://localhost:8081/ASP. Username: admin, password admin. See ./config/ASP/config.php # phpmyadmin available at http://localhost:8083. Username: root, password: ascent. See ./config/ASP/config.php config file +# 2. Once everything has started, restart the BF2 server to begin recording stats +docker-compose restart bf2 + +# 3. Before launching the BF2 client, spoof gamespy DNS by adding these entries in C:\Windows\system32\drivers\etc\hosts. This is needed for the BF2 client to work correctly. +# Replace '192.168.1.100' with your development machine's IP address +192.168.1.100 battlefield2.available.gamespy.com +192.168.1.100 battlefield2.master.gamespy.com +192.168.1.100 battlefield2.ms14.gamespy.com +192.168.1.100 master.gamespy.com +192.168.1.100 motd.gamespy.com +192.168.1.100 gpsp.gamespy.com +192.168.1.100 gpcm.gamespy.com +192.168.1.100 gamespy.com +192.168.1.100 bf2web.gamespy.com +192.168.1.100 gamestats.gamespy.com +192.168.1.100 eapusher.dice.se + +# 4. Launch BF2 client and connect to the BF2 server +# - To use BF2Hub as the Gamespy server, launch BF2.exe, and login to your BF2Hub account, and connect to the BF2 server using MULTIPLAYER > CONNECT TO IP +# - To use PRMasterserver in docker-compose as the Gamespy server, if you have previously patched BF2.exe using the BF2Hub patcher, you must unpatch BF2.exe. Then launch BF2.exe (do not use BF2Hub), create a new Online account, login, and connect to the BF2 server using MULTIPLAYER > CONNECT TO IP. +# At the end of a round, the BF2 server will send a stats snapshot to the ASP. View stats in ASP. + # Development - Install vscode extensions # Once installed, set breakpoints in code, and press F5 to start debugging. code --install-extension bmewburn.vscode-intelephense-client # PHP intellisense @@ -33,26 +59,52 @@ code --install-extension ms-python.python # Python intellisense # Execute this to allow php to reach the host machine via the docker0 bridge sudo iptables -A INPUT -i br+ -j ACCEPT -# Test routes -docker-compose -f docker-compose.test.yml up - -# Test production builds locally -docker build -t startersclan/asp:nginx -f Dockerfile.nginx --target prod . -docker build -t startersclan/asp:php -f Dockerfile.php --target prod . +# BF2 server - Restart server +docker-compose restart bf2 +# BF2 server - Attach to the bf2 server console +docker attach $( docker-compose ps -q bf2 ) +# BF2 server - Exec into container +docker exec -it $( docker-compose ps -q bf2) bash +# BF2 server - Read python logs +docker exec -it $( docker-compose ps -q bf2 ) bash -c 'cat python/bf2/logs/bf2game_*' +# BF2 server - List snapshots +docker exec -it $( docker-compose ps -q bf2 ) bash -c 'ls -al python/bf2/logs/snapshots/sent' +docker exec -it $( docker-compose ps -q bf2 ) bash -c 'ls -al python/bf2/logs/snapshots/unsent' + +# asp - Exec into container +docker exec -it $( docker-compose ps -q asp ) sh +# asp - List backups +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/backups +# asp - List cache +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/cache +# asp - List config +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/config +# asp - List logs +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/logs +# asp - List snapshots +docker exec -it $( docker-compose ps -q asp ) ls -alR /src/ASP/system/snapshots/ + +# Test +./test/test.sh dev 1 + +# Test production builds +./test/test.sh prod 1 1 # Dump the DB -docker exec $( docker-compose ps | grep db | awk '{print $1}' ) mysqldump -uroot -pascent bf2stats | gzip > bf2stats.sql.gz +docker exec $( docker-compose ps -q db ) mysqldump -uroot -pascent bf2stats | gzip > bf2stats.sql.gz # Restore the DB -zcat bf2stats.sql.gz | docker exec -i $( docker-compose ps | grep db | awk '{print $1}' ) mysql -uroot -pascent bf2stats +zcat bf2stats.sql.gz | docker exec -i $( docker-compose ps -q db ) mysql -uroot -pascent bf2stats # Stop docker-compose down # Cleanup -docker-compose down +docker-compose down --remove-orphans +docker volume rm asp_prmasterserver-volume docker volume rm asp_backups-volume docker volume rm asp_cache-volume +docker volume rm asp_config-volume docker volume rm asp_logs-volume docker volume rm asp_snapshots-volume docker volume rm asp_db-volume @@ -70,14 +122,19 @@ git commit -m "Chore: Release 3.x.x" ### Q: ASP dashboard shows `Parse error: syntax error, unexpected 'admin' (T_STRING) in /src/ASP/system/framework/View.php(346) : eval()'d code on line 153` -A: Grant `php`'s `www-data` user write permission for `config.php`. +A: Grant the PHP user write permission for `./src/ASP/system/config/config.php`. + +### Q: Database cannot be backed up using the ASP when the database is not on the same host!!! + +A: If you are seeing this in docker, it is expected, since `ASP` and `db` containers are running on different hosts. + +It is better to backup the DB on a `cron` schedule using `mysqldump` from another container linked to the `db` container: ```sh -chown 82:82 ./config/ASP/config.php -chmod 666 ./config/ASP/config.php -docker-compose restart php +# Dump a DB at host `db`, user `root`, password `ascent`, database `bf2stats` +mysqldump -hdb -uroot -pascent bf2stats ``` -### Q: `Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9000 (through xdebug.client_host/xdebug.client_port)` appears in the php logs +### Q: `Xdebug: [Step Debug] Could not connect to debugging client. Tried: host.docker.internal:9000 (through xdebug.client_host/xdebug.client_port)` appears in PHP logs on `docker-compose up` -A: The debugger is not running. Press `F5` in `vscode` to start the `php` `xdebug` debugger. If you stopped the debugger, it is safe to ignore this message. +A: If you are seeing this in development, the PHP debugger is not running. Press `F5` in `vscode` to start the PHP debugger. If you don't need debugging, set `XDEBUG_MODE=off` in `docker-compose.yml` to disable XDebug. diff --git a/config/ASP/armyAbbreviationMap.php b/config/ASP/armyAbbreviationMap.php deleted file mode 100644 index 3fa3c298..00000000 --- a/config/ASP/armyAbbreviationMap.php +++ /dev/null @@ -1,204 +0,0 @@ - [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'mec' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'ch' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - - // Xpack 1 Special Forces - 'seal' => [ - 'flag' => 0, - 'name' => 'United States Navy Seals' - ], - 'mecsf' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition Special Forces' - ], - 'sas' => [ - 'flag' => 4, - 'name' => 'British Special Air Service' - ], - 'spetz' => [ - 'flag' => 5, - 'name' => 'Russian Spetsnaz' - ], - 'chinsurgent' => [ - 'flag' => 7, - 'name' => 'Rebels' - ], - 'meinsurgent' => [ - 'flag' => 8, - 'name' => 'Insurgents' - ], - - // Booster Pack 1 EuroForce - 'eu' => [ - 'flag' => 9, - 'name' => 'European Union' - ], - - // POE2 - 'ger' => [ - 'flag' => 10, - 'name' => 'German Forces' - ], - 'ukr' => [ - 'flag' => 12, - 'name' => 'Ukrainian Forces' - ], - - // AIX - 'un' => [ - 'flag' => 13, - 'name' => 'United Nations' - ], - - // Hard Justice - 'us2' => [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'us3' => [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'mec2' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'mec3' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'ch2' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - 'ch3' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - 'ca' => [ - 'flag' => 13, - 'name' => 'Canadian Forces' - ], - - // Nations at war 8.0 - 'blackwater' => [ - 'flag' => 14, - 'name' => 'Blackwater Military Contractors' - ], - 'taliban' => [ - 'flag' => 15, - 'name' => 'Taliban Forces' - ], - 'au' => [ - 'flag' => 16, - 'name' => 'Australian Forces' - ], - 'ru' => [ - 'flag' => 17, - 'name' => 'Russian Forces' - ], - 'gb' => [ - 'flag' => 18, - 'name' => 'British Forces' - ], - 'nato' => [ - 'flag' => 19, - 'name' => 'NATO Forces' - ], - 'isis' => [ - 'flag' => 20, - 'name' => 'ISIS Forces' - ], - 'iraqa' => [ - 'flag' => 21, - 'name' => 'Iraqi Forces' - ], - 'usmc' => [ - 'flag' => 0, - 'name' => 'United States Marines Corps' - ], - 'somalia' => [ - 'flag' => 23, - 'name' => 'Somalian Forces' - ], - 'rangers' => [ - 'flag' => 0, - 'name' => 'U.S Army Rangers' - ], - 'idf' => [ - 'flag' => 25, - 'name' => 'Israel Defense Force' - ], - 'chsf' => [ - 'flag' => 2, - 'name' => 'Chinese Special Forces' - ], - 'paras' => [ - 'flag' => 27, - 'name' => 'British Paratroop Regiment' - ], - 'casf' => [ - 'flag' => 28, - 'name' => 'Canadian Special Forces' - ], - 'hamas' => [ - 'flag' => 29, - 'name' => 'Hamas Forces' - ], - 'hezbollah' => [ - 'flag' => 30, - 'name' => 'Hezbollah Forces' - ], - 'iran' => [ - 'flag' => 31, - 'name' => 'Iran Forces' - ], - 'saudi_arabia' => [ - 'flag' => 32, - 'name' => 'Saudi Arabia Forces' - ], - 'syria' => [ - 'flag' => 33, - 'name' => 'Syrian Forces' - ], - 'egypt' => [ - 'flag' => 34, - 'name' => 'Egyptian Army' - ], - 'pakistan' => [ - 'flag' => 35, - 'name' => 'Pakistan Army' - ], - 'india' => [ - 'flag' => 36, - 'name' => 'Indian Armed forces' - ], -); \ No newline at end of file diff --git a/config/ASP/backendAwards.php b/config/ASP/backendAwards.php deleted file mode 100644 index 053add94..00000000 --- a/config/ASP/backendAwards.php +++ /dev/null @@ -1,235 +0,0 @@ - 180000); -}; - -/** - * Vanilla BF2 Medal Criteria - * - * @param array $row The AwardCriteria resulting row - * @param int $timesAwarded the number of times this award has been awarded - * - * @return bool true if the player is eligible to receive the award, false otherwise - */ -$vServiceMedalCriteria = function($row, $timesAwarded) -{ - $level = $timesAwarded + 1; - $best = (int)$row['brnd']; - $time = ((int)$row['time']) / 3600; // Convert to hours - $wins = (int)$row['wins']; - return ($best >= (100 * $level) && $time >= (100 * $level) && $wins >= (100 * $level)); -}; - -/** - * Special Forces Medal Criteria - * - * @param array $row The AwardCriteria resulting row - * @param int $timesAwarded the number of times this award has been awarded - * - * @return bool true if the player is eligible to receive the award, false otherwise - */ -$xServiceMedalCriteria = function($row, $timesAwarded) -{ - $level = $timesAwarded + 1; - $best = (int)$row['brnd']; - $time = ((int)$row['time']) / 3600; // Convert to hours - $wins = (int)$row['wins']; - return ($best >= (100 * $level) && $time >= (50 * $level) && $wins >= (50 * $level)); -}; - - -/** - * ------------------------------------------------------------------ - * Return Backend Awards and Criteria - * ------------------------------------------------------------------ - */ - -return array( - /** Service Ribbons */ - - // Middle Eastern Service Ribbon - new BackendAward(3191305, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (0,1,2,3,4,5,6) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 7); - } - ) - ]), - // Far East Service Ribbon - new BackendAward(3190605, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (100,101,102,103,105,601) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 6); - } - ) - ]), - // European Union Service Ribbon - new BackendAward(3270519, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (10,11,110) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - new AwardCriteria('player_map', 'sum(time) AS result', 'map_id IN (10,11,110)', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 180000); - } - ), - ]), - // North American Service Ribbon - new BackendAward(3271401, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (200,201,202) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - new AwardCriteria('player_map', 'sum(time) AS result', 'map_id IN (200,201,202)', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 90000); - } - ), - ]), - - /** Xpack Service Ribbons */ - - // Navy Seal Special Service Ribbon - new BackendAward(3261919, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 3', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (300,301,304) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // SAS Special Service Ribbon - new BackendAward(3261901, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 4', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (302,303,307) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // SPETZNAS Service Ribbon - new BackendAward(3261819, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 5', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (305,306,307) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // MECSF Service Ribbon - new BackendAward(3261319, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 6', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (300,301,304) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // Rebel Service Ribbon - new BackendAward(3261805, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 7', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (305,306) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 2); - } - ), - ]), - // Insurgent Service Ribbon - new BackendAward(3260914, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 8', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (302,303) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 2); - } - ), - ]), - - /** Service Medals */ - - // Navy Cross - new BackendAward(2021403, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 0', $vServiceMedalCriteria), - ]), - // Golden Scimitar - new BackendAward(2020719, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 1', $vServiceMedalCriteria), - ]), - // Peoples Medallion - new BackendAward(2021613, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 2', $vServiceMedalCriteria), - ]), - // European Union Service Medal - new BackendAward(2270521, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 9', $xServiceMedalCriteria), - ]), - - /** Xpack Service Medals */ - - // Navy Seal Special Service Medal - new BackendAward(2261913, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 3', $xServiceMedalCriteria), - ]), - // SAS Special Service Medal - new BackendAward(2261919, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 4', $xServiceMedalCriteria), - ]), - // SPETZ Special Service Medal - new BackendAward(2261613, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 5', $xServiceMedalCriteria), - ]), - // MECSF Special Service Medal - new BackendAward(2261303, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 6', $xServiceMedalCriteria), - ]), - // Rebels Special Service Medal - new BackendAward(2261802, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 7', $xServiceMedalCriteria), - ]), - // Insurgent Special Service Medal - new BackendAward(2260914, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 8', $xServiceMedalCriteria), - ]), -); \ No newline at end of file diff --git a/config/ASP/config.php b/config/ASP/config.php deleted file mode 100644 index 4fa69033..00000000 --- a/config/ASP/config.php +++ /dev/null @@ -1,54 +0,0 @@ - 'db_host' + VALUE=$( echo "$VALUE" | rev | sed 's/^;//' | rev ) # Strip the trailing semicolon + TYPE= + if echo "$VALUE" | grep -E "^'" > /dev/null; then + TYPE=string + elif echo "$VALUE" | grep -E "^array\(" > /dev/null; then + TYPE=array + elif echo "$VALUE" | grep -E "^[0-9]+$" > /dev/null; then + TYPE=int + else + echo "Unable to determine variable type of KEY '$KEY' in config file $CONFIG_FILE. Please check syntax." + exit 1 + fi + write_config "$KEY" "$TYPE" "$CONFIG_FILE" +done + +echo "Checking syntax of config file: $CONFIG_FILE" +php "$CONFIG_FILE" + +exec /usr/bin/supervisord -c /supervisor.conf --pidfile /run/supervisord.pid diff --git a/config/ASP/nginx/nginx.conf b/config/ASP/etc/nginx/nginx.conf similarity index 96% rename from config/ASP/nginx/nginx.conf rename to config/ASP/etc/nginx/nginx.conf index 685fdf32..d391c0ae 100644 --- a/config/ASP/nginx/nginx.conf +++ b/config/ASP/etc/nginx/nginx.conf @@ -52,12 +52,15 @@ http { set_real_ip_from 192.168.0.0/16; real_ip_header X-Real-IP; + # Redirect to /ASP + location = / { + return 301 /ASP/; + } + # We don't want to pass calls to css, script, and image files to the index, # whether they exist or not. So quit right here, and allow direct access # to common format files. Add formats here to allow direct link access location ~ \.(gif|png|jpe?g|bmp|css|js|swf|wav|avi|mpg|ttf|woff)$ { - add_header Cache-Control "public, s-maxage=600, maxage=600"; - # Show empty flag location ~ (/ASP/frontend/images/maps)/.*\.(png|jpeg|jpg|gif)$ { add_header Cache-Control "public, s-maxage=600, maxage=600"; @@ -73,6 +76,8 @@ http { add_header Cache-Control "public, s-maxage=600, maxage=600"; try_files $uri $1/small/-1.png =404; } + access_log off; + add_header Cache-Control "public, s-maxage=600, maxage=600"; try_files $uri =404; } @@ -106,7 +111,7 @@ http { fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param HTTP_MOD_REWRITE On; # Tell PHP that the mod_rewrite module is ENABLED. fastcgi_index index.php; - fastcgi_pass asp-php:9000; + fastcgi_pass 127.0.0.1:9000; } location / { diff --git a/config/ASP/php-fpm.d/www.conf b/config/ASP/php-fpm.d/www.conf deleted file mode 100644 index b931b2cf..00000000 --- a/config/ASP/php-fpm.d/www.conf +++ /dev/null @@ -1,10 +0,0 @@ -[www] -user = www-data -group = www-data -security.limit_extensions = .php -pm = dynamic -pm.max_children = 5 -pm.start_servers = 2 -pm.min_spare_servers = 1 -pm.max_spare_servers = 3 -pm.status_path = /status.php diff --git a/config/ASP/ranks.php b/config/ASP/ranks.php deleted file mode 100644 index 0b76d79d..00000000 --- a/config/ASP/ranks.php +++ /dev/null @@ -1,253 +0,0 @@ - array( - 'title' => 'Private', - 'points' => 0, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 0, - 'has_awards' => array() - ), - 1 => array( - 'title' => 'Private First Class', - 'points' => 150, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 0, - 'has_awards' => array() - ), - 2 => array( - 'title' => 'Lance Corporal', - 'points' => 500, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 1, - 'has_awards' => array() - ), - 3 => array( - 'title' => 'Corporal', - 'points' => 800, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 2, - 'has_awards' => array() - ), - 4 => array( - 'title' => 'Sergeant', - 'points' => 2500, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 3, - 'has_awards' => array() - ), - 5 => array( - 'title' => 'Staff Sergeant', - 'points' => 5000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 4, - 'has_awards' => array() - ), - 6 => array( - 'title' => 'Gunnery Sergeant', - 'points' => 8000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 5, - 'has_awards' => array() - ), - 7 => array( - 'title' => 'Master Sergeant', - 'points' => 20000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 6, - 'has_awards' => array() - ), - 8 => array( - 'title' => 'First Sergeant', - 'points' => 20000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 6, - 'has_awards' => array( - '1031105' => 1, // Engineer Combat Badge - '1031109' => 1, // Sniper Combat Badge - '1031113' => 1, // Medic Combat Badge - '1031115' => 1, // Spec Ops Combat Badge - '1031119' => 1, // Assault Combat Badge - '1031120' => 1, // Anti-tank Combat Badge - '1031121' => 1, // Support Combat Badge - '1031406' => 1, // Knife Combat Badge - '1031619' => 1 // Pistol Combat Badge - ) - ), - 9 => array( - 'title' => 'Master Gunnery Sergeant', - 'points' => 50000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(7, 8), - 'has_awards' => array() - ), - 10 => array( - 'title' => 'Sergeant Major', - 'points' => 50000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(7, 8), - 'has_awards' => array( - '1031923' => 1, // Ground Defense - '1220104' => 1, // Air Defense - '1220118' => 1, // Armor Badge - '1220122' => 1, // Aviator Badge - '1220803' => 1, // Helicopter Badge - '1222016' => 1 // Transport Badge - ) - ), - 11 => array( - 'title' => 'Sergeant Major of the Corp', - 'points' => 50000, - 'time' => 0, - 'skip' => true, - 'backend' => true, - 'has_rank' => 10, - 'has_awards' => array() - ), - 12 => array( - 'title' => '2nd Lieutenant', - 'points' => 60000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(9, 10, 11), - 'has_awards' => array() - ), - 13 => array( - 'title' => '1st Lieutenant', - 'points' => 75000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 12, - 'has_awards' => array() - ), - 14 => array( - 'title' => 'Captain', - 'points' => 90000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 13, - 'has_awards' => array() - ), - 15 => array( - 'title' => 'Major', - 'points' => 115000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 14, - 'has_awards' => array() - ), - 16 => array( - 'title' => 'Lieutenant Colonel', - 'points' => 125000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 15, - 'has_awards' => array() - ), - 17 => array( - 'title' => 'Colonel', - 'points' => 150000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 16, - 'has_awards' => array() - ), - 18 => array( - 'title' => 'Brigadier General', - 'points' => 180000, - 'time' => 3888000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 17, - 'has_awards' => array( - '1031105' => 2, // Engineer Combat Badge - '1031109' => 2, // Sniper Combat Badge - '1031113' => 2, // Medic Combat Badge - '1031115' => 2, // Spec Ops Combat Badge - '1031119' => 2, // Assault Combat Badge - '1031120' => 2, // Anti-tank Combat Badge - '1031121' => 2, // Support Combat Badge - '1031406' => 2, // Knife Combat Badge - '1031619' => 2 // Pistol Combat Badge - ) - ), - 19 => array( - 'title' => 'Major General', - 'points' => 180000, - 'time' => 4500000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 18, - 'has_awards' => array( - '1031923' => 2, // Ground Defense - '1220104' => 2, // Air Defense - '1220118' => 2, // Armor Badge - '1220122' => 2, // Aviator Badge - '1220803' => 2, // Helicopter Badge - '1222016' => 2 // Transport Badge - ) - ), - 20 => array( - 'title' => 'Lieutenant General', - 'points' => 200000, - 'time' => 5184000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 19, - 'has_awards' => array() - ), - 21 => array( - 'title' => 'General', - 'points' => 200000, - 'time' => 0, - 'skip' => false, - 'backend' => true, - 'has_rank' => 20, - 'has_awards' => array() - ), -); \ No newline at end of file diff --git a/config/ASP/supervisor.conf b/config/ASP/supervisor.conf new file mode 100644 index 00000000..a7e7d801 --- /dev/null +++ b/config/ASP/supervisor.conf @@ -0,0 +1,44 @@ +[supervisord] +nodaemon=true +user=root +logfile=/dev/null +logfile_maxbytes=0 + +[program:nginx] +command=nginx -c /etc/nginx/nginx.conf -g 'daemon off;' +process_name=%(program_name)s_%(process_num)02d +user=root +numprocs=1 +autostart=true +autorestart=false +startsecs=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:php-fpm] +command=php-fpm -F +process_name=%(program_name)s_%(process_num)02d +user=root +numprocs=1 +autostart=true +autorestart=false +startsecs=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:tail] +command=/tail.sh +process_name=%(program_name)s_%(process_num)02d +user=root +numprocs=1 +autostart=true +autorestart=false +startsecs=0 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/config/ASP/tail.sh b/config/ASP/tail.sh new file mode 100755 index 00000000..86be84e7 --- /dev/null +++ b/config/ASP/tail.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -eu +echo "Tailing logs in /src/ASP/system/logs/*.log" +tail -n0 -F \ + /src/ASP/system/logs/asp_debug.log \ + /src/ASP/system/logs/php_errors.log \ + /src/ASP/system/logs/stats_debug.log \ + 2>/dev/null diff --git a/config/ASP/usr/local/etc/php-fpm.d/php-fpm.conf b/config/ASP/usr/local/etc/php-fpm.d/php-fpm.conf new file mode 100644 index 00000000..c01d475e --- /dev/null +++ b/config/ASP/usr/local/etc/php-fpm.d/php-fpm.conf @@ -0,0 +1,33 @@ +; Docs: https://www.php.net/manual/en/install.fpm.configuration.php +[global] +daemonize = no +error_log = /proc/self/fd/2 +; https://github.com/docker-library/php/pull/725#issuecomment-443540114 +log_limit = 8192 +log_buffering = no + +[default] +access.log = /proc/self/fd/2 +listen = 9000 +user = www-data +listen.owner = www-data +listen.group = www-data +pm = dynamic +pm.max_children = 10 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 +pm.process_idle_timeout = 10s +pm.max_requests = 500 +; Enable php to read env vars +clear_env = no +catch_workers_output = yes +decorate_workers_output = no +chdir = / +pm.status_path = /status +; Allow .aspx extension +security.limit_extensions = .php .aspx +php_admin_value[log_errors] = On +php_admin_value[expose_php] = Off +php_admin_value[display_errors] = Off +php_admin_value[date.timezone] = UTC diff --git a/config/ASP/php/conf.d/php.ini b/config/ASP/usr/local/etc/php/conf.d/php.ini similarity index 100% rename from config/ASP/php/conf.d/php.ini rename to config/ASP/usr/local/etc/php/conf.d/php.ini diff --git a/config/bf2/mods/bf2/ai/aidefault-custom.ai b/config/bf2/mods/bf2/ai/aidefault-custom.ai new file mode 100644 index 00000000..129043b9 --- /dev/null +++ b/config/bf2/mods/bf2/ai/aidefault-custom.ai @@ -0,0 +1,21 @@ +echo ***************************************************************************************** +echo AIDefault.ai **************************************************************************** +echo ***************************************************************************************** + +aiSettings.setNSides 2 +aiSettings.setAutoSpawnBots 1 + +rem Let's go with 63 bots! Hope your system holds up +aiSettings.overrideMenuSettings 1 +aiSettings.setMaxNBots 63 +aiSettings.maxBotsIncludeHumans 0 +aiSettings.setBotSkill 1.0 + +run BotNames.ai + +aiSettings.setInformationGridDimension 32 + + +run AIPathFinding.ai + +rem EOF diff --git a/config/bf2/mods/bf2/settings/maplist-custom-coop.con b/config/bf2/mods/bf2/settings/maplist-custom-coop.con new file mode 100644 index 00000000..bb49e48a --- /dev/null +++ b/config/bf2/mods/bf2/settings/maplist-custom-coop.con @@ -0,0 +1,24 @@ +; mods/bf2 coop +maplist.append dalian_plant gpm_coop 16 +maplist.append daqing_oilfields gpm_coop 16 +maplist.append dragon_valley gpm_coop 16 +maplist.append fushe_pass gpm_coop 16 +maplist.append greatwall gpm_coop 16 +maplist.append gulf_of_oman gpm_coop 16 +maplist.append midnight_sun gpm_coop 16 +maplist.append operation_clean_sweep gpm_coop 16 +maplist.append operationharvest gpm_coop 16 +maplist.append operationroadrage gpm_coop 16 +maplist.append operationsmokescreen gpm_coop 16 +maplist.append road_to_jalalabad gpm_coop 16 +maplist.append sharqi_peninsula gpm_coop 16 +maplist.append songhua_stalemate gpm_coop 16 +maplist.append taraba_quarry gpm_coop 16 +maplist.append zatar_wetlands gpm_coop 16 + +; mods/xpack coop +maplist.append ghost_town gpm_coop 16 +maplist.append mass_destruction gpm_coop 16 +maplist.append night_flight gpm_coop 16 +maplist.append surge gpm_coop 16 +maplist.append warlord gpm_coop 16 diff --git a/config/bf2/mods/bf2/settings/maplist-custom-cq.con b/config/bf2/mods/bf2/settings/maplist-custom-cq.con new file mode 100644 index 00000000..fc529e84 --- /dev/null +++ b/config/bf2/mods/bf2/settings/maplist-custom-cq.con @@ -0,0 +1,82 @@ +; mods/bf2 conquest +maplist.append dalian_plant gpm_cq 16 +maplist.append dalian_plant gpm_cq 32 +maplist.append dalian_plant gpm_cq 64 +maplist.append daqing_oilfields gpm_cq 16 +maplist.append daqing_oilfields gpm_cq 32 +maplist.append daqing_oilfields gpm_cq 64 +maplist.append dragon_valley gpm_cq 128 +maplist.append dragon_valley gpm_cq 16 +maplist.append dragon_valley gpm_cq 32 +maplist.append dragon_valley gpm_cq 64 +maplist.append fushe_pass gpm_cq 16 +maplist.append fushe_pass gpm_cq 32 +maplist.append fushe_pass gpm_cq 64 +maplist.append greatwall gpm_cq 16 +maplist.append greatwall gpm_cq 32 +maplist.append gulf_of_oman gpm_cq 16 +maplist.append gulf_of_oman gpm_cq 32 +maplist.append gulf_of_oman gpm_cq 64 +maplist.append highway_tampa gpm_cq 128 +maplist.append highway_tampa gpm_cq 16 +maplist.append highway_tampa gpm_cq 32 +maplist.append highway_tampa gpm_cq 64 +maplist.append highway_tampa gpm_cq 8 +maplist.append mashtuur_city gpm_cq 16 +maplist.append mashtuur_city gpm_cq 32 +maplist.append mashtuur_city gpm_cq 64 +maplist.append midnight_sun gpm_cq 16 +maplist.append midnight_sun gpm_cq 32 +maplist.append midnight_sun gpm_cq 64 +maplist.append operation_blue_pearl gpm_cq 16 +maplist.append operation_blue_pearl gpm_cq 32 +maplist.append operation_blue_pearl gpm_cq 64 +maplist.append operation_clean_sweep gpm_cq 16 +maplist.append operation_clean_sweep gpm_cq 32 +maplist.append operation_clean_sweep gpm_cq 64 +maplist.append operationharvest gpm_cq 16 +maplist.append operationharvest gpm_cq 32 +maplist.append operationharvest gpm_cq 64 +maplist.append operationroadrage gpm_cq 16 +maplist.append operationroadrage gpm_cq 32 +maplist.append operationroadrage gpm_cq 64 +maplist.append operationsmokescreen gpm_cq 16 +maplist.append operationsmokescreen gpm_cq 32 +maplist.append road_to_jalalabad gpm_cq 16 +maplist.append road_to_jalalabad gpm_cq 32 +maplist.append road_to_jalalabad gpm_cq 64 +maplist.append sharqi_peninsula gpm_cq 16 +maplist.append sharqi_peninsula gpm_cq 32 +maplist.append sharqi_peninsula gpm_cq 64 +maplist.append songhua_stalemate gpm_cq 16 +maplist.append songhua_stalemate gpm_cq 32 +maplist.append songhua_stalemate gpm_cq 64 +maplist.append taraba_quarry gpm_cq 16 +maplist.append taraba_quarry gpm_cq 32 +maplist.append wake_island_2007 gpm_cq 64 +maplist.append zatar_wetlands gpm_cq 16 +maplist.append zatar_wetlands gpm_cq 32 +maplist.append zatar_wetlands gpm_cq 64 + +; mods/xpack cq +maplist.append ghost_town gpm_cq 16 +maplist.append ghost_town gpm_cq 32 +maplist.append ghost_town gpm_cq 64 +maplist.append iron_gator gpm_cq 16 +maplist.append iron_gator gpm_cq 32 +maplist.append iron_gator gpm_cq 64 +maplist.append leviathan gpm_cq 16 +maplist.append leviathan gpm_cq 32 +maplist.append leviathan gpm_cq 64 +maplist.append mass_destruction gpm_cq 16 +maplist.append mass_destruction gpm_cq 32 +maplist.append mass_destruction gpm_cq 64 +maplist.append night_flight gpm_cq 16 +maplist.append night_flight gpm_cq 32 +maplist.append night_flight gpm_cq 64 +maplist.append surge gpm_cq 16 +maplist.append surge gpm_cq 32 +maplist.append surge gpm_cq 64 +maplist.append warlord gpm_cq 16 +maplist.append warlord gpm_cq 32 +maplist.append warlord gpm_cq 64 diff --git a/config/bf2/mods/bf2/settings/serversettings-custom.con b/config/bf2/mods/bf2/settings/serversettings-custom.con new file mode 100644 index 00000000..fc65057d --- /dev/null +++ b/config/bf2/mods/bf2/settings/serversettings-custom.con @@ -0,0 +1,68 @@ +sv.serverName "Default Server Name" +sv.password "" +sv.internet 1 +sv.serverIP "" +sv.serverPort 16567 +sv.welcomeMessage "" +sv.punkBuster 1 +sv.allowFreeCam 1 +sv.allowExternalViews 1 +sv.allowNoseCam 1 +sv.hitIndicator 1 +sv.maxPlayers 64 +sv.numPlayersNeededToStart 0 +sv.notEnoughPlayersRestartDelay 15 +sv.startDelay 15 +sv.endDelay 15 +sv.spawnTime 15 +sv.manDownTime 15 +sv.endOfRoundDelay 15 +sv.ticketRatio 100 +sv.roundsPerMap 3 +sv.timeLimit 0 +sv.scoreLimit 0 +sv.soldierFriendlyFire 100 +sv.vehicleFriendlyFire 100 +sv.soldierSplashFriendlyFire 100 +sv.vehicleSplashFriendlyFire 100 +sv.tkPunishEnabled 1 +sv.tkNumPunishToKick 3 +sv.tkPunishByDefault 0 +sv.votingEnabled 1 +sv.voteTime 15 +sv.minPlayersForVoting 1 +sv.teamVoteOnly 1 +sv.gameSpyPort 29900 +sv.allowNATNegotiation 0 +sv.interfaceIP "" +sv.autoRecord 0 +sv.demoIndexURL http:// +sv.demoDownloadURL http:// +sv.autoDemoHook "adminutils/demo/rotate_demo.py" +sv.demoQuality 1 +sv.adminScript "default" +sv.timeBeforeRestartMap 30 +sv.autoBalanceTeam 0 +sv.teamRatioPercent 100 +sv.coopBotRatio 100 +sv.coopBotCount 16 +sv.coopBotDifficulty 50 +sv.voipEnabled 1 +sv.voipQuality 3 +sv.voipServerRemote 0 +sv.voipServerRemoteIP "" +sv.voipServerPort 55125 +sv.voipBFClientPort 55123 +sv.voipBFServerPort 55124 +sv.voipSharedPassword "" +sv.useGlobalRank 1 +sv.useGlobalUnlocks 1 +sv.noVehicles 0 +sv.sponsorText "" +sv.sponsorLogoURL "https://example.com/bf2logo.png" +sv.communityLogoURL "https://example.com/bf2logo.png" +sv.radioSpamInterval 6 +sv.radioMaxSpamFlagCount 6 +sv.radioBlockedDurationTime 30 +sv.numReservedSlots 0 +sv.friendlyFireWithMines 1 diff --git a/config/bf2/python/bf2/BF2StatisticsConfig-custom.py b/config/bf2/python/bf2/BF2StatisticsConfig-custom.py new file mode 100644 index 00000000..a71834b6 --- /dev/null +++ b/config/bf2/python/bf2/BF2StatisticsConfig-custom.py @@ -0,0 +1,69 @@ +# ------------------------------------------------------------------------------ +# BF2Statistics 3.0.0 - Config File +# ------------------------------------------------------------------------------ +# Conventions: +# 0 -> Disable +# 1 -> Enable +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# Debug Logging +# ------------------------------------------------------------------------------ +debug_enable = 1 +debug_log_path = 'python/bf2/logs' # Relative from BF2 base folder +debug_fraglog_enable = 0 # Detailed 'Fragalyzer' Logs (requires existing folder "mods//logs/") + +# ------------------------------------------------------------------------------ +# Statistics Enabling +# ------------------------------------------------------------------------------ +# 0 = disable statistics, 1 = enable statistics (requires an ASP stats server) +# By disabling the stats, this server will be "non-ranked" +# +# An AuthID and AuthToken are required to post stats data to the ASP backend. +# Contact your local Stats Admin to recieve an AuthID and AuthToken. Both of +# which are NOT to be shared with anyone! +# ------------------------------------------------------------------------------ +stats_enable = 1 +stats_auth_id = 112950 # Required to post stats data at the end of round. +stats_auth_token = 'GGB6Vj70Nmf98nm7' # Required to post stats data at the end of round. + +# ------------------------------------------------------------------------------ +# ASP Stats Backend Web Server +# ------------------------------------------------------------------------------ +http_backend_addr = 'asp' # Reach ASP over the `bf2-network` docker network. If your ASP is on another host, use its hostname or domain name here. +http_backend_port = 80 +http_backend_asp = '/ASP/bf2statistics.php' + +# ------------------------------------------------------------------------------ +# Snapshot Logging +# ------------------------------------------------------------------------------ +# Enables server to make snapshot backups. +# 0 = log only on error sending to backend +# 1 = all snapshots +# ------------------------------------------------------------------------------ +snapshot_logging = 1 +snapshot_log_path_sent = 'python/bf2/logs/snapshots/sent' # Relative from the BF2 base folder +snapshot_log_path_unsent = 'python/bf2/logs/snapshots/unsent' # Relative from the BF2 base folder + +# ------------------------------------------------------------------------------ +# Medals Processing +# ------------------------------------------------------------------------------ +# Suffix for your custom medals file(s). +# Example: A profile named "custom" = medal_data_custom.py +# ------------------------------------------------------------------------------ +medals_custom_data = 'custom' +# A list of mods that xpack (special forces) medals can be earned while playing +# Example: ['mods/xpack', 'mods/bf2', 'mods/ModName'] (all entries must be lower case!!) +medals_xpack_mods = ['mods/bf2sfsp','mods/xpack'] + +# ------------------------------------------------------------------------------ +# Player Manager +# ------------------------------------------------------------------------------ +# Local IP address for AI Bots +# ------------------------------------------------------------------------------ +pm_ai_player_addr = '127.0.0.1' # Not recommended to change + + +# ------------------------------------------------------------------------------ +# END CONFIGURATION +# ------------------------------------------------------------------------------ diff --git a/config/db/setup.sql b/config/db/setup.sql new file mode 100644 index 00000000..fddb4b98 --- /dev/null +++ b/config/db/setup.sql @@ -0,0 +1,142 @@ +-- -------------------------------------------------------- +-- Setup the DB the first time in development +-- -------------------------------------------------------- + +-- Authorize the defalt stats provider +UPDATE stats_provider SET authorized = 1 WHERE id = 1; +SELECT * FROM stats_provider; + +UPDATE stats_provider_auth_ip SET address = '0.0.0.0/0' ; +SELECT * FROM stats_provider_auth_ip; + +-- Authorize bot players +INSERT INTO player (name, password, email, country, rank_id) values ('T. Karlsson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Edgren', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P.K. Johansson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Jonsson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Wallberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('A. Andersson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('H. Karlsson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('K. Bergqvist', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Hornlund', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Lopez', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Gothberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P. Hoyles', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Kopparhed', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Cyreus', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('T. Dahl', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Bagge', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('T. Laedre', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Martensson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Mod', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Eriksson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Sundberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('N. Goksu', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('N. Fegraeus', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('F. Lindblom', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Stenkvist', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Lord', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Fujita', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('E. Sjovold', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('T. Holmsten', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Ostman', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('T. Soderman', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Wiksten', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Sanick', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Salt', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Doran', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Lindgren', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Sjoberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Votypka', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('B. Pajor', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Andersson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P. Lindholm', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P. OShaughnessy', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('N. Stromquist', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Strandberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Fritze', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Norberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('B. Hedberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('O. Carlen', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('K. Hegethorn', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Sirland', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Dawsari', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Gustavsson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Bergqvist', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Kjellstrom', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P. Soderlund', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('H. Andersson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Gonzales', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Crabtree', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Tou', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('G. Pigula', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Le', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Pace', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('K. Lee', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Walton', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. King', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('F. Morales', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Castillo', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Persson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('U. Rask', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Dohl', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Kylmamaa', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Parkinson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Newton', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('A. Papasavas', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Josephson', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Decker', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Rudberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Hedlund', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Grass', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Aberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Smedberg', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('P. Osterblom', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Evans', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Abdey', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Aberin', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Belanger', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Biro', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Brassard', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Ceron', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Choy', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Davey', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('E. Douridas', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Elliott', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Evans', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Gimbel', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Hallwood', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Hart', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('K. Hoang', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Kerr', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('F. Liliegren', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Linde', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Livesey', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('R. Love', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('A. Marini', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. North', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Pinkerton', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Price', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Rickard', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Ross', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('E. Smith', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('N. White', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('K. Yip', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('W. Young', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('D. Yee', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Vifian', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('B. Smith', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('L. Brown', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('S. Flyte', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Barnett', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('M. Cassidy', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('T. Kingston', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('I. Ackworth', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. VanRooyen', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Hartling', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('J. Hargelid', '', 'bot@botNames.ai', 'US', 0); +INSERT INTO player (name, password, email, country, rank_id) values ('C. Clarke', '', 'bot@botNames.ai', 'US', 0); + +-- Authorize a 'test' player of id '100000001'. The ID '100000001' is the ID of the first player to register with the master server (PRMasterServer) +INSERT INTO player (id, name, password, email, country, rank_id) values (100000001, 'test', '098f6bcd4621d373cade4e832627b4f6', 'test@test.com', 'US', 0); +SELECT id, name, password, email, country, rank_id FROM player; diff --git a/docker-compose.build.yml b/docker-compose.build.yml index 7674decb..610b5b0e 100644 --- a/docker-compose.build.yml +++ b/docker-compose.build.yml @@ -1,15 +1,8 @@ # This is a docker compose override file, for development builds with caching for CI environments services: - asp-nginx: + asp: build: cache_from: - - type=local,src=/tmp/.buildx-cache-web - cache_to: - - type=local,dest=/tmp/.buildx-cache-web,mode=max - - asp-php: - build: - cache_from: - - type=local,src=/tmp/.buildx-cache-php - cache_to: - - type=local,dest=/tmp/.buildx-cache-php,mode=max + - type=local,src=/tmp/.buildx-cache-asp + # cache_to: + # - type=local,dest=/tmp/.buildx-cache-asp,mode=maxmax diff --git a/docker-compose.test.yml b/docker-compose.test.yml deleted file mode 100644 index bde9817d..00000000 --- a/docker-compose.test.yml +++ /dev/null @@ -1,72 +0,0 @@ -version: '2.2' -services: - test-routes: - image: alpine:latest - environment: - URLS: | - http://asp-nginx/ 403 - http://asp-nginx/.htaccess 401 - http://asp-nginx/ASP/ 200 - http://asp-nginx/ASP/aspx 401 - http://asp-nginx/ASP/bf2statistics.php 403 - http://asp-nginx/ASP/frontend 401 - http://asp-nginx/ASP/frontend/css/bootstrap.min.css 200 - http://asp-nginx/ASP/frontend/images/maps/foo.png 200 - http://asp-nginx/ASP/frontend/images/ranks/foo.png 200 - http://asp-nginx/ASP/frontend/images/armies/foo.png 200 - http://asp-nginx/ASP/frontend/css/bootstrap.min.css 200 - http://asp-nginx/ASP/createplayer.aspx 200 - http://asp-nginx/ASP/getawardsinfo.aspx 200 - http://asp-nginx/ASP/getbackendinfo.aspx 200 - http://asp-nginx/ASP/getleaderboard.aspx 200 - http://asp-nginx/ASP/getmapinfo.aspx 200 - http://asp-nginx/ASP/getplayerid.aspx 200 - http://asp-nginx/ASP/getplayerinfo.aspx 200 - http://asp-nginx/ASP/getrankinfo.aspx 200 - http://asp-nginx/ASP/getunlocksinfo.aspx 200 - http://asp-nginx/ASP/ranknotification.aspx 200 - http://asp-nginx/ASP/searchforplayers.aspx 200 - http://asp-nginx/ASP/selectunlock.aspx 200 - http://asp-nginx/ASP/verifyplayer.aspx 200 - http://asp-nginx/ASP/index.php 200 - http://asp-nginx/ASP/ranknotification.aspx 200 - http://asp-nginx/ASP/searchforplayers.aspx 200 - http://asp-nginx/ASP/selectunlock.aspx 200 - http://asp-nginx/ASP/getplayerinfo.aspx 200 - http://asp-nginx/ASP/system 401 - networks: - - bf2-network - entrypoint: - - /bin/sh - command: - - -c - - | - set -eu - - echo "Waiting for stack to be ready" - s=0 - while true; do - nc -vz -w 1 asp-nginx 80 \ - && nc -vz -w 1 asp-php 9000 \ - && nc -vz -w 1 db 3306 \ - && break || true - s=$$(( $$s + 1 )) - if [ "$$s" -eq 600 ]; then - exit 1 - fi - echo "Retrying in 3 seconds" - sleep 3 - done - - echo "$$URLS" | awk NF | while read -r i j; do - if wget -q -SO- "$$i" 2>&1 | grep "HTTP/1.1 $$j " > /dev/null; then - echo "PASS: $$i" - else - echo "FAIL: $$i" - exit 1 - fi - done - -networks: - bf2-network: - name: bf2-network diff --git a/docker-compose.yml b/docker-compose.yml index eac55880..a5317629 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,54 @@ version: '2.2' services: + # Battlefield 2 1.5 server with bf2stats 3.2.0 python scripts + bf2: + image: startersclan/docker-bf2:v1.5.3153.0-bf2stats-3.2.0 + volumes: + - ./config/bf2/mods/bf2/ai/aidefault-custom.ai:/server/bf2/mods/bf2/ai/aidefault.ai:ro # Customize bots + - ./config/bf2/mods/bf2/settings/serversettings-custom.con:/server/bf2/mods/bf2/settings/serversettings.con:ro # Server config + - ./config/bf2/mods/bf2/settings/maplist-custom-coop.con:/server/bf2/mods/bf2/settings/maplist.con:ro # Maplist (coop) + - ./config/bf2/python/bf2/BF2StatisticsConfig-custom.py:/server/bf2/python/bf2/BF2StatisticsConfig.py:ro # bf2stats python config + ports: + - 16567:16567/udp + - 29900:29900/udp + networks: + - gamespy-network + - bf2-network + depends_on: + - asp + restart: unless-stopped + tty: true + stdin_open: true + + # The Gamespy master server + prmasterserver: + image: startersclan/prmasterserver:v0.1.0 + volumes: + - prmasterserver-volume:/data + ports: + - 29900:29900/tcp # Login server + - 29901:29901/tcp # Login server + - 28910:28910/tcp # Master server + - 27900:27900/udp # Master server + - 29910:29910/udp # CD key server + networks: + # Spoof all gamespy DNS for the BF2 server connected to this network + gamespy-network: + aliases: + - battlefield2.available.gamespy.com + - battlefield2.master.gamespy.com + - battlefield2.ms14.gamespy.com + - master.gamespy.com + - motd.gamespy.com + - gpsp.gamespy.com + - gpcm.gamespy.com + - gamespy.com + restart: unless-stopped + init-container: image: alpine:latest volumes: - ./src:/src - - ./config/ASP/config.php:/src/ASP/system/config/config.php - - backups-volume:/src/ASP/system/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. - - cache-volume:/src/ASP/system/cache - - logs-volume:/src/ASP/system/logs - - snapshots-volume:/src/ASP/system/snapshots - db-volume:/var/lib/mysql entrypoint: - /bin/sh @@ -17,78 +57,47 @@ services: - | set -eu - echo "Granting nginx and php read permissions" - find /src -type d -exec chmod 755 {} \; - find /src -type f -exec chmod 644 {} \; - - echo "Granting php write permissions" - chmod 777 /src/ASP/system/config - chmod 666 /src/ASP/system/config/config.php - chmod 666 /src/ASP/system/config/config.php.bak || true - - find /src/ASP/system/backups -type d -exec chmod 777 {} \; - find /src/ASP/system/backups -type f -exec chmod 666 {} \; - - find /src/ASP/system/cache -type d -exec chmod 777 {} \; - find /src/ASP/system/cache -type f -exec chmod 666 {} \; - - find /src/ASP/system/config -type d -exec chmod 777 {} \; - find /src/ASP/system/config -type f -exec chmod 666 {} \; - - find /src/ASP/system/logs -type d -exec chmod 777 {} \; - find /src/ASP/system/logs -type f -exec chmod 666 {} \; - - mkdir -p /src/ASP/system/snapshots/failed - mkdir -p /src/ASP/system/snapshots/processed - mkdir -p /src/ASP/system/snapshots/unauthorized - mkdir -p /src/ASP/system/snapshots/unprocessed - find /src/ASP/system/snapshots -type d -exec chmod 777 {} \; - find /src/ASP/system/snapshots -type f -exec chmod 666 {} \; - echo "Granting db write permissions" chown -R 999:999 /var/lib/mysql - asp-nginx: + asp: build: - dockerfile: Dockerfile.nginx - context: . - target: dev - volumes: - - ./src:/src:ro - - ./config/ASP/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ports: - - 8081:80 - networks: - - bf2-network - depends_on: - - init-container - - asp-php - - asp-php: - build: - dockerfile: Dockerfile.php + dockerfile: Dockerfile context: . target: dev + labels: + - "traefik.enable=true" + - "traefik.docker.network=${COMPOSE_PROJECT_NAME?err}_traefik-network" + # traefik v2 + # http + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-gamespy-http.entrypoints=web" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-gamespy-http.rule=Host(`bf2web.gamespy.com`)" # Note: `bf2web.gamespy.com` doesn't need https. The BF2 client BFHQ makes a HTTP requests to `bf2web.gamespy.com` with `Host: bf2web.gamespy.com`. + environment: + # - XDEBUG_MODE=off # Uncomment to disable xdebug + # See ./src/ASP/system/config/config.php for all supported env vars + - DB_HOST=db + - DB_PORT=3306 + - DB_NAME=bf2stats + - DB_USER=root + - DB_PASS=ascent volumes: - ./src:/src - - ./config/ASP/config.php:/src/ASP/system/config/config.php # Main config file. Must be writeable or else ASP will throw an exception. Customize as needed - # - ./config/ASP/armyAbbreviationMap.php:/src/ASP/system/config/armyAbbreviationMap.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/backendAwards.php:/src/ASP/system/config/backendAwards.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/ranks.php:/src/ASP/system/config/ranks.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/php/conf.d/php.ini:/usr/local/etc/php/conf.d/php.ini:ro - # - ./config/ASP/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro - backups-volume:/src/ASP/system/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. - cache-volume:/src/ASP/system/cache + - config-volume:/src/ASP/system/config # For a stateful config file - logs-volume:/src/ASP/system/logs - snapshots-volume:/src/ASP/system/snapshots + ports: + - 8081:80 + - 9000 networks: - - bf2-network + bf2-network: + aliases: + - bf2web.gamespy.com # Spoof gamespy DNS for the BF2 server connected to this network extra_hosts: # For xdebug to reach the host via `host.docker.internal`. See: https://github.com/moby/moby/pull/40007#issuecomment-578729356 and https://stackoverflow.com/questions/49907308/installing-xdebug-in-docker # If xdebug does not work, you may need to add an iptables rule to the INPUT chain: iptables -A INPUT -i br+ -j ACCEPT - host.docker.internal:host-gateway - depends_on: - - init-container db: image: mariadb:10.8 @@ -98,6 +107,9 @@ services: # - MARIADB_PASSWORD=admin # Uncomment this if you want to create a regular user - MARIADB_DATABASE=bf2stats volumes: + - ./src/ASP/system/sql/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro # Seed the DB the first time + - ./src/ASP/system/sql/data.sql:/docker-entrypoint-initdb.d/02-data.sql:ro # Seed the DB the first time + - ./config/db/setup.sql:/docker-entrypoint-initdb.d/03-setup.sql:ro # Setup the DB the first time - ./config/db/my.cnf:/etc/my.cnf:ro - db-volume:/var/lib/mysql networks: @@ -116,11 +128,13 @@ services: networks: bf2-network: - name: bf2-network + gamespy-network: volumes: + prmasterserver-volume: backups-volume: cache-volume: + config-volume: logs-volume: snapshots-volume: db-volume: diff --git a/docs/full-bf2-stack-example/.env b/docs/full-bf2-stack-example/.env index 9a2f2c31..4cc5b70a 100644 --- a/docs/full-bf2-stack-example/.env +++ b/docs/full-bf2-stack-example/.env @@ -1 +1 @@ -COMPOSE_PROJECT_NAME=bf2stats +COMPOSE_PROJECT_NAME=asp diff --git a/docs/full-bf2-stack-example/README.md b/docs/full-bf2-stack-example/README.md index c26a2cd8..087ec353 100644 --- a/docs/full-bf2-stack-example/README.md +++ b/docs/full-bf2-stack-example/README.md @@ -2,11 +2,11 @@ This example deploys a stack with BF2Statistics `ASP` `3.x.x`. -Note that the `bf2sclone` `2.2.0` included in this example does not work with BF2Statistics `3.x.x`, but is only for demonstrative purposes. The community has tried to [fix it](https://bf2statistics.com/threads/bf2sclone-v3.2972/), but so far none seem to have shared their working changes. Wilson212, the original author of BF2Statistics did mention to be working on it, see [here](https://bf2statistics.com/threads/bf2statistics-v3-1-0-full-release.3010/), so we might see it soon. If you want to have a working `bf2sclone`, use [BF2Statistics `2.x.x`](https://github.com/startersclan/bf2stats) in the meantime. +Note that the `bf2sclone` `2.x.x` does not work with BF2Statistics `3.x.x`, and so it is excluded from this example. The community has tried to [fix it](https://bf2statistics.com/threads/bf2sclone-v3.2972/), but so far none seem to have shared their working changes. Wilson212, the original author of BF2Statistics did mention to be working on it, see [here](https://bf2statistics.com/threads/bf2statistics-v3-1-0-full-release.3010/), so we might see it soon. If you want to have a working `bf2sclone`, use [BF2Statistics `2.x.x`](https://github.com/startersclan/bf2stats) in the meantime. ## Usage -In this example, we will use the domain name `example.com`. In production, you should use your own domain name for `traefik` (our TLS-terminating load balancer) to be able to serve HTTPS for the web endpoints: `ASP`, `bf2sclone`, and `phpmyadmin`. +In this example, we will use the domain name `example.com`. In production, you should use your own domain name for `traefik` (our TLS-terminating load balancer) to be able to serve HTTPS for the web endpoints: `ASP`, and `phpmyadmin`. ### 1. Setup hosts file @@ -15,7 +15,6 @@ Add a couple of hostnames to the `hosts` file for the web endpoints: ```sh # Since we are testing this stack locally, we need these DNS records in the hosts file echo '127.0.0.1 asp.example.com' | sudo tee -a /etc/hosts -echo '127.0.0.1 bf2sclone.example.com' | sudo tee -a /etc/hosts echo '127.0.0.1 phpmyadmin.example.com' | sudo tee -a /etc/hosts ``` @@ -51,18 +50,16 @@ You should see something like: ```sh $ docker-compose up -[+] Running 10/0 - ⠿ Container bf2stats-prmasterserver-1 Running 0.0s - ⠿ Container bf2stats-phpmyadmin-1 Running 0.0s - ⠿ Container bf2stats-bf2-1 Running 0.0s - ⠿ Container bf2stats-traefik-1 Running 0.0s - ⠿ Container bf2stats-init-container-1 Created 0.0s - ⠿ Container bf2stats-db-1 Running 0.0s - ⠿ Container bf2stats-asp-php-1 Running 0.0s - ⠿ Container bf2stats-bf2sclone-php-1 Running 0.0s - ⠿ Container bf2stats-asp-nginx-1 Running 0.0s - ⠿ Container bf2stats-bf2sclone-nginx-1 Running 0.0s -Attaching to bf2stats-asp-nginx-1, bf2stats-asp-php-1, bf2stats-bf2-1, bf2stats-bf2sclone-nginx-1, bf2stats-bf2sclone-php-1, bf2stats-coredns-1, bf2stats-db-1, bf2stats-init-container-1, bf2stats-phpmyadmin-1, bf2stats-prmasterserver-1, bf2stats-traefik-1 +[+] Running 8/8 + ✔ Container bf2stats-coredns-1 Created 0.0s + ✔ Container bf2stats-init-container-1 Created 0.0s + ✔ Container bf2stats-db-1 Created 0.0s + ✔ Container bf2stats-phpmyadmin-1 Created 0.0s + ✔ Container bf2stats-prmasterserver-1 Created 0.0s + ✔ Container bf2stats-traefik-1 Created 0.0s + ✔ Container bf2stats-asp-1 Recreated 0.0s + ✔ Container bf2stats-bf2-1 Recreated 0.0s +Attaching to bf2stats-asp-1, bf2stats-bf2-1, bf2stats-coredns-1, bf2stats-db-1, bf2stats-init-container-1, bf2stats-phpmyadmin-1, bf2stats-prmasterserver-1, bf2stats-traefik-1 ``` The full stack is now running: @@ -72,7 +69,6 @@ The full stack is now running: - `coredns` available on your external IP address on UDP port `53` on your external IP address - `traefik` (TLS-terminated reverse web proxy) available on port `80` and `443` on your external IP address - `ASP` available at https://asp.example.com on your external IP address. -- `bf2sclone` available at https://bf2sclone.example.com on your external IP address. - `phpmyadmin` available at https://phpmyadmin.example.com on your external IP address. > If you are behind NAT, you will need to forward all of the above TCP and UDP ports to your external IP address, in order for clients to reach your gameserver and webserver over the internet. @@ -81,7 +77,7 @@ The full stack is now running: Visit https://asp.example.com/ASP and login using `$admin_user` and `$admin_pass` defined in its [config file](./config/ASP/config.php). -> Since traefik hasn't got a valid TLS certificate via `ACME`, it will serve the `TRAEFIK DEFAULT CERT`. The browser will show a security issue when visiting https://asp.example.com, https://bf2sclone.example.com, and https://phpmyadmin.example.com. Simply click "visit site anyway" button to get past the security check. +> Since traefik hasn't got a valid TLS certificate via `ACME`, it will serve the `TRAEFIK DEFAULT CERT`. The browser will show a security issue when visiting https://asp.example.com and https://phpmyadmin.example.com. Simply click "visit site anyway" button to get past the security check. Click on `System > System Installation` and install the DB using `$db_host`,`$db_port`,`$db_name`,`$db_user`,`$db_pass` you defined in [`config.php`](./config/ASP/config.php). Click `System > System Tests` and `Run System Tests` and all tests should be green, except for the `BF2Statistics Processing` test and the four `.aspx` tests, because we still don't have a Fully Qualified Domain Name (FQDN) with a public DNS record. @@ -96,17 +92,17 @@ docker-compose restart bf2 Configure `coredns` to spoof all gamespy DNS in [config/coredns/hosts](config/coredns/hosts), replacing the IP addresses with your machine's external IP address which you specified in Step `2.`. Assuming your external IP is `192.168.1.100`, it should look like: ```txt -192.168.1.100 eapusher.dice.se 192.168.1.100 battlefield2.available.gamespy.com 192.168.1.100 battlefield2.master.gamespy.com 192.168.1.100 battlefield2.ms14.gamespy.com -192.168.1.100 gamestats.gamespy.com 192.168.1.100 master.gamespy.com 192.168.1.100 motd.gamespy.com 192.168.1.100 gpsp.gamespy.com 192.168.1.100 gpcm.gamespy.com 192.168.1.100 gamespy.com 192.168.1.100 bf2web.gamespy.com +192.168.1.100 gamestats.gamespy.com +192.168.1.100 eapusher.dice.se ``` Save the file. `coredns` immediately reads the changed file and serves the updated DNS records. @@ -146,7 +142,6 @@ At the end of the first game, you should see your stats updated at https://bf2sc ### Cheat sheet - Visit https://asp.example.com/ASP to adminstrate your stats database and gamespy server. Login using `$admin_user` and `$admin_pass` defined in its [config file](./config/ASP/config.php). -- Visit https://bf2sclone.example.com to view your stats over the web. It's a nice pretty web interface. Your stats will be updated at the end of each gameserver round. - Visit https://phpmyadmin.example.com if you want to self-manage your DB (if you know how). Login using user `root` and password `MARIADB_ROOT_PASSWORD` (or `MARIADB_USER` and `MARIADB_PASSWORD`) defined on the `db` service in [docker-compose.yml](./docker-compose.yml) - This example includes all the configuration files for each stack component. Customize them to suit your needs. - Mount the `ASP` [`config.php`](./config/ASP/config.php) with write permissions, or else `ASP` dashboard will throw an error. Use `System > Edit Configuration` as reference to customize the config file. @@ -156,7 +151,7 @@ At the end of the first game, you should see your stats updated at https://bf2sc - In a production setup, you want to make sure: - to use a custom domain name (FQDN) - to configure `traefik` to be issued an ACME certificate for HTTPS to work for the web endpoints - - to run `traefik` on `--network host` or to use the PROXY protocol, so that it preserves client IP addresses, + - to run `traefik` on `--network host` or to use the PROXY protocol, so that it preserves client IP addresses - to run the `bf2` server and `prmasterserver` on `--network host` so that they: 1) talk to each other over the machine's external interface - to use stronger authentication in front of the `ASP` and `phpmyadmin`, which don't have in-built strong authentication - to use strong passwords for the `ASP` admin user in [config file](./config/ASP/config.php) @@ -186,10 +181,23 @@ iptables -A INPUT -p udp -m udp -m conntrack --ctstate NEW --dport 80 -j ACCEPT iptables -A INPUT -p udp -m udp -m conntrack --ctstate NEW --dport 443 -j ACCEPT # Attach to the bf2 server console -docker attach bf2stats_bf2_1 +docker attach asp_bf2_1 # Copy logs from bf2 server to this folder -docker cp bf2stats_bf2_1:/server/bf2/python/bf2/logs . +docker cp asp_bf2_1:/server/bf2/python/bf2/logs . + +# asp - Exec into container +docker exec -it $( docker-compose ps -q asp ) sh +# asp - List backups +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/backups +# asp - List cache +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/cache +# asp - List config +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/config +# asp - List logs +docker exec -it $( docker-compose ps -q asp ) ls -al /src/ASP/system/logs +# asp - List snapshots +docker exec -it $( docker-compose ps -q asp ) ls -alR /src/ASP/system/snapshots/ # Dump the DB docker exec $( docker-compose ps | grep db | awk '{print $1}' ) mysqldump -uroot -pascent bf2stats | gzip > bf2stats.sql.gz @@ -202,19 +210,19 @@ docker-compose down # Cleanup. Warning: This destroys the all data! docker-compose down -docker volume rm bf2stats_prmasterserver-volume -docker volume rm bf2stats_traefik-acme-volume -docker volume rm bf2stats_backups-volume -docker volume rm bf2stats_cache-volume -docker volume rm bf2stats_logs-volume -docker volume rm bf2stats_snapshots-volume -docker volume rm bf2stats_bf2sclone-cache-volume -docker volume rm bf2stats_db-volume +docker volume rm asp_prmasterserver-volume +docker volume rm asp_traefik-acme-volume +docker volume rm asp_backups-volume +docker volume rm asp_cache-volume +docker volume rm asp_config-volume +docker volume rm asp_logs-volume +docker volume rm asp_snapshots-volume +docker volume rm asp_db-volume ``` ## Background: Keeping Battlefield 2 working -Problem: The Battlefield 2 client and server binaries are hardcoded with gamespy DNS records, e.g. `bf2web.gamespy.com`. Because gamespy has shut down, the DNS records no longer exist on public DNS servers. In order to keep the game's multiplayer working, we need: +Problem: The Battlefield 2 client and server binaries are hardcoded with gamespy DNS records, e.g. `bf2web.gamespy.com`. Because gamespy has shut down, the DNS records no longer exist on public DNS servers (read more about it [here](https://github.com/startersclan/docker-bf2/master/docs/usage.md#dns-spoofing)). In order to keep the game's multiplayer working, we need: - A gamespy replacement - solved by `PRMasterServer` - DNS resolution for gamespy DNS records - solved by either by: `1.` hex patching the game binaries; `2.` spoofing DNS server responses; `3.` spoofing DNS records via `hosts` file diff --git a/docs/full-bf2-stack-example/config/ASP/armyAbbreviationMap.php b/docs/full-bf2-stack-example/config/ASP/armyAbbreviationMap.php deleted file mode 100644 index 3fa3c298..00000000 --- a/docs/full-bf2-stack-example/config/ASP/armyAbbreviationMap.php +++ /dev/null @@ -1,204 +0,0 @@ - [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'mec' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'ch' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - - // Xpack 1 Special Forces - 'seal' => [ - 'flag' => 0, - 'name' => 'United States Navy Seals' - ], - 'mecsf' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition Special Forces' - ], - 'sas' => [ - 'flag' => 4, - 'name' => 'British Special Air Service' - ], - 'spetz' => [ - 'flag' => 5, - 'name' => 'Russian Spetsnaz' - ], - 'chinsurgent' => [ - 'flag' => 7, - 'name' => 'Rebels' - ], - 'meinsurgent' => [ - 'flag' => 8, - 'name' => 'Insurgents' - ], - - // Booster Pack 1 EuroForce - 'eu' => [ - 'flag' => 9, - 'name' => 'European Union' - ], - - // POE2 - 'ger' => [ - 'flag' => 10, - 'name' => 'German Forces' - ], - 'ukr' => [ - 'flag' => 12, - 'name' => 'Ukrainian Forces' - ], - - // AIX - 'un' => [ - 'flag' => 13, - 'name' => 'United Nations' - ], - - // Hard Justice - 'us2' => [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'us3' => [ - 'flag' => 0, - 'name' => 'United States Marine Corps' - ], - 'mec2' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'mec3' => [ - 'flag' => 1, - 'name' => 'Middle Eastern Coalition' - ], - 'ch2' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - 'ch3' => [ - 'flag' => 2, - 'name' => 'People\'s Liberation Army' - ], - 'ca' => [ - 'flag' => 13, - 'name' => 'Canadian Forces' - ], - - // Nations at war 8.0 - 'blackwater' => [ - 'flag' => 14, - 'name' => 'Blackwater Military Contractors' - ], - 'taliban' => [ - 'flag' => 15, - 'name' => 'Taliban Forces' - ], - 'au' => [ - 'flag' => 16, - 'name' => 'Australian Forces' - ], - 'ru' => [ - 'flag' => 17, - 'name' => 'Russian Forces' - ], - 'gb' => [ - 'flag' => 18, - 'name' => 'British Forces' - ], - 'nato' => [ - 'flag' => 19, - 'name' => 'NATO Forces' - ], - 'isis' => [ - 'flag' => 20, - 'name' => 'ISIS Forces' - ], - 'iraqa' => [ - 'flag' => 21, - 'name' => 'Iraqi Forces' - ], - 'usmc' => [ - 'flag' => 0, - 'name' => 'United States Marines Corps' - ], - 'somalia' => [ - 'flag' => 23, - 'name' => 'Somalian Forces' - ], - 'rangers' => [ - 'flag' => 0, - 'name' => 'U.S Army Rangers' - ], - 'idf' => [ - 'flag' => 25, - 'name' => 'Israel Defense Force' - ], - 'chsf' => [ - 'flag' => 2, - 'name' => 'Chinese Special Forces' - ], - 'paras' => [ - 'flag' => 27, - 'name' => 'British Paratroop Regiment' - ], - 'casf' => [ - 'flag' => 28, - 'name' => 'Canadian Special Forces' - ], - 'hamas' => [ - 'flag' => 29, - 'name' => 'Hamas Forces' - ], - 'hezbollah' => [ - 'flag' => 30, - 'name' => 'Hezbollah Forces' - ], - 'iran' => [ - 'flag' => 31, - 'name' => 'Iran Forces' - ], - 'saudi_arabia' => [ - 'flag' => 32, - 'name' => 'Saudi Arabia Forces' - ], - 'syria' => [ - 'flag' => 33, - 'name' => 'Syrian Forces' - ], - 'egypt' => [ - 'flag' => 34, - 'name' => 'Egyptian Army' - ], - 'pakistan' => [ - 'flag' => 35, - 'name' => 'Pakistan Army' - ], - 'india' => [ - 'flag' => 36, - 'name' => 'Indian Armed forces' - ], -); \ No newline at end of file diff --git a/docs/full-bf2-stack-example/config/ASP/backendAwards.php b/docs/full-bf2-stack-example/config/ASP/backendAwards.php deleted file mode 100644 index 053add94..00000000 --- a/docs/full-bf2-stack-example/config/ASP/backendAwards.php +++ /dev/null @@ -1,235 +0,0 @@ - 180000); -}; - -/** - * Vanilla BF2 Medal Criteria - * - * @param array $row The AwardCriteria resulting row - * @param int $timesAwarded the number of times this award has been awarded - * - * @return bool true if the player is eligible to receive the award, false otherwise - */ -$vServiceMedalCriteria = function($row, $timesAwarded) -{ - $level = $timesAwarded + 1; - $best = (int)$row['brnd']; - $time = ((int)$row['time']) / 3600; // Convert to hours - $wins = (int)$row['wins']; - return ($best >= (100 * $level) && $time >= (100 * $level) && $wins >= (100 * $level)); -}; - -/** - * Special Forces Medal Criteria - * - * @param array $row The AwardCriteria resulting row - * @param int $timesAwarded the number of times this award has been awarded - * - * @return bool true if the player is eligible to receive the award, false otherwise - */ -$xServiceMedalCriteria = function($row, $timesAwarded) -{ - $level = $timesAwarded + 1; - $best = (int)$row['brnd']; - $time = ((int)$row['time']) / 3600; // Convert to hours - $wins = (int)$row['wins']; - return ($best >= (100 * $level) && $time >= (50 * $level) && $wins >= (50 * $level)); -}; - - -/** - * ------------------------------------------------------------------ - * Return Backend Awards and Criteria - * ------------------------------------------------------------------ - */ - -return array( - /** Service Ribbons */ - - // Middle Eastern Service Ribbon - new BackendAward(3191305, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (0,1,2,3,4,5,6) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 7); - } - ) - ]), - // Far East Service Ribbon - new BackendAward(3190605, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (100,101,102,103,105,601) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 6); - } - ) - ]), - // European Union Service Ribbon - new BackendAward(3270519, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (10,11,110) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - new AwardCriteria('player_map', 'sum(time) AS result', 'map_id IN (10,11,110)', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 180000); - } - ), - ]), - // North American Service Ribbon - new BackendAward(3271401, [ - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (200,201,202) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - new AwardCriteria('player_map', 'sum(time) AS result', 'map_id IN (200,201,202)', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 90000); - } - ), - ]), - - /** Xpack Service Ribbons */ - - // Navy Seal Special Service Ribbon - new BackendAward(3261919, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 3', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (300,301,304) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // SAS Special Service Ribbon - new BackendAward(3261901, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 4', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (302,303,307) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // SPETZNAS Service Ribbon - new BackendAward(3261819, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 5', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (305,306,307) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // MECSF Service Ribbon - new BackendAward(3261319, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 6', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (300,301,304) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 3); - } - ), - ]), - // Rebel Service Ribbon - new BackendAward(3261805, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 7', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (305,306) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 2); - } - ), - ]), - // Insurgent Service Ribbon - new BackendAward(3260914, [ - new AwardCriteria('player_army', 'time AS result', 'army_id = 8', $sRibbonCriteria), - new AwardCriteria('player_map', 'count(*) AS result', 'map_id IN (302,303) AND time >= 1', - function($row, $timesAwarded) - { - return ($timesAwarded == 0 && $row['result'] == 2); - } - ), - ]), - - /** Service Medals */ - - // Navy Cross - new BackendAward(2021403, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 0', $vServiceMedalCriteria), - ]), - // Golden Scimitar - new BackendAward(2020719, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 1', $vServiceMedalCriteria), - ]), - // Peoples Medallion - new BackendAward(2021613, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 2', $vServiceMedalCriteria), - ]), - // European Union Service Medal - new BackendAward(2270521, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 9', $xServiceMedalCriteria), - ]), - - /** Xpack Service Medals */ - - // Navy Seal Special Service Medal - new BackendAward(2261913, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 3', $xServiceMedalCriteria), - ]), - // SAS Special Service Medal - new BackendAward(2261919, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 4', $xServiceMedalCriteria), - ]), - // SPETZ Special Service Medal - new BackendAward(2261613, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 5', $xServiceMedalCriteria), - ]), - // MECSF Special Service Medal - new BackendAward(2261303, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 6', $xServiceMedalCriteria), - ]), - // Rebels Special Service Medal - new BackendAward(2261802, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 7', $xServiceMedalCriteria), - ]), - // Insurgent Special Service Medal - new BackendAward(2260914, [ - new AwardCriteria('player_army', 'time, wins, brnd', 'army_id = 8', $xServiceMedalCriteria), - ]), -); \ No newline at end of file diff --git a/docs/full-bf2-stack-example/config/ASP/config.php b/docs/full-bf2-stack-example/config/ASP/config.php deleted file mode 100644 index 4fa69033..00000000 --- a/docs/full-bf2-stack-example/config/ASP/config.php +++ /dev/null @@ -1,54 +0,0 @@ - array( - 'title' => 'Private', - 'points' => 0, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 0, - 'has_awards' => array() - ), - 1 => array( - 'title' => 'Private First Class', - 'points' => 150, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 0, - 'has_awards' => array() - ), - 2 => array( - 'title' => 'Lance Corporal', - 'points' => 500, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 1, - 'has_awards' => array() - ), - 3 => array( - 'title' => 'Corporal', - 'points' => 800, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 2, - 'has_awards' => array() - ), - 4 => array( - 'title' => 'Sergeant', - 'points' => 2500, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 3, - 'has_awards' => array() - ), - 5 => array( - 'title' => 'Staff Sergeant', - 'points' => 5000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 4, - 'has_awards' => array() - ), - 6 => array( - 'title' => 'Gunnery Sergeant', - 'points' => 8000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 5, - 'has_awards' => array() - ), - 7 => array( - 'title' => 'Master Sergeant', - 'points' => 20000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 6, - 'has_awards' => array() - ), - 8 => array( - 'title' => 'First Sergeant', - 'points' => 20000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 6, - 'has_awards' => array( - '1031105' => 1, // Engineer Combat Badge - '1031109' => 1, // Sniper Combat Badge - '1031113' => 1, // Medic Combat Badge - '1031115' => 1, // Spec Ops Combat Badge - '1031119' => 1, // Assault Combat Badge - '1031120' => 1, // Anti-tank Combat Badge - '1031121' => 1, // Support Combat Badge - '1031406' => 1, // Knife Combat Badge - '1031619' => 1 // Pistol Combat Badge - ) - ), - 9 => array( - 'title' => 'Master Gunnery Sergeant', - 'points' => 50000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(7, 8), - 'has_awards' => array() - ), - 10 => array( - 'title' => 'Sergeant Major', - 'points' => 50000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(7, 8), - 'has_awards' => array( - '1031923' => 1, // Ground Defense - '1220104' => 1, // Air Defense - '1220118' => 1, // Armor Badge - '1220122' => 1, // Aviator Badge - '1220803' => 1, // Helicopter Badge - '1222016' => 1 // Transport Badge - ) - ), - 11 => array( - 'title' => 'Sergeant Major of the Corp', - 'points' => 50000, - 'time' => 0, - 'skip' => true, - 'backend' => true, - 'has_rank' => 10, - 'has_awards' => array() - ), - 12 => array( - 'title' => '2nd Lieutenant', - 'points' => 60000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => array(9, 10, 11), - 'has_awards' => array() - ), - 13 => array( - 'title' => '1st Lieutenant', - 'points' => 75000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 12, - 'has_awards' => array() - ), - 14 => array( - 'title' => 'Captain', - 'points' => 90000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 13, - 'has_awards' => array() - ), - 15 => array( - 'title' => 'Major', - 'points' => 115000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 14, - 'has_awards' => array() - ), - 16 => array( - 'title' => 'Lieutenant Colonel', - 'points' => 125000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 15, - 'has_awards' => array() - ), - 17 => array( - 'title' => 'Colonel', - 'points' => 150000, - 'time' => 0, - 'skip' => false, - 'backend' => false, - 'has_rank' => 16, - 'has_awards' => array() - ), - 18 => array( - 'title' => 'Brigadier General', - 'points' => 180000, - 'time' => 3888000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 17, - 'has_awards' => array( - '1031105' => 2, // Engineer Combat Badge - '1031109' => 2, // Sniper Combat Badge - '1031113' => 2, // Medic Combat Badge - '1031115' => 2, // Spec Ops Combat Badge - '1031119' => 2, // Assault Combat Badge - '1031120' => 2, // Anti-tank Combat Badge - '1031121' => 2, // Support Combat Badge - '1031406' => 2, // Knife Combat Badge - '1031619' => 2 // Pistol Combat Badge - ) - ), - 19 => array( - 'title' => 'Major General', - 'points' => 180000, - 'time' => 4500000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 18, - 'has_awards' => array( - '1031923' => 2, // Ground Defense - '1220104' => 2, // Air Defense - '1220118' => 2, // Armor Badge - '1220122' => 2, // Aviator Badge - '1220803' => 2, // Helicopter Badge - '1222016' => 2 // Transport Badge - ) - ), - 20 => array( - 'title' => 'Lieutenant General', - 'points' => 200000, - 'time' => 5184000, // In seconds - 'skip' => false, - 'backend' => false, - 'has_rank' => 19, - 'has_awards' => array() - ), - 21 => array( - 'title' => 'General', - 'points' => 200000, - 'time' => 0, - 'skip' => false, - 'backend' => true, - 'has_rank' => 20, - 'has_awards' => array() - ), -); \ No newline at end of file diff --git a/docs/full-bf2-stack-example/config/bf2/mods/bf2/settings/serversettings-custom.con b/docs/full-bf2-stack-example/config/bf2/mods/bf2/settings/serversettings-custom.con index 386e277b..93bb7180 100644 --- a/docs/full-bf2-stack-example/config/bf2/mods/bf2/settings/serversettings-custom.con +++ b/docs/full-bf2-stack-example/config/bf2/mods/bf2/settings/serversettings-custom.con @@ -30,7 +30,7 @@ sv.tkNumPunishToKick 3 sv.tkPunishByDefault 0 sv.votingEnabled 1 sv.voteTime 90 -sv.minPlayersForVoting 2 +sv.minPlayersForVoting 1 sv.teamVoteOnly 1 sv.gameSpyPort 29900 sv.allowNATNegotiation 0 diff --git a/docs/full-bf2-stack-example/config/bf2/python/bf2/BF2StatisticsConfig-custom.py b/docs/full-bf2-stack-example/config/bf2/python/bf2/BF2StatisticsConfig-custom.py index 2532dfb5..04bb56bb 100644 --- a/docs/full-bf2-stack-example/config/bf2/python/bf2/BF2StatisticsConfig-custom.py +++ b/docs/full-bf2-stack-example/config/bf2/python/bf2/BF2StatisticsConfig-custom.py @@ -24,13 +24,13 @@ # which are NOT to be shared with anyone! # ------------------------------------------------------------------------------ stats_enable = 1 -stats_auth_id = 112960 # Required to post stats data at the end of round. -stats_auth_token = '2GS61JLR2WQq2n6N' # Required to post stats data at the end of round. +stats_auth_id = 112950 # Required to post stats data at the end of round. +stats_auth_token = 'GGB6Vj70Nmf98nm7' # Required to post stats data at the end of round. # ------------------------------------------------------------------------------ # ASP Stats Backend Web Server # ------------------------------------------------------------------------------ -http_backend_addr = 'asp-nginx' # Reach ASP over the `bf2-network` docker network. If your ASP is on another host, use its hostname or domain name here. +http_backend_addr = 'asp' # Reach ASP over the `bf2-network` docker network. If your ASP is on another host, use its hostname or domain name here. http_backend_port = 80 http_backend_asp = '/ASP/bf2statistics.php' @@ -41,7 +41,7 @@ # 0 = log only on error sending to backend # 1 = all snapshots # ------------------------------------------------------------------------------ -snapshot_logging = 0 +snapshot_logging = 1 snapshot_log_path_sent = 'python/bf2/logs/snapshots/sent' # Relative from the BF2 base folder snapshot_log_path_unsent = 'python/bf2/logs/snapshots/unsent' # Relative from the BF2 base folder diff --git a/docs/full-bf2-stack-example/config/coredns/hosts b/docs/full-bf2-stack-example/config/coredns/hosts index 76e7dcb4..ad8b84b2 100644 --- a/docs/full-bf2-stack-example/config/coredns/hosts +++ b/docs/full-bf2-stack-example/config/coredns/hosts @@ -1,12 +1,12 @@ # Replace these gamespy's DNS records with your machine's external IP addresses -192.168.1.100 eapusher.dice.se 192.168.1.100 battlefield2.available.gamespy.com 192.168.1.100 battlefield2.master.gamespy.com 192.168.1.100 battlefield2.ms14.gamespy.com -192.168.1.100 gamestats.gamespy.com 192.168.1.100 master.gamespy.com 192.168.1.100 motd.gamespy.com 192.168.1.100 gpsp.gamespy.com 192.168.1.100 gpcm.gamespy.com 192.168.1.100 gamespy.com 192.168.1.100 bf2web.gamespy.com +192.168.1.100 gamestats.gamespy.com +192.168.1.100 eapusher.dice.se diff --git a/docs/full-bf2-stack-example/docker-compose.build.yml b/docs/full-bf2-stack-example/docker-compose.build.yml new file mode 100644 index 00000000..a44bf955 --- /dev/null +++ b/docs/full-bf2-stack-example/docker-compose.build.yml @@ -0,0 +1,17 @@ +# This is a docker compose override file, for production builds with caching for CI environments +services: + asp: + build: + dockerfile: Dockerfile + context: ../../ + target: prod + cache_from: + - type=local,src=/tmp/.buildx-cache-asp + # cache_to: + # - type=local,dest=/tmp/.buildx-cache-asp,mode=max + + db: + volumes: + - ../../src/ASP/system/sql/schema.sql:/docker-entrypoint-initdb.d/01-schema.sql:ro # Seed the DB the first time + - ../../src/ASP/system/sql/data.sql:/docker-entrypoint-initdb.d/02-data.sql:ro # Seed the DB the first time + - ../../config/db/setup.sql:/docker-entrypoint-initdb.d/03-setup.sql:ro # Setup the DB the first time diff --git a/docs/full-bf2-stack-example/docker-compose.yml b/docs/full-bf2-stack-example/docker-compose.yml index 8065763c..92f8b548 100644 --- a/docs/full-bf2-stack-example/docker-compose.yml +++ b/docs/full-bf2-stack-example/docker-compose.yml @@ -1,12 +1,12 @@ version: '2.2' services: - # Battlefield 2 1.5 server with bf2stats 3.2.0 python scripts + # Battlefield 2 1.5 server with bf2stats 3 python scripts bf2: image: startersclan/docker-bf2:v1.5.3153.0-bf2stats-3.2.0 volumes: - ./config/bf2/mods/bf2/ai/aidefault-custom.ai:/server/bf2/mods/bf2/ai/aidefault.ai:ro # Customize bots - ./config/bf2/mods/bf2/settings/serversettings-custom.con:/server/bf2/mods/bf2/settings/serversettings.con:ro # Server config - - ./config/bf2/mods/bf2/settings/maplist-custom-coop.con:/server/bf2/mods/bf2/settings/maplist.con:ro # Maplist + - ./config/bf2/mods/bf2/settings/maplist-custom-coop.con:/server/bf2/mods/bf2/settings/maplist.con:ro # Maplist (coop) - ./config/bf2/python/bf2/BF2StatisticsConfig-custom.py:/server/bf2/python/bf2/BF2StatisticsConfig.py:ro # bf2stats python config ports: - 16567:16567/udp @@ -15,8 +15,7 @@ services: - gamespy-network - bf2-network depends_on: - - asp-nginx - - asp-php + - asp restart: unless-stopped tty: true stdin_open: true @@ -33,20 +32,17 @@ services: - 27900:27900/udp # Master server - 29910:29910/udp # CD key server networks: - # Spoof all gamespy DNS for the gameserver connected to this network + # Spoof all gamespy DNS for the BF2 server connected to this network gamespy-network: aliases: - - eapusher.dice.se - battlefield2.available.gamespy.com - battlefield2.master.gamespy.com - battlefield2.ms14.gamespy.com - - gamestats.gamespy.com - master.gamespy.com - motd.gamespy.com - gpsp.gamespy.com - gpcm.gamespy.com - gamespy.com - - bf2web.gamespy.com restart: unless-stopped # A DNS server to spoof gamespy's DNS records for BF2 clients or servers @@ -57,7 +53,7 @@ services: # ports: # - 192.168.1.100:53:53/udp # Test DNS records to ensure coredns responds with your machine's external IP address. For example: - # nslookup bf2web.gamespy.com 192.168.1.100 + # nslookup bf2web.gamespy.com 192.168.1.100 # Response should be: # Server: 192.168.1.100 # Address: 192.168.1.100#53 @@ -68,10 +64,12 @@ services: coredns: image: coredns/coredns:1.9.3 ports: - - 192.168.1.132:53:53/udp + - 53:53/udp volumes: - ./config/coredns/Corefile:/Corefile:ro - ./config/coredns/hosts:/hosts:ro + networks: + - bf2-network restart: unless-stopped entrypoint: - /coredns @@ -90,6 +88,7 @@ services: ports: - 80:80 - 443:443 + # - 8080:8080 # Uncomment to view traefik dashboard on port 8080 networks: - traefik-public-network - traefik-network @@ -97,26 +96,22 @@ services: command: - --global.checknewversion=false - --global.sendanonymoususage=false + # - --api.insecure # Uncomment to view traefik dashboard on port 8080 # - --log.level=DEBUG - --providers.docker=true - --providers.docker.exposedbydefault=false - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - - "--certificatesresolvers.myresolver.acme.dnschallenge=true" - - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh" - # - "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" - - "--certificatesresolvers.myresolver.acme.email=postmaster@example.com" - - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" + - --certificatesresolvers.myresolver.acme.dnschallenge=true + - --certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh + # - --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory + - --certificatesresolvers.myresolver.acme.email=postmaster@example.com + - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json - # The init container that sets up permissions for the asp and db volumes + # The init container that sets up permissions init-container: image: alpine:latest volumes: - - ./config/ASP/config.php:/src/ASP/system/config/config.php - - backups-volume:/src/ASP/system/database/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. - - logs-volume:/src/ASP/system/logs - - snapshots-volume:/src/ASP/system/snapshots - - bf2sclone-cache-volume:/src/bf2sclone/cache - db-volume:/var/lib/mysql entrypoint: - /bin/sh @@ -125,136 +120,61 @@ services: - | set -eu - echo "Granting ASP php write permissions" - chmod 666 /src/ASP/system/config/config.php - - chown -R 82:82 /src/ASP/system/backups - find /src/ASP/system/backups -type d -exec chmod 750 {} \; - find /src/ASP/system/backups -type f -exec chmod 640 {} \; - - chown -R 82:82 /src/ASP/system/cache - find /src/ASP/system/cache -type d -exec chmod 750 {} \; - find /src/ASP/system/cache -type f -exec chmod 640 {} \; - - chown -R 82:82 /src/ASP/system/logs - find /src/ASP/system/logs -type d -exec chmod 750 {} \; - find /src/ASP/system/logs -type f -exec chmod 640 {} \; - - mkdir -p /src/ASP/system/snapshots/failed - mkdir -p /src/ASP/system/snapshots/processed - mkdir -p /src/ASP/system/snapshots/unauthorized - mkdir -p /src/ASP/system/snapshots/unprocessed - chown -R 82:82 /src/ASP/system/snapshots - find /src/ASP/system/snapshots -type d -exec chmod 750 {} \; - find /src/ASP/system/snapshots -type f -exec chmod 640 {} \; - - echo "Granting bf2sclone php write permissions" - chown -R 82:82 /src/bf2sclone/cache - find /src/bf2sclone/cache -type d -exec chmod 750 {} \; - find /src/bf2sclone/cache -type f -exec chmod 640 {} \; - - echo "Granting db's 'mysql' user write permissions" + echo "Granting db write permissions" chown -R 999:999 /var/lib/mysql # The gamespy ASP. The dashboard is available at https://asp.example.com/ASP - asp-nginx: - image: startersclan/asp:3.2.0-nginx + asp: + image: startersclan/asp:3.2.0 labels: - "traefik.enable=true" - "traefik.docker.network=${COMPOSE_PROJECT_NAME?err}_traefik-network" # traefik v2 # http - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-gamespy-http.entrypoints=web" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-gamespy-http.rule=Host(`bf2web.gamespy.com`)" # Note: bf2web.gamespy.com doesn't need https + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-gamespy-http.rule=Host(`bf2web.gamespy.com`)" # Note: `bf2web.gamespy.com` doesn't need https. The BF2 client BFHQ, and the BF2 server python files, make a HTTP requests to `bf2web.gamespy.com` with `Host: bf2web.gamespy.com`. # http - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx-http.entrypoints=web" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx-http.rule=Host(`asp.example.com`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx-http.middlewares=${COMPOSE_PROJECT_NAME?err}-asp-nginx-http-myRedirectScheme" # Redirect http to https - - "traefik.http.middlewares.${COMPOSE_PROJECT_NAME?err}-asp-nginx-http-myRedirectScheme.redirectScheme.scheme=https" # Redirect http to https + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-http.entrypoints=web" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-http.rule=Host(`asp.example.com`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-http.middlewares=${COMPOSE_PROJECT_NAME?err}-asp-http-myRedirectScheme" # Redirect http to https + - "traefik.http.middlewares.${COMPOSE_PROJECT_NAME?err}-asp-http-myRedirectScheme.redirectScheme.scheme=https" # Redirect http to https # https - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx.entrypoints=websecure" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx.tls" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp-nginx.rule=Host(`asp.example.com`)" - - "traefik.http.services.${COMPOSE_PROJECT_NAME?err}-asp-nginx.loadbalancer.server.port=80" - # volumes: - # - ./config/ASP/nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Customize only if needed - networks: - - traefik-network - - bf2-network - depends_on: - - init-container - - asp-php - restart: unless-stopped - - asp-php: - image: startersclan/asp:3.2.0-php + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp.tls=" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-asp.rule=Host(`asp.example.com`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME?err}-asp.loadbalancer.server.port=80" + environment: + # See ./src/ASP/system/config/config.php for all supported env vars + - DB_HOST=db + - DB_PORT=3306 + - DB_NAME=bf2stats + - DB_USER=admin + - DB_PASS=admin + # ports: + # - 8081:80 volumes: - - ./config/ASP/config.php:/src/ASP/system/config/config.php # Main config file. Must be writeable or else ASP will throw an exception. Customize only if needed - # - ./config/ASP/armyAbbreviationMap.php:/src/ASP/system/config/armyAbbreviationMap.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/backendAwards.php:/src/ASP/system/config/backendAwards.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/ranks.php:/src/ASP/system/config/ranks.php:ro # Optional: Customize as needed if using a custom mod - # - ./config/ASP/php/conf.d/php.ini:/usr/local/etc/php/conf.d/php.ini:ro # Customize only if needed - # - ./config/ASP/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro # Customize only if needed - backups-volume:/src/ASP/system/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. - cache-volume:/src/ASP/system/cache + - config-volume:/src/ASP/system/config # For a stateful config file - logs-volume:/src/ASP/system/logs - snapshots-volume:/src/ASP/system/snapshots networks: - - bf2-network - depends_on: - - init-container - restart: unless-stopped - - # The bf2sclone for viewing BFHQ on the web. It is available at https://bf2sclone.example.com - bf2sclone-nginx: - image: startersclan/bf2stats:2.3.0-bf2sclone-nginx - labels: - - "traefik.enable=true" - - "traefik.docker.network=${COMPOSE_PROJECT_NAME?err}_traefik-network" - # traefik v2 - # http - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx-http.entrypoints=web" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx-http.rule=Host(`bf2sclone.example.com`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx-http.middlewares=${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx-http-myRedirectScheme" # Redirect http to https - - "traefik.http.middlewares.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx-http-myRedirectScheme.redirectScheme.scheme=https" # Redirect http to https - # https - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx.entrypoints=websecure" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx.tls" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx.rule=Host(`bf2sclone.example.com`)" - - "traefik.http.services.${COMPOSE_PROJECT_NAME?err}-bf2sclone-nginx.loadbalancer.server.port=80" - # volumes: - # - ./config/bf2sclone/nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Customize only if needed - networks: - - traefik-network - - bf2-network - depends_on: - - init-container - - bf2sclone-php - restart: unless-stopped - - bf2sclone-php: - image: startersclan/bf2stats:2.3.0-bf2sclone-php - volumes: - - ./config/bf2sclone/config.inc.php:/src/bf2sclone/config.inc.php:ro # Main config file. Customize as needed - # - ./config/bf2sclone/php/conf.d/php.ini:/usr/local/etc/php/conf.d/php.ini:ro # Customize only if needed - # - ./config/bf2sclone/php-fpm.d/www.conf:/usr/local/etc/php-fpm.d/www.conf:ro # Customize only if needed - - bf2sclone-cache-volume:/src/bf2sclone/cache - networks: - - bf2-network - depends_on: - - init-container + traefik-network: + bf2-network: + aliases: + - bf2web.gamespy.com # Spoof gamespy DNS for the BF2 server connected to this network restart: unless-stopped # The DB container db: image: mariadb:10.8 environment: - - MARIADB_ROOT_PASSWORD=ascent # Change this to a strong password + - MARIADB_ROOT_PASSWORD=ascent - MARIADB_USER=admin - - MARIADB_PASSWORD=admin # Change this to a strong password + - MARIADB_PASSWORD=admin - MARIADB_DATABASE=bf2stats volumes: - - ./config/db/my.cnf:/etc/my.cnf:ro # Customize as needed + - ./config/db/my.cnf:/etc/my.cnf:ro # Config file. Customize as needed - db-volume:/var/lib/mysql networks: - bf2-network @@ -262,7 +182,7 @@ services: - init-container restart: unless-stopped - # The phpmyadmin interface for administrating the DB. It is available at phpmyadmin.example.com + # The phpmyadmin interface for administrating the DB. It is available at https://phpmyadmin.example.com phpmyadmin: image: phpmyadmin:5.2 labels: @@ -276,12 +196,14 @@ services: - "traefik.http.middlewares.${COMPOSE_PROJECT_NAME?err}-phpmyadmin-http-myRedirectScheme.redirectScheme.scheme=https" # Redirect http to https # https - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-phpmyadmin.entrypoints=websecure" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-phpmyadmin.tls" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-phpmyadmin.tls=" - "traefik.http.routers.${COMPOSE_PROJECT_NAME?err}-phpmyadmin.rule=Host(`phpmyadmin.example.com`)" - "traefik.http.services.${COMPOSE_PROJECT_NAME?err}-phpmyadmin.loadbalancer.server.port=80" environment: - PMA_ABSOLUTE_URI=https://phpmyadmin.example.com # Enable this if behind a reverse proxy - PMA_HOST=db + # ports: + # - 8083:80 networks: - traefik-network - bf2-network @@ -299,6 +221,7 @@ volumes: traefik-acme-volume: backups-volume: cache-volume: + config-volume: logs-volume: snapshots-volume: bf2sclone-cache-volume: diff --git a/docs/upgrading-docker-images-to-3.3.md b/docs/upgrading-docker-images-to-3.3.md new file mode 100644 index 00000000..7b40ce5b --- /dev/null +++ b/docs/upgrading-docker-images-to-3.3.md @@ -0,0 +1,97 @@ +# Upgrading docker images to v3.3.x + +In <= v3.2.x, `asp` had separate `nginx` and `php` images. + +Since v3.3.0, `asp` image contains both `nginx` and `php`, with environment variable support, and entrypoint that sets the correct permissions. + +Benefits: + +- Easier to deploy / upgrade. No need to separate `nginx` and `php` containers +- Environment variable configuration means no more need to mount config into `asp` container +- Entrypoint script sets permissions on volumes. `init-container` should only need to set permissions for `db` volume + +## Upgrade steps + +These steps are demonstrated using Docker Compose. + +1. Merge the networks and volumes of `asp-nginx` and `asp-php` into a single `asp` container, switch to a volume and env vars for `asp` configuration, and remove `depends_on`. + +For instance, from this: + +```yaml + asp-nginx: + image: startersclan/bf2stats:3.2.0-nginx + volumes: + - ./src:/src:ro + - ./config/ASP/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + networks: + traefik-network: + bf2-network: + aliases: + - bf2web.gamespy.com # Important! Spoof Gamespy DNS for the BF2 server to reach our ASP server IP over this network + depends_on: + - init-container + - asp-php + + asp-php: + image: startersclan/bf2stats:3.2.0-php + volumes: + - ./config/ASP/config.php:/src/ASP/system/config/config.php # Main config file. Must be writeable or else ASP will throw an exception. Customize only if needed + - backups-volume:/src/ASP/system/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. + - cache-volume:/src/ASP/system/cache + - logs-volume:/src/ASP/system/logs + - snapshots-volume:/src/ASP/system/snapshots + networks: + - traefik-network + - bf2-network + depends_on: + - init-container +``` + +To this: + +```yaml + asp: + image: startersclan/bf2stats:3.3.0 + environment: + # See ./src/ASP/system/config/config.php for all supported env vars + - DB_HOST=db + - DB_PORT=3306 + - DB_NAME=bf2stats + - DB_USER=root + - DB_PASS=ascent + volumes: + - backups-volume:/src/ASP/system/backups # This volume is effectively unused since ASP doesn't allow DB backups for a remote DB, but mount it anyway to avoid errors. + - cache-volume:/src/ASP/system/cache + - config-volume:/src/ASP/system/config # For a stateful config file + - logs-volume:/src/ASP/system/logs + - snapshots-volume:/src/ASP/system/snapshots + networks: + traefik-network: + bf2-network: + aliases: + - bf2web.gamespy.com # Important! Spoof Gamespy DNS for the BF2 server to reach our ASP server IP over this network + +volumes: + config-volume: +``` + +2. If you have `init-container`, now it only needs to set permission for the `db` volume: + +```yaml + init-container: + image: alpine:latest + volumes: + - db-volume:/var/lib/mysql + entrypoint: + - /bin/sh + command: + - -c + - | + set -eu + + echo "Granting db write permissions" + chown -R 999:999 /var/lib/mysql +``` + +Done. Enjoy the simpler setup 😀 diff --git a/test/.env b/test/.env new file mode 100644 index 00000000..4cc5b70a --- /dev/null +++ b/test/.env @@ -0,0 +1 @@ +COMPOSE_PROJECT_NAME=asp diff --git a/test/docker-compose.yml b/test/docker-compose.yml new file mode 100644 index 00000000..7bbce4e3 --- /dev/null +++ b/test/docker-compose.yml @@ -0,0 +1,35 @@ +version: '2.2' +services: + test-container-networking: + image: alpine:latest + volumes: + - ./:/test:ro + networks: + - gamespy-network + - bf2-network + stop_signal: SIGKILL + working_dir: /test + entrypoint: + - /bin/sh + command: + - -c + - | + sleep infinity + + test-host-networking: + image: alpine:latest + volumes: + - ./:/test:ro + network_mode: host + stop_signal: SIGKILL + working_dir: /test + entrypoint: + - /bin/sh + command: + - -c + - | + sleep infinity + +networks: + bf2-network: + gamespy-network: diff --git a/test/snapshots/dalian_plant_20231107_2211.json b/test/snapshots/dalian_plant_20231107_2211.json new file mode 100644 index 00000000..ed991a2d --- /dev/null +++ b/test/snapshots/dalian_plant_20231107_2211.json @@ -0,0 +1 @@ +{"authId":"112950","authToken":"GGB6Vj70Nmf98nm7","serverName":"Default Server Name","gamePort":"16567","queryPort":"29900","mapId":"101","mapName":"dalian_plant","mapStart":"1699395042.42","mapEnd":"1699395087.35","winner":"0","gameMode":"2","mod":"bf2","version":"3.1","pc":"63","ra1":"2","rs1":"100","ra2":"0","rs2":"100","rst2":"100","players":[]} \ No newline at end of file diff --git a/test/snapshots/daqing_oilfields_20231107_2212.json b/test/snapshots/daqing_oilfields_20231107_2212.json new file mode 100644 index 00000000..4386679d --- /dev/null +++ b/test/snapshots/daqing_oilfields_20231107_2212.json @@ -0,0 +1 @@ +{"authId":"112950","authToken":"GGB6Vj70Nmf98nm7","serverName":"Default Server Name","gamePort":"16567","queryPort":"29900","mapId":"100","mapName":"daqing_oilfields","mapStart":"1699395118.92","mapEnd":"1699395139.05","winner":"0","gameMode":"2","mod":"bf2","version":"3.1","pc":"126","ra1":"2","rs1":"100","ra2":"0","rs2":"100","rst2":"100","players":[]} \ No newline at end of file diff --git a/test/test-endpoints.sh b/test/test-endpoints.sh new file mode 100755 index 00000000..ecb2009e --- /dev/null +++ b/test/test-endpoints.sh @@ -0,0 +1,47 @@ +#!/bin/sh +set -eu + +echo +echo "[test-endpoints]" +ENDPOINTS=" +asp.example.com/ 200 +asp.example.com/.htaccess 401 +asp.example.com/ASP/ 200 +asp.example.com/ASP/aspx 401 +asp.example.com/ASP/bf2statistics.php 403 +asp.example.com/ASP/frontend 401 +asp.example.com/ASP/frontend/css/bootstrap.min.css 200 +asp.example.com/ASP/frontend/images/maps/foo.png 200 +asp.example.com/ASP/frontend/images/ranks/foo.png 200 +asp.example.com/ASP/frontend/images/armies/foo.png 200 +asp.example.com/ASP/frontend/css/bootstrap.min.css 200 +asp.example.com/ASP/createplayer.aspx 200 +asp.example.com/ASP/getawardsinfo.aspx 200 +asp.example.com/ASP/getbackendinfo.aspx 200 +asp.example.com/ASP/getleaderboard.aspx 200 +asp.example.com/ASP/getmapinfo.aspx 200 +asp.example.com/ASP/getplayerid.aspx 200 +asp.example.com/ASP/getplayerinfo.aspx 200 +asp.example.com/ASP/getrankinfo.aspx 200 +asp.example.com/ASP/getunlocksinfo.aspx 200 +asp.example.com/ASP/ranknotification.aspx 200 +asp.example.com/ASP/searchforplayers.aspx 200 +asp.example.com/ASP/selectunlock.aspx 200 +asp.example.com/ASP/verifyplayer.aspx 200 +asp.example.com/ASP/index.php 200 +asp.example.com/ASP/ranknotification.aspx 200 +asp.example.com/ASP/searchforplayers.aspx 200 +asp.example.com/ASP/selectunlock.aspx 200 +asp.example.com/ASP/getplayerinfo.aspx 200 +asp.example.com/ASP/system 401 +" +command -v curl || apk add --no-cache curl +echo "$ENDPOINTS" | awk NF | while read -r i j; do + d=$( echo "$i" | cut -d '/' -f1 ) + if curl --head -skL http://$i --resolve $d:80:127.0.0.1 --resolve $d:443:127.0.0.1 2>&1 | grep -E "^HTTP/(1.1|2) $j " > /dev/null; then + echo "PASS: $i" + else + echo "FAIL: $i" + exit 1 + fi +done diff --git a/test/test-external-dns.sh b/test/test-external-dns.sh new file mode 100755 index 00000000..55c946d4 --- /dev/null +++ b/test/test-external-dns.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -eu + +echo +echo "[test-external-dns]" +DNS=" +192.168.1.100 battlefield2.available.gamespy.com +192.168.1.100 battlefield2.master.gamespy.com +192.168.1.100 battlefield2.ms14.gamespy.com +192.168.1.100 master.gamespy.com +192.168.1.100 motd.gamespy.com +192.168.1.100 gpsp.gamespy.com +192.168.1.100 gpcm.gamespy.com +192.168.1.100 gamespy.com +192.168.1.100 bf2web.gamespy.com +192.168.1.100 gamestats.gamespy.com +192.168.1.100 eapusher.dice.se +" +echo "$DNS" | awk NF | while read -r ip h; do + if nslookup $h coredns | grep -E "^Address: $ip" > /dev/null; then + echo "PASS: $h" + else + echo "FAIL: $h" + exit 1 + fi +done diff --git a/test/test-internal-dns.sh b/test/test-internal-dns.sh new file mode 100755 index 00000000..76916d13 --- /dev/null +++ b/test/test-internal-dns.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -eu + +echo +echo "[test-internal-dns]" +DNS=" +battlefield2.available.gamespy.com +battlefield2.master.gamespy.com +battlefield2.ms14.gamespy.com +master.gamespy.com +motd.gamespy.com +gpsp.gamespy.com +gpcm.gamespy.com +gamespy.com +bf2web.gamespy.com +" +echo "$DNS" | awk NF | grep -v '127.0.0' | while read -r h; do + if nslookup $h | grep -v '127.0.0' | grep -E '^Address: [0-9\.]+$'; then + echo "PASS: $h" + else + echo "FAIL: $h" + exit 1 + fi +done diff --git a/test/test-ready.sh b/test/test-ready.sh new file mode 100755 index 00000000..fd306cd9 --- /dev/null +++ b/test/test-ready.sh @@ -0,0 +1,18 @@ +#!/bin/sh +set -eu + +echo +echo "[test-ready] Waiting for stack to be ready" +s=0 +while true; do + nc -vz -w 1 asp 80 \ + && nc -vz -w 1 asp 9000 \ + && nc -vz -w 1 db 3306 \ + && break || true + s=$(( $s + 1 )) + if [ "$s" -eq 100 ]; then + exit 1 + fi + echo "Retrying in 3 seconds" + sleep 3 +done diff --git a/test/test-routes.sh b/test/test-routes.sh new file mode 100755 index 00000000..30df365c --- /dev/null +++ b/test/test-routes.sh @@ -0,0 +1,47 @@ +#!/bin/sh +set -eu + +echo +echo "[test-routes]" +URLS=" +http://asp/ 200 +http://asp/.htaccess 401 +http://asp/ASP/ 200 +http://asp/ASP/aspx 401 +http://asp/ASP/bf2statistics.php 403 +http://asp/ASP/frontend 401 +http://asp/ASP/frontend/css/bootstrap.min.css 200 +http://asp/ASP/frontend/images/maps/foo.png 200 +http://asp/ASP/frontend/images/ranks/foo.png 200 +http://asp/ASP/frontend/images/armies/foo.png 200 +http://asp/ASP/frontend/css/bootstrap.min.css 200 +http://asp/ASP/createplayer.aspx 200 +http://asp/ASP/getawardsinfo.aspx 200 +http://asp/ASP/getbackendinfo.aspx 200 +http://asp/ASP/getleaderboard.aspx 200 +http://asp/ASP/getmapinfo.aspx 200 +http://asp/ASP/getplayerid.aspx 200 +http://asp/ASP/getplayerinfo.aspx 200 +http://asp/ASP/getrankinfo.aspx 200 +http://asp/ASP/getunlocksinfo.aspx 200 +http://asp/ASP/ranknotification.aspx 200 +http://asp/ASP/searchforplayers.aspx 200 +http://asp/ASP/selectunlock.aspx 200 +http://asp/ASP/verifyplayer.aspx 200 +http://asp/ASP/index.php 200 +http://asp/ASP/ranknotification.aspx 200 +http://asp/ASP/searchforplayers.aspx 200 +http://asp/ASP/selectunlock.aspx 200 +http://asp/ASP/getplayerinfo.aspx 200 +http://asp/ASP/system 401 + +http://phpmyadmin/ 200 +" +echo "$URLS" | awk NF | while read -r i j; do + if wget -q -SO- "$i" 2>&1 | grep "HTTP/1.1 $j " > /dev/null; then + echo "PASS: $i" + else + echo "FAIL: $i" + exit 1 + fi +done diff --git a/test/test-snapshots.sh b/test/test-snapshots.sh new file mode 100755 index 00000000..ca8d1e86 --- /dev/null +++ b/test/test-snapshots.sh @@ -0,0 +1,19 @@ +#!/bin/sh +set -eu + +SCRIPT_DIR=$( cd "$( dirname "$0" )" && pwd ) +cd "$SCRIPT_DIR" + +echo +echo "[test-snapshots]" +command -v curl || apk add --no-cache curl +for i in ./snapshots/*; do + RES=$( set -x; curl -s -A GameSpyHTTP/1.0 -H 'Content-Type: application/json' --data "@$i" http://asp/ASP/bf2statistics.php ) + echo "$RES"; echo + if [ "$( echo "$RES" | head -c1 )" = 'O' ]; then + echo "PASS: $i" + else + echo "FAIL: $i" + exit 1 + fi +done diff --git a/test/test.sh b/test/test.sh new file mode 100755 index 00000000..79cee5d3 --- /dev/null +++ b/test/test.sh @@ -0,0 +1,81 @@ +#!/bin/sh +set -eu + +TEST=${1:-} # Test environment +UP=${2:-} # Whether to docker compose up and down the test stack +CACHE=${3:-} # Whether to override with docker-compose.build.yml + +# Validation and normalization +if ! echo "$TEST" | grep -E '^(dev|prod|dns|snapshots)$' > /dev/null; then + echo "Specify TEST as the first argument. E.g. 'dev', 'prod', 'dns'" + exit 1 +fi +if [ -n "$CACHE" ]; then + CACHE='-f docker-compose.build.yml' +fi + +SCRIPT_DIR=$( cd "$( dirname "$0" )" && pwd ) +ERR= +setup_test() { + cd "$SCRIPT_DIR" + docker compose up -d + if [ -n "$UP" ]; then + setup + fi + run +} +cleanup_test() { + ERR=$? + if [ -n "$UP" ]; then + cleanup + fi + docker compose stop + if [ -z "$ERR" ] || [ "$ERR" = 0 ]; then + echo "All tests succeeded" + else + echo "Some tests failed" + echo "Exit code: $ERR" + exit "$ERR" + fi +} +trap cleanup_test INT TERM EXIT + +echo "Testing..." +if [ "$TEST" = 'dev' ]; then + setup() { + (cd .. && docker compose -f docker-compose.yml $CACHE up --build -d) + } + run() { + docker exec $( docker compose ps -q test-container-networking ) ./test-ready.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-routes.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-snapshots.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-internal-dns.sh + } + cleanup() { + (cd .. && docker compose stop) + } +fi +if [ "$TEST" = 'prod' ]; then + setup() { + (cd ../docs/full-bf2-stack-example && docker compose -f docker-compose.yml $CACHE up --build -d) + } + run() { + docker exec $( docker compose ps -q test-container-networking ) ./test-ready.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-routes.sh + docker exec $( docker compose ps -q test-host-networking ) ./test-endpoints.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-snapshots.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-internal-dns.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-external-dns.sh + } + cleanup() { + (cd ../docs/full-bf2-stack-example && docker compose -f docker-compose.yml $CACHE stop) + } +fi +if [ "$TEST" = 'dns' ]; then + run() { + docker exec $( docker compose ps -q test-container-networking ) ./test-ready.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-internal-dns.sh + docker exec $( docker compose ps -q test-container-networking ) ./test-external-dns.sh + } +fi +setup_test