Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2b13c0c8f | |||
| bfb15c7299 | |||
| 627f017cdc | |||
| 10113fd057 | |||
| b57e5827df | |||
| b98bdd2a64 | |||
| 285b73efbd | |||
| 06f2adfeb6 | |||
| 4651bb796f | |||
| 1df29cf3a6 | |||
| ff492e1dc8 | |||
| 9978b7ea78 | |||
| 9a9784673f | |||
| 83dd2d4335 | |||
| 4377e05d5c | |||
| 3188241737 | |||
| 64b5d5567f | |||
| 65c3fabd91 | |||
| b0ddb274d2 |
@@ -470,3 +470,330 @@
|
|||||||
### Reverts
|
### Reverts
|
||||||
|
|
||||||
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-22)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** make HybridBot veto actionable and use it for expert ([1df29cf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1df29cf3a6e21af3f396b2b7a6da67d978f941ae))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** play only own tournament games with correct color ([4651bb7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4651bb796f07a21bd013d9521b2dfe2e1078cebb))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** make HybridBot veto actionable and use it for expert ([1df29cf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1df29cf3a6e21af3f396b2b7a6da67d978f941ae))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** play only own tournament games with correct color ([4651bb7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4651bb796f07a21bd013d9521b2dfe2e1078cebb))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** resume tournaments already joined after restart ([285b73e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/285b73efbd6dd98cec410ade9eead9881d693a8f))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** make HybridBot veto actionable and use it for expert ([1df29cf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1df29cf3a6e21af3f396b2b7a6da67d978f941ae))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** discover tournament games by polling, not just the stream ([10113fd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/10113fd0579b614d15870798d933bc9c495d2049))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** play only own tournament games with correct color ([4651bb7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4651bb796f07a21bd013d9521b2dfe2e1078cebb))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** resume tournaments already joined after restart ([285b73e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/285b73efbd6dd98cec410ade9eead9881d693a8f))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add initialization metrics for various services ([d438e97](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d438e97f32bdde0bfc63c1b4a8cc810cdd093166))
|
||||||
|
* add OpenTelemetry trace configuration with parentbased sampler ([3904d5a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3904d5ad8ad4930ddee65287a7bfab785a6148f5))
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* **config:** update application.yml for PostgreSQL and remove staging/production configurations ([2404e61](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/2404e6164c3b50ffccbea5238d636060d6abe4d6))
|
||||||
|
* **config:** update application.yml for staging and production environments ([6113432](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6113432a14c476a3a0dfc0d449e17d023697f2ba))
|
||||||
|
* configure logging and add OpenTelemetry support ([#49](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/49)) ([d57c488](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/d57c4886612d1d92da0e1b79209fc83e6ef537a1))
|
||||||
|
* **docker:** add .dockerignore and .gitignore files for build exclusions ([c987d8e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c987d8e258c0e6c4cfbdaa8381c64c410d7a2b83))
|
||||||
|
* **docker:** add Dockerfiles for building Quarkus application in native and JVM modes ([3f2d2bb](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/3f2d2bb4c97fa8cddba66e1da4427c54236dfeed))
|
||||||
|
* **docker:** add Dockerfiles for Quarkus application in JVM and native modes ([34b9933](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/34b993304670cf2aa62cd2f6460cee7b9864b08e))
|
||||||
|
* **events:** migrate game-creation and bot flows to Redis Streams NCS-89 ([#62](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/62)) ([a24924c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a24924c23057db3d700a75dbc4333557789cd991))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#46](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/46)) ([649566e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/649566eb3fcf38f91c8896a739f74ea318af312d))
|
||||||
|
* NCS-78 Add Traceability to the Applications ([#47](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/47)) ([87dfc6c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/87dfc6c2bcce7f7d58fc641bd8d468a2e584c108))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **official-bots:** consume GameOver stream for bot cleanup ([#67](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/67)) ([db9d153](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/db9d1533912f4b41c4d1ca80ccffdde5d23d6ff6))
|
||||||
|
* **official-bots:** make HybridBot veto actionable and use it for expert ([1df29cf](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1df29cf3a6e21af3f396b2b7a6da67d978f941ae))
|
||||||
|
* **official-bots:** park expert bot on tournament server at startup ([#75](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/75)) ([30295a4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/30295a4bb95855ee8261c92278bb9ebc80ee12ee))
|
||||||
|
* **official-bots:** resolve tournament bot token from Redis and account service ([386ddc5](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/386ddc5c19f8f893b16c6422aa5393b54c872e45))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
* true-microservices ([#40](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/40)) ([5909242](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/590924254e8a2754de661a57a03e43f89ceb6299))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* enable official bots to connect to external tournament server ([#71](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/71)) ([688d30e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/688d30e2b10026923372be5fca3c63eaaee2de2a))
|
||||||
|
* **official-bots:** configure JWT verification ([#72](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/72)) ([98c64fc](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/98c64fc0d56dc542beb31c75f4b9056d91de03cd))
|
||||||
|
* **official-bots:** correct parkOn path from /api/bots to /api/account/bots ([1be9949](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1be9949c0b5c6a1db535696620d77735050d6c93))
|
||||||
|
* **official-bots:** discover tournament games by polling, not just the stream ([10113fd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/10113fd0579b614d15870798d933bc9c495d2049))
|
||||||
|
* **official-bots:** make botToken optional, fall back to env, fix 502 status ([f43d193](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f43d1930d80670d810c57b54eaa3789854fa082c))
|
||||||
|
* **official-bots:** NCS-70-auto-register official bots with account service ([#59](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/59)) ([7117a93](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/7117a93376272094d0b1a6abf2121254ce396684))
|
||||||
|
* **official-bots:** park on external tournament servers using correct endpoint and token ([3188241](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/31882417377468b41bbe3ff94506aa4928024450))
|
||||||
|
* **official-bots:** play games by polling state instead of NDJSON stream ([bfb15c7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/bfb15c7299bd471d5e064a577ed10af98e2ea90a))
|
||||||
|
* **official-bots:** play only own tournament games with correct color ([4651bb7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/4651bb796f07a21bd013d9521b2dfe2e1078cebb))
|
||||||
|
* **official-bots:** prioritize Redis token over stale env var in joinTournament ([83dd2d4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/83dd2d4335ca48eb3e5aa234a75367574276ba63))
|
||||||
|
* **official-bots:** register with tournament server directly to get correct token ([64b5d55](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/64b5d5567f110c2fe152558c7de275a1e0b30e21))
|
||||||
|
* **official-bots:** resume tournaments already joined after restart ([285b73e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/285b73efbd6dd98cec410ade9eead9881d693a8f))
|
||||||
|
* **official-bots:** sync bots before token fetch on first startup after DB wipe ([b0ddb27](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b0ddb274d23bca8b1b3f691ce0d643f33e0b54cd))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "refactor: update metrics paths formatting in application.yml for clarity" ([3870566](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/38705663498d5f47c40dafe2f26198589ede8656))
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package de.nowchess.bot
|
package de.nowchess.bot
|
||||||
|
|
||||||
import de.nowchess.bot.bots.ClassicalBot
|
import de.nowchess.bot.bots.{ClassicalBot, HybridBot}
|
||||||
import jakarta.enterprise.context.ApplicationScoped
|
import jakarta.enterprise.context.ApplicationScoped
|
||||||
|
import org.jboss.logging.Logger
|
||||||
|
|
||||||
object BotController:
|
object BotController:
|
||||||
|
private val log = Logger.getLogger(classOf[BotController])
|
||||||
|
|
||||||
private val bots: Map[String, Bot] = Map(
|
private val bots: Map[String, Bot] = Map(
|
||||||
"easy" -> ClassicalBot(BotDifficulty.Easy),
|
"easy" -> ClassicalBot(BotDifficulty.Easy),
|
||||||
"medium" -> ClassicalBot(BotDifficulty.Medium),
|
"medium" -> ClassicalBot(BotDifficulty.Medium),
|
||||||
"hard" -> ClassicalBot(BotDifficulty.Hard),
|
"hard" -> ClassicalBot(BotDifficulty.Hard),
|
||||||
"expert" -> ClassicalBot(BotDifficulty.Expert),
|
"expert" -> HybridBot(BotDifficulty.Expert, vetoReporter = log.debug(_)),
|
||||||
)
|
)
|
||||||
|
|
||||||
def getBot(name: String): Option[Bot] = bots.get(name.toLowerCase)
|
def getBot(name: String): Option[Bot] = bots.get(name.toLowerCase)
|
||||||
|
|||||||
@@ -24,16 +24,24 @@ object HybridBot:
|
|||||||
val search = AlphaBetaSearch(rules, TranspositionTable(), classicalEvaluation)
|
val search = AlphaBetaSearch(rules, TranspositionTable(), classicalEvaluation)
|
||||||
context =>
|
context =>
|
||||||
val blockedMoves = BotMoveRepetition.blockedMoves(context)
|
val blockedMoves = BotMoveRepetition.blockedMoves(context)
|
||||||
|
|
||||||
|
def nnueScore(move: Move): Int = nnueEvaluation.evaluate(rules.applyMove(context)(move))
|
||||||
|
def classicalScore(move: Move): Int = classicalEvaluation.evaluate(rules.applyMove(context)(move))
|
||||||
|
|
||||||
|
def refine(move: Move): Move =
|
||||||
|
val moveNnue = nnueScore(move)
|
||||||
|
if (classicalScore(move) - moveNnue).abs <= Config.VETO_THRESHOLD then move
|
||||||
|
else
|
||||||
|
search
|
||||||
|
.bestMoveWithTime(context, Config.TIME_LIMIT_MS, blockedMoves + move)
|
||||||
|
.filterNot(blockedMoves.contains)
|
||||||
|
.filter(alt => nnueScore(alt) < moveNnue)
|
||||||
|
.map { alt =>
|
||||||
|
vetoReporter(f"[Veto] ${move.from}->${move.to} replaced by ${alt.from}->${alt.to} — NNUE prefers it")
|
||||||
|
alt
|
||||||
|
}
|
||||||
|
.getOrElse(move)
|
||||||
|
|
||||||
book.flatMap(_.probe(context)).filterNot(blockedMoves.contains).orElse {
|
book.flatMap(_.probe(context)).filterNot(blockedMoves.contains).orElse {
|
||||||
search.bestMoveWithTime(context, Config.TIME_LIMIT_MS, blockedMoves).map { move =>
|
search.bestMoveWithTime(context, Config.TIME_LIMIT_MS, blockedMoves).map(refine)
|
||||||
val next = rules.applyMove(context)(move)
|
|
||||||
val staticNnue = nnueEvaluation.evaluate(next)
|
|
||||||
val classical = classicalEvaluation.evaluate(next)
|
|
||||||
val diff = (classical - staticNnue).abs
|
|
||||||
if diff > Config.VETO_THRESHOLD then
|
|
||||||
vetoReporter(
|
|
||||||
f"[Veto] ${move.from}->${move.to}: nnue=$staticNnue classical=$classical diff=$diff — flagged but trusted (deep search)",
|
|
||||||
)
|
|
||||||
move
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+225
-78
@@ -38,14 +38,25 @@ class TournamentBotGamePlayer:
|
|||||||
private val client: Client = ClientBuilder.newClient()
|
private val client: Client = ClientBuilder.newClient()
|
||||||
private val workers: ExecutorService = Executors.newCachedThreadPool()
|
private val workers: ExecutorService = Executors.newCachedThreadPool()
|
||||||
private val activeGames = ConcurrentHashMap.newKeySet[String]()
|
private val activeGames = ConcurrentHashMap.newKeySet[String]()
|
||||||
|
private val joinedTournaments = ConcurrentHashMap.newKeySet[String]()
|
||||||
|
|
||||||
|
private val hardestDifficulty = "expert"
|
||||||
|
private val autoJoinIntervalMs = 15000L
|
||||||
|
|
||||||
|
private val gameTerminalStatuses =
|
||||||
|
Set("checkmate", "stalemate", "draw", "resigned", "timeout", "aborted", "finished")
|
||||||
|
|
||||||
// scalafix:off DisableSyntax.var
|
// scalafix:off DisableSyntax.var
|
||||||
@volatile private var running = true
|
@volatile private var running = true
|
||||||
|
@volatile private var autoJoinToken: Option[String] = None
|
||||||
// scalafix:on DisableSyntax.var
|
// scalafix:on DisableSyntax.var
|
||||||
|
|
||||||
val tournamentServiceUrl: String =
|
val tournamentServiceUrl: String =
|
||||||
System.getenv().asScala.getOrElse("TOURNAMENT_SERVICE_URL", "http://localhost:8086")
|
System.getenv().asScala.getOrElse("TOURNAMENT_SERVICE_URL", "http://localhost:8086")
|
||||||
|
|
||||||
|
val autoJoinServerUrl: String =
|
||||||
|
System.getenv().asScala.getOrElse("TOURNAMENT_AUTO_JOIN_URL", "http://141.37.123.132:8086")
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
def initialize(): Unit =
|
def initialize(): Unit =
|
||||||
val env = System.getenv().asScala.toMap
|
val env = System.getenv().asScala.toMap
|
||||||
@@ -58,16 +69,106 @@ class TournamentBotGamePlayer:
|
|||||||
case Some(cfg) =>
|
case Some(cfg) =>
|
||||||
log.infof("Tournament bot enabled — server=%s tournament=%s bot=%s", cfg.serverUrl, cfg.tournamentId, cfg.botId)
|
log.infof("Tournament bot enabled — server=%s tournament=%s bot=%s", cfg.serverUrl, cfg.tournamentId, cfg.botId)
|
||||||
startAsync(cfg)
|
startAsync(cfg)
|
||||||
|
startAutoJoin()
|
||||||
|
|
||||||
|
private def startAutoJoin(): Unit =
|
||||||
|
val thread = new Thread(() => autoJoinLoop(), "TournamentBot-auto-join")
|
||||||
|
thread.setDaemon(true)
|
||||||
|
thread.start()
|
||||||
|
log.infof("Auto-join enabled — server=%s difficulty=%s", autoJoinServerUrl, hardestDifficulty)
|
||||||
|
|
||||||
|
private def autoJoinLoop(): Unit =
|
||||||
|
while running do
|
||||||
|
Try(autoJoinScan()).failed.foreach(ex => log.warnf(ex, "Auto-join scan failed"))
|
||||||
|
sleep(autoJoinIntervalMs)
|
||||||
|
|
||||||
|
private def autoJoinScan(): Unit =
|
||||||
|
resolveAutoJoinToken().foreach { token =>
|
||||||
|
TournamentBotConfig.jwtSubject(token).foreach { botId =>
|
||||||
|
val open = openTournaments()
|
||||||
|
log.infof("Auto-join scan — server=%s open tournaments=%d bot=%s", autoJoinServerUrl, open.size, botId)
|
||||||
|
open.foreach { tournamentId =>
|
||||||
|
if joinedTournaments.add(tournamentId) then
|
||||||
|
val cfg = TournamentBotConfig(autoJoinServerUrl, tournamentId, token, botId, hardestDifficulty)
|
||||||
|
if !joinedOrParticipating(cfg) then joinedTournaments.remove(tournamentId)
|
||||||
|
}
|
||||||
|
playPendingGames(token, botId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tournament-server does not reliably replay gameStart to late subscribers, so we cannot
|
||||||
|
// depend on the event stream to discover games. Poll each joined tournament for our active game.
|
||||||
|
private def playPendingGames(token: String, botId: String): Unit =
|
||||||
|
joinedTournaments.forEach { tournamentId =>
|
||||||
|
val cfg = TournamentBotConfig(autoJoinServerUrl, tournamentId, token, botId, hardestDifficulty)
|
||||||
|
pendingGame(cfg).foreach { (gameId, color) =>
|
||||||
|
if activeGames.add(gameId) then
|
||||||
|
log.infof("Polled active game %s as %s in tournament %s", gameId, color, tournamentId)
|
||||||
|
workers.submit(new Runnable { def run(): Unit = playGame(cfg, gameId, color) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def pendingGame(cfg: TournamentBotConfig): Option[(String, String)] =
|
||||||
|
for
|
||||||
|
detail <- fetchJson(cfg, target(cfg))
|
||||||
|
if detail.path("status").asText() == "started"
|
||||||
|
round = detail.path("round").asInt(0)
|
||||||
|
if round > 0
|
||||||
|
pairings <- fetchJson(cfg, target(cfg).path("round").path(round.toString)).map(_.path("pairings"))
|
||||||
|
result <- findBotGame(pairings, cfg.botId)
|
||||||
|
yield result
|
||||||
|
|
||||||
|
private def findBotGame(pairings: JsonNode, botId: String): Option[(String, String)] =
|
||||||
|
pairings.elements().asScala
|
||||||
|
.flatMap { p =>
|
||||||
|
val whiteId = p.path("white").path("id").asText()
|
||||||
|
val blackId = p.path("black").path("id").asText()
|
||||||
|
val color = if whiteId == botId then Some("white") else if blackId == botId then Some("black") else None
|
||||||
|
color.flatMap(c => activeMatch(p.path("matches")).map(gameId => (gameId, c)))
|
||||||
|
}
|
||||||
|
.nextOption()
|
||||||
|
|
||||||
|
private def activeMatch(matches: JsonNode): Option[String] =
|
||||||
|
matches.elements().asScala
|
||||||
|
.find(m => m.path("gameId").asText().nonEmpty && !(m.has("outcome") && !m.path("outcome").isNull))
|
||||||
|
.map(_.path("gameId").asText())
|
||||||
|
|
||||||
|
private def fetchJson(cfg: TournamentBotConfig, t: jakarta.ws.rs.client.WebTarget): Option[JsonNode] =
|
||||||
|
Try {
|
||||||
|
val response = authed(cfg, t).get()
|
||||||
|
try
|
||||||
|
if response.getStatus == 200 then Some(objectMapper.readTree(response.readEntity(classOf[String])))
|
||||||
|
else None
|
||||||
|
finally response.close()
|
||||||
|
}.getOrElse(None)
|
||||||
|
|
||||||
|
private def resolveAutoJoinToken(): Option[String] =
|
||||||
|
autoJoinToken match
|
||||||
|
case some @ Some(_) => some
|
||||||
|
case None =>
|
||||||
|
autoJoinToken = registerWithServer(autoJoinServerUrl, botName(hardestDifficulty))
|
||||||
|
autoJoinToken
|
||||||
|
|
||||||
|
private def openTournaments(): List[String] =
|
||||||
|
Try {
|
||||||
|
val response = client.target(autoJoinServerUrl)
|
||||||
|
.path("api").path("tournament")
|
||||||
|
.request(MediaType.APPLICATION_JSON).get()
|
||||||
|
if response.getStatus == 200 then
|
||||||
|
val node = objectMapper.readTree(response.readEntity(classOf[String]))
|
||||||
|
response.close()
|
||||||
|
node.path("created").elements().asScala.toList.map(_.path("id").asText()).filter(_.nonEmpty)
|
||||||
|
else { response.close(); Nil }
|
||||||
|
}.getOrElse(Nil)
|
||||||
|
|
||||||
private def resolveToken(difficulty: String): Option[String] =
|
private def resolveToken(difficulty: String): Option[String] =
|
||||||
val name = botName(difficulty)
|
val name = botName(difficulty)
|
||||||
val redisKey = s"${redisConfig.prefix}:tournament-bot:token:$name"
|
val redisKey = s"${redisConfig.prefix}:tournament-bot:token:$name"
|
||||||
Try(accountServiceClient.getBotToken(name).token)
|
registerWithServer(tournamentServiceUrl, name)
|
||||||
.toOption
|
.orElse(fetchTokenFromAccountService(name))
|
||||||
.filter(_.nonEmpty)
|
|
||||||
.map { token =>
|
.map { token =>
|
||||||
redis.value(classOf[String]).set(redisKey, token)
|
redis.value(classOf[String]).set(redisKey, token)
|
||||||
log.infof("Fetched fresh bot token for %s from account service", name)
|
log.infof("Refreshed bot token for %s — stored in Redis", name)
|
||||||
token
|
token
|
||||||
}
|
}
|
||||||
.orElse {
|
.orElse {
|
||||||
@@ -83,15 +184,50 @@ class TournamentBotGamePlayer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def registerWithServer(serverUrl: String, name: String): Option[String] =
|
||||||
|
Try {
|
||||||
|
val body = s"""{"name":"${name.replace("\"", "\\\"")}","isBot":true}"""
|
||||||
|
val response = client.target(serverUrl)
|
||||||
|
.path("api").path("auth").path("register")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.post(Entity.entity(body, MediaType.APPLICATION_JSON))
|
||||||
|
val status = response.getStatus
|
||||||
|
if status == 200 || status == 201 then
|
||||||
|
val token = objectMapper.readTree(response.readEntity(classOf[String])).path("token").asText()
|
||||||
|
response.close()
|
||||||
|
Option(token).filter(_.nonEmpty)
|
||||||
|
else
|
||||||
|
val errBody = response.readEntity(classOf[String])
|
||||||
|
log.warnf("Register %s on %s returned status %d: %s", name, serverUrl, status, errBody)
|
||||||
|
response.close()
|
||||||
|
None
|
||||||
|
}.recover { case ex => log.warnf(ex, "Register %s on %s failed", name, serverUrl); None }
|
||||||
|
.toOption.flatten
|
||||||
|
|
||||||
|
private def fetchTokenFromAccountService(name: String): Option[String] =
|
||||||
|
Try(accountServiceClient.getBotToken(name).token).toOption.filter(_.nonEmpty)
|
||||||
|
.orElse {
|
||||||
|
Try {
|
||||||
|
val allNames = BotController.listBots.map(botName)
|
||||||
|
accountServiceClient.syncBots(de.nowchess.bot.client.SyncOfficialBotsRequest(allNames))
|
||||||
|
accountServiceClient.getBotToken(name).token
|
||||||
|
}.toOption.filter(_.nonEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
private def parkOnStartup(token: Option[String]): Unit =
|
private def parkOnStartup(token: Option[String]): Unit =
|
||||||
|
val localAccountUrl = System.getenv().asScala.getOrElse("ACCOUNT_SERVICE_URL", "http://localhost:8083")
|
||||||
token match
|
token match
|
||||||
case None => log.warn("No bot token resolved — skipping park")
|
case None => log.warn("No bot token resolved — skipping local park")
|
||||||
case Some(tok) =>
|
case Some(tok) =>
|
||||||
val localAccountUrl = System.getenv().asScala.getOrElse("ACCOUNT_SERVICE_URL", "http://localhost:8083")
|
BotController.listBots.foreach(diff => parkOnAccountService(localAccountUrl, diff, tok))
|
||||||
BotController.listBots.foreach(diff => parkOn(localAccountUrl, diff, tok))
|
fetchRemoteServers().foreach { serverUrl =>
|
||||||
fetchRemoteServers().foreach { serverUrl =>
|
BotController.listBots.foreach { diff =>
|
||||||
BotController.listBots.foreach(diff => parkOn(serverUrl, diff, tok))
|
val name = botName(diff)
|
||||||
}
|
registerWithServer(serverUrl, name) match
|
||||||
|
case None => log.warnf("Could not register %s on %s — skipping park", name, serverUrl)
|
||||||
|
case Some(tok) => parkOnTournamentServer(serverUrl, name, tok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def fetchRemoteServers(): List[String] =
|
private def fetchRemoteServers(): List[String] =
|
||||||
Try {
|
Try {
|
||||||
@@ -105,7 +241,7 @@ class TournamentBotGamePlayer:
|
|||||||
else { response.close(); Nil }
|
else { response.close(); Nil }
|
||||||
}.getOrElse(Nil)
|
}.getOrElse(Nil)
|
||||||
|
|
||||||
private def parkOn(serverUrl: String, difficulty: String, token: String): Unit =
|
private def parkOnAccountService(serverUrl: String, difficulty: String, token: String): Unit =
|
||||||
Try {
|
Try {
|
||||||
val body = s"""{"name":"${botName(difficulty)}"}"""
|
val body = s"""{"name":"${botName(difficulty)}"}"""
|
||||||
val response = client.target(serverUrl).path("api").path("account").path("bots")
|
val response = client.target(serverUrl).path("api").path("account").path("bots")
|
||||||
@@ -119,6 +255,20 @@ class TournamentBotGamePlayer:
|
|||||||
response.close()
|
response.close()
|
||||||
}.failed.foreach(ex => log.warnf(ex, "Failed to park %s on %s", botName(difficulty), serverUrl))
|
}.failed.foreach(ex => log.warnf(ex, "Failed to park %s on %s", botName(difficulty), serverUrl))
|
||||||
|
|
||||||
|
private def parkOnTournamentServer(serverUrl: String, name: String, token: String): Unit =
|
||||||
|
Try {
|
||||||
|
val body = s"""{"name":"${name.replace("\"", "\\\"")}"}"""
|
||||||
|
val response = client.target(serverUrl).path("api").path("bots")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.header("Authorization", s"Bearer $token")
|
||||||
|
.post(Entity.entity(body, MediaType.APPLICATION_JSON))
|
||||||
|
if response.getStatus == 201 || response.getStatus == 200 then
|
||||||
|
val id = objectMapper.readTree(response.readEntity(classOf[String])).path("id").asText()
|
||||||
|
log.infof("Parked bot %s on tournament server %s as id %s", name, serverUrl, id)
|
||||||
|
else log.warnf("Park %s on tournament server %s returned status %d", name, serverUrl, response.getStatus)
|
||||||
|
response.close()
|
||||||
|
}.failed.foreach(ex => log.warnf(ex, "Failed to park %s on tournament server %s", name, serverUrl))
|
||||||
|
|
||||||
private def botName(difficulty: String): String = s"NowChess ${difficulty.capitalize}"
|
private def botName(difficulty: String): String = s"NowChess ${difficulty.capitalize}"
|
||||||
|
|
||||||
def joinTournament(
|
def joinTournament(
|
||||||
@@ -128,8 +278,8 @@ class TournamentBotGamePlayer:
|
|||||||
): Either[String, String] =
|
): Either[String, String] =
|
||||||
val redisKey = s"${redisConfig.prefix}:tournament-bot:token:${botName(difficulty)}"
|
val redisKey = s"${redisConfig.prefix}:tournament-bot:token:${botName(difficulty)}"
|
||||||
val resolvedToken = botToken.filter(_.nonEmpty)
|
val resolvedToken = botToken.filter(_.nonEmpty)
|
||||||
.orElse(System.getenv().asScala.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty))
|
|
||||||
.orElse(Option(redis.value(classOf[String]).get(redisKey)).filter(_.nonEmpty))
|
.orElse(Option(redis.value(classOf[String]).get(redisKey)).filter(_.nonEmpty))
|
||||||
|
.orElse(System.getenv().asScala.get("TOURNAMENT_BOT_TOKEN").filter(_.nonEmpty))
|
||||||
resolvedToken match
|
resolvedToken match
|
||||||
case None => Left("No bot token provided and TOURNAMENT_BOT_TOKEN not configured")
|
case None => Left("No bot token provided and TOURNAMENT_BOT_TOKEN not configured")
|
||||||
case Some(token) =>
|
case Some(token) =>
|
||||||
@@ -137,7 +287,7 @@ class TournamentBotGamePlayer:
|
|||||||
case None => Left("Invalid bot token — could not extract subject")
|
case None => Left("Invalid bot token — could not extract subject")
|
||||||
case Some(botId) =>
|
case Some(botId) =>
|
||||||
val cfg = TournamentBotConfig(tournamentServiceUrl, tournamentId, token, botId, difficulty)
|
val cfg = TournamentBotConfig(tournamentServiceUrl, tournamentId, token, botId, difficulty)
|
||||||
if join(cfg) then
|
if joinedOrParticipating(cfg) then
|
||||||
startAsync(cfg)
|
startAsync(cfg)
|
||||||
Right(botId)
|
Right(botId)
|
||||||
else Left("Failed to join tournament")
|
else Left("Failed to join tournament")
|
||||||
@@ -160,15 +310,18 @@ class TournamentBotGamePlayer:
|
|||||||
case Failure(ex) => log.warnf(ex, "Tournament event stream dropped — reconnecting"); sleep(5000)
|
case Failure(ex) => log.warnf(ex, "Tournament event stream dropped — reconnecting"); sleep(5000)
|
||||||
case Success(_) => sleep(2000)
|
case Success(_) => sleep(2000)
|
||||||
|
|
||||||
private def join(cfg: TournamentBotConfig): Boolean =
|
// 200 = joined, 409 = already a participant (e.g. after a restart) — both mean "play this tournament".
|
||||||
|
private def joinedOrParticipating(cfg: TournamentBotConfig): Boolean =
|
||||||
Try {
|
Try {
|
||||||
val response = authed(cfg, target(cfg).path("join"))
|
val response = authed(cfg, target(cfg).path("join"))
|
||||||
.post(Entity.entity("", MediaType.APPLICATION_JSON))
|
.post(Entity.entity("", MediaType.APPLICATION_JSON))
|
||||||
val ok = response.getStatus == 200
|
val status = response.getStatus
|
||||||
if ok then log.infof("Joined tournament %s", cfg.tournamentId)
|
|
||||||
else log.errorf("Failed to join tournament %s — status %d", cfg.tournamentId, response.getStatus)
|
|
||||||
response.close()
|
response.close()
|
||||||
ok
|
status match
|
||||||
|
case 200 => log.infof("Joined tournament %s", cfg.tournamentId); true
|
||||||
|
case 409 => log.infof("Already in tournament %s — resuming", cfg.tournamentId); true
|
||||||
|
case other =>
|
||||||
|
log.errorf("Failed to join tournament %s — status %d", cfg.tournamentId, other); false
|
||||||
}.getOrElse { log.error("Join request failed"); false }
|
}.getOrElse { log.error("Join request failed"); false }
|
||||||
|
|
||||||
private def streamEvents(cfg: TournamentBotConfig): Unit =
|
private def streamEvents(cfg: TournamentBotConfig): Unit =
|
||||||
@@ -184,68 +337,71 @@ class TournamentBotGamePlayer:
|
|||||||
forEachLine(response.readEntity(classOf[InputStream])): line =>
|
forEachLine(response.readEntity(classOf[InputStream])): line =>
|
||||||
parse(line).foreach: node =>
|
parse(line).foreach: node =>
|
||||||
if node.path("type").asText() == "gameStart" then
|
if node.path("type").asText() == "gameStart" then
|
||||||
onGameStart(cfg, node.path("gameId").asText(), node.path("color").asText())
|
onGameStart(cfg, node.path("gameId").asText())
|
||||||
|
|
||||||
private def onGameStart(cfg: TournamentBotConfig, gameId: String, color: String): Unit =
|
private def onGameStart(cfg: TournamentBotConfig, gameId: String): Unit =
|
||||||
if gameId.nonEmpty && color.nonEmpty && activeGames.add(gameId) then
|
if gameId.isEmpty then ()
|
||||||
workers.submit(new Runnable { def run(): Unit = playGame(cfg, gameId, color) })
|
else
|
||||||
()
|
log.infof("gameStart received — tournament=%s game=%s bot=%s", cfg.tournamentId, gameId, cfg.botId)
|
||||||
|
resolveColor(cfg, gameId) match
|
||||||
|
case None => log.infof("Skipping game %s — bot %s is not a participant", gameId, cfg.botId)
|
||||||
|
case Some(color) =>
|
||||||
|
if activeGames.add(gameId) then
|
||||||
|
log.infof("Joining game %s as %s", gameId, color)
|
||||||
|
workers.submit(new Runnable { def run(): Unit = playGame(cfg, gameId, color) })
|
||||||
|
()
|
||||||
|
|
||||||
|
private def resolveColor(cfg: TournamentBotConfig, gameId: String): Option[String] =
|
||||||
|
fetchGame(cfg, gameId).flatMap { node =>
|
||||||
|
val whiteId = node.path("white").path("id").asText()
|
||||||
|
val blackId = node.path("black").path("id").asText()
|
||||||
|
if whiteId == cfg.botId then Some("white")
|
||||||
|
else if blackId == cfg.botId then Some("black")
|
||||||
|
else None
|
||||||
|
}
|
||||||
|
|
||||||
|
private def fetchGame(cfg: TournamentBotConfig, gameId: String): Option[JsonNode] =
|
||||||
|
Try {
|
||||||
|
val response = authed(cfg, target(cfg).path("game").path(gameId)).get()
|
||||||
|
try
|
||||||
|
if response.getStatus == 200 then Some(objectMapper.readTree(response.readEntity(classOf[String])))
|
||||||
|
else { log.warnf("Game detail %s returned status %d", gameId, response.getStatus); None }
|
||||||
|
finally response.close()
|
||||||
|
}.getOrElse(None)
|
||||||
|
|
||||||
private def playGame(cfg: TournamentBotConfig, gameId: String, color: String): Unit =
|
private def playGame(cfg: TournamentBotConfig, gameId: String, color: String): Unit =
|
||||||
Try {
|
Try {
|
||||||
log.infof("Playing game %s as %s", gameId, color)
|
log.infof("Playing game %s as %s", gameId, color)
|
||||||
openGameStream(cfg, gameId).foreach(consumeGameStream(cfg, gameId, color, _))
|
pollGameLoop(cfg, gameId, color)
|
||||||
activeGames.remove(gameId)
|
activeGames.remove(gameId)
|
||||||
} match
|
} match
|
||||||
case Failure(ex) => log.errorf(ex, "Game %s crashed", gameId); activeGames.remove(gameId)
|
case Failure(ex) => log.errorf(ex, "Game %s crashed", gameId); activeGames.remove(gameId)
|
||||||
case Success(_) => ()
|
case Success(_) => ()
|
||||||
|
|
||||||
private def consumeGameStream(cfg: TournamentBotConfig, gameId: String, color: String, stream: InputStream): Unit =
|
// The native JAX-RS client buffers streaming responses, so reading the NDJSON game stream blocks
|
||||||
val reader = new BufferedReader(new InputStreamReader(stream))
|
// forever. Poll the game state with plain GETs (which work) and move when it is our turn.
|
||||||
|
private def pollGameLoop(cfg: TournamentBotConfig, gameId: String, color: String): Unit =
|
||||||
// scalafix:off DisableSyntax.var
|
// scalafix:off DisableSyntax.var
|
||||||
var done = false
|
var done = false
|
||||||
|
var lastFen = ""
|
||||||
// scalafix:on DisableSyntax.var
|
// scalafix:on DisableSyntax.var
|
||||||
Iterator
|
while running && !done do
|
||||||
.continually(reader.readLine())
|
fetchJson(cfg, target(cfg).path("game").path(gameId)) match
|
||||||
.map(Option(_))
|
case None => sleep(2000)
|
||||||
.takeWhile(opt => opt.isDefined && running && !done)
|
case Some(node) =>
|
||||||
.flatten
|
val status = node.path("status").asText()
|
||||||
.foreach { line =>
|
if gameTerminalStatuses.contains(status) then
|
||||||
parse(line).foreach: node =>
|
log.infof("Game %s ended — status=%s", gameId, status); done = true
|
||||||
node.path("type").asText() match
|
else
|
||||||
case "gameState" =>
|
val turn = node.path("turn").asText()
|
||||||
maybeMove(
|
val fen = node.path("fen").asText()
|
||||||
cfg,
|
if turn == color && status == "ongoing" && fen.nonEmpty && fen != lastFen then
|
||||||
gameId,
|
lastFen = fen
|
||||||
color,
|
log.infof("Our turn in game %s — computing move (fen=%s)", gameId, fen)
|
||||||
node.path("turn").asText(),
|
computeUci(cfg, fen) match
|
||||||
node.path("status").asText(),
|
case None => log.warnf("No move found for game %s (fen=%s)", gameId, fen)
|
||||||
node.path("fen").asText(),
|
case Some(uci) => submitMove(cfg, gameId, uci)
|
||||||
)
|
sleep(1000)
|
||||||
case "move" =>
|
|
||||||
maybeMove(cfg, gameId, color, node.path("turn").asText(), "ongoing", node.path("fen").asText())
|
|
||||||
case "gameEnd" =>
|
|
||||||
log.infof(
|
|
||||||
"Game %s ended — status=%s winner=%s",
|
|
||||||
gameId,
|
|
||||||
node.path("status").asText(),
|
|
||||||
node.path("winner").asText(),
|
|
||||||
); done = true
|
|
||||||
case _ => ()
|
|
||||||
}
|
|
||||||
|
|
||||||
private def maybeMove(
|
|
||||||
cfg: TournamentBotConfig,
|
|
||||||
gameId: String,
|
|
||||||
color: String,
|
|
||||||
turn: String,
|
|
||||||
status: String,
|
|
||||||
fen: String,
|
|
||||||
): Unit =
|
|
||||||
if turn == color && status == "ongoing" && fen.nonEmpty then
|
|
||||||
computeUci(cfg, fen) match
|
|
||||||
case None => log.warnf("No move found for game %s (fen=%s)", gameId, fen)
|
|
||||||
case Some(uci) => submitMove(cfg, gameId, uci)
|
|
||||||
|
|
||||||
private def computeUci(cfg: TournamentBotConfig, fen: String): Option[String] =
|
private def computeUci(cfg: TournamentBotConfig, fen: String): Option[String] =
|
||||||
FenParser.parseFen(fen) match
|
FenParser.parseFen(fen) match
|
||||||
@@ -263,15 +419,6 @@ class TournamentBotGamePlayer:
|
|||||||
case Failure(ex) => log.errorf(ex, "Error submitting move %s in game %s", uci, gameId)
|
case Failure(ex) => log.errorf(ex, "Error submitting move %s in game %s", uci, gameId)
|
||||||
case Success(_) => ()
|
case Success(_) => ()
|
||||||
|
|
||||||
private def openGameStream(cfg: TournamentBotConfig, gameId: String): Option[InputStream] =
|
|
||||||
Try {
|
|
||||||
val response = authed(cfg, target(cfg).path("game").path(gameId).path("stream"))
|
|
||||||
.header("Accept", "application/x-ndjson")
|
|
||||||
.get()
|
|
||||||
if response.getStatus == 200 then Some(response.readEntity(classOf[InputStream]))
|
|
||||||
else { log.warnf("Game stream %s returned status %d", gameId, response.getStatus); response.close(); None }
|
|
||||||
}.getOrElse(None)
|
|
||||||
|
|
||||||
private def engine(cfg: TournamentBotConfig): Bot =
|
private def engine(cfg: TournamentBotConfig): Bot =
|
||||||
botController.getBot(cfg.difficulty).orElse(botController.getBot("medium")).get
|
botController.getBot(cfg.difficulty).orElse(botController.getBot("medium")).get
|
||||||
|
|
||||||
|
|||||||
@@ -80,76 +80,58 @@ class HybridBotTest extends AnyFunSuite with Matchers:
|
|||||||
bot.apply(ctx) should be(Some(Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4), MoveType.Normal())))
|
bot.apply(ctx) should be(Some(Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4), MoveType.Normal())))
|
||||||
finally Files.deleteIfExists(tempFile)
|
finally Files.deleteIfExists(tempFile)
|
||||||
|
|
||||||
test("HybridBot reports veto when classical and NNUE differ above threshold"):
|
// Classical search picks mateMove (delivers mate); NNUE distrusts it and prefers altMove.
|
||||||
val forcedMove = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R3), MoveType.Normal())
|
private val mateMove = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R4), MoveType.Normal())
|
||||||
val oneMoveRules = new RuleSet:
|
private val altMove = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R3), MoveType.Normal())
|
||||||
def candidateMoves(context: GameContext)(square: Square): List[Move] = List(forcedMove)
|
|
||||||
def legalMoves(context: GameContext)(square: Square): List[Move] = List(forcedMove)
|
|
||||||
def allLegalMoves(context: GameContext): List[Move] = List(forcedMove)
|
|
||||||
def isCheck(context: GameContext): Boolean = false
|
|
||||||
def isCheckmate(context: GameContext): Boolean = false
|
|
||||||
def isStalemate(context: GameContext): Boolean = false
|
|
||||||
def isInsufficientMaterial(context: GameContext): Boolean = false
|
|
||||||
def isFiftyMoveRule(context: GameContext): Boolean = false
|
|
||||||
def isThreefoldRepetition(context: GameContext): Boolean = false
|
|
||||||
def applyMove(context: GameContext)(move: Move): GameContext =
|
|
||||||
context.copy(turn = context.turn.opposite, moves = context.moves :+ move)
|
|
||||||
|
|
||||||
object LowNnue extends Evaluation:
|
private def vetoRules: RuleSet = new RuleSet:
|
||||||
val CHECKMATE_SCORE: Int = 10_000_000
|
private def fresh(ctx: GameContext): Boolean = ctx.moves.isEmpty
|
||||||
val DRAW_SCORE: Int = 0
|
def candidateMoves(context: GameContext)(square: Square): List[Move] = Nil
|
||||||
def evaluate(context: GameContext): Int = 0
|
def legalMoves(context: GameContext)(square: Square): List[Move] = Nil
|
||||||
|
def allLegalMoves(context: GameContext): List[Move] =
|
||||||
|
if fresh(context) then List(mateMove, altMove) else Nil
|
||||||
|
def isCheck(context: GameContext): Boolean = false
|
||||||
|
def isCheckmate(context: GameContext): Boolean = context.moves.lastOption.contains(mateMove)
|
||||||
|
def isStalemate(context: GameContext): Boolean = context.moves.lastOption.contains(altMove)
|
||||||
|
def isInsufficientMaterial(context: GameContext): Boolean = false
|
||||||
|
def isFiftyMoveRule(context: GameContext): Boolean = false
|
||||||
|
def isThreefoldRepetition(context: GameContext): Boolean = false
|
||||||
|
def applyMove(context: GameContext)(move: Move): GameContext =
|
||||||
|
context.copy(turn = context.turn.opposite, moves = context.moves :+ move)
|
||||||
|
|
||||||
object HighClassic extends Evaluation:
|
// NNUE rates the mate move worse for us (higher = better for opponent) than the alternative.
|
||||||
val CHECKMATE_SCORE: Int = 10_000_000
|
private object DistrustfulNnue extends Evaluation:
|
||||||
val DRAW_SCORE: Int = 0
|
val CHECKMATE_SCORE: Int = 10_000_000
|
||||||
def evaluate(context: GameContext): Int = 10_000
|
val DRAW_SCORE: Int = 0
|
||||||
|
def evaluate(context: GameContext): Int = if context.moves.lastOption.contains(mateMove) then 5_000 else 0
|
||||||
|
|
||||||
|
private object HighClassic extends Evaluation:
|
||||||
|
val CHECKMATE_SCORE: Int = 10_000_000
|
||||||
|
val DRAW_SCORE: Int = 0
|
||||||
|
def evaluate(context: GameContext): Int = if context.moves.lastOption.contains(mateMove) then 10_000 else 0
|
||||||
|
|
||||||
|
test("HybridBot switches to NNUE's preferred move and reports veto when evals diverge"):
|
||||||
val reported = AtomicBoolean(false)
|
val reported = AtomicBoolean(false)
|
||||||
val bot = HybridBot(
|
val bot = HybridBot(
|
||||||
BotDifficulty.Easy,
|
BotDifficulty.Easy,
|
||||||
rules = oneMoveRules,
|
rules = vetoRules,
|
||||||
nnueEvaluation = LowNnue,
|
nnueEvaluation = DistrustfulNnue,
|
||||||
classicalEvaluation = HighClassic,
|
classicalEvaluation = HighClassic,
|
||||||
vetoReporter = _ => reported.set(true),
|
vetoReporter = _ => reported.set(true),
|
||||||
)
|
)
|
||||||
|
|
||||||
bot.apply(GameContext.initial) should be(Some(forcedMove))
|
bot.apply(GameContext.initial) should be(Some(altMove))
|
||||||
reported.get should be(true)
|
reported.get should be(true)
|
||||||
|
|
||||||
test("HybridBot default veto reporter prints when threshold is exceeded"):
|
test("HybridBot default veto reporter prints when threshold is exceeded"):
|
||||||
val forcedMove = Move(Square(File.E, Rank.R2), Square(File.E, Rank.R3), MoveType.Normal())
|
|
||||||
val oneMoveRules = new RuleSet:
|
|
||||||
def candidateMoves(context: GameContext)(square: Square): List[Move] = List(forcedMove)
|
|
||||||
def legalMoves(context: GameContext)(square: Square): List[Move] = List(forcedMove)
|
|
||||||
def allLegalMoves(context: GameContext): List[Move] = List(forcedMove)
|
|
||||||
def isCheck(context: GameContext): Boolean = false
|
|
||||||
def isCheckmate(context: GameContext): Boolean = false
|
|
||||||
def isStalemate(context: GameContext): Boolean = false
|
|
||||||
def isInsufficientMaterial(context: GameContext): Boolean = false
|
|
||||||
def isFiftyMoveRule(context: GameContext): Boolean = false
|
|
||||||
def isThreefoldRepetition(context: GameContext): Boolean = false
|
|
||||||
def applyMove(context: GameContext)(move: Move): GameContext =
|
|
||||||
context.copy(turn = context.turn.opposite, moves = context.moves :+ move)
|
|
||||||
|
|
||||||
object LowNnue extends Evaluation:
|
|
||||||
val CHECKMATE_SCORE: Int = 10_000_000
|
|
||||||
val DRAW_SCORE: Int = 0
|
|
||||||
def evaluate(context: GameContext): Int = 0
|
|
||||||
|
|
||||||
object HighClassic extends Evaluation:
|
|
||||||
val CHECKMATE_SCORE: Int = 10_000_000
|
|
||||||
val DRAW_SCORE: Int = 0
|
|
||||||
def evaluate(context: GameContext): Int = 10_000
|
|
||||||
|
|
||||||
val bot = HybridBot(
|
val bot = HybridBot(
|
||||||
BotDifficulty.Easy,
|
BotDifficulty.Easy,
|
||||||
rules = oneMoveRules,
|
rules = vetoRules,
|
||||||
nnueEvaluation = LowNnue,
|
nnueEvaluation = DistrustfulNnue,
|
||||||
classicalEvaluation = HighClassic,
|
classicalEvaluation = HighClassic,
|
||||||
)
|
)
|
||||||
|
|
||||||
val printed = Console.withOut(new java.io.ByteArrayOutputStream()) {
|
val printed = Console.withOut(new java.io.ByteArrayOutputStream()) {
|
||||||
bot.apply(GameContext.initial)
|
bot.apply(GameContext.initial)
|
||||||
}
|
}
|
||||||
printed should be(Some(forcedMove))
|
printed should be(Some(altMove))
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=0
|
MAJOR=0
|
||||||
MINOR=24
|
MINOR=32
|
||||||
PATCH=0
|
PATCH=0
|
||||||
|
|||||||
@@ -55,3 +55,42 @@
|
|||||||
* **tournament:** replace scala.util.Random singleton with UUID for native image ([a50884a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a50884a11b1de500e74c18fd08d2d102d53cc3e9))
|
* **tournament:** replace scala.util.Random singleton with UUID for native image ([a50884a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a50884a11b1de500e74c18fd08d2d102d53cc3e9))
|
||||||
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
||||||
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* NCS-121 pipeline for tournament ([#68](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/68)) ([145f467](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/145f4676483f92bfe6f2d9ca40e2cb4200982e87))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **reflection:** add GameWritebackEventDto to native reflection configuration ([1aee39c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1aee39c1ad286984501ac4b47da2b72d60b58a6f))
|
||||||
|
* **reflection:** add native reflection configuration for tournament classes ([65bc6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/65bc6a759937543df2d29905688bfa9e68d0c9d4))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** remove dynamic server add/remove endpoints ([6d06edd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6d06edda69a50de65cd9efa27f26a4cc6b437f9d))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tournament:** replace scala.util.Random singleton with UUID for native image ([a50884a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a50884a11b1de500e74c18fd08d2d102d53cc3e9))
|
||||||
|
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
||||||
|
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
||||||
|
## (2026-06-23)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **analytics:** add Spark batch analytics module ([#70](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/70)) ([39f1657](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/39f1657e1db6e84889af338c43be8cb5c03c3ec3))
|
||||||
|
* NCS-121 pipeline for tournament ([#68](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/68)) ([145f467](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/145f4676483f92bfe6f2d9ca40e2cb4200982e87))
|
||||||
|
* NCS-82 add Swiss-system tournament module ([#55](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/55)) ([c5661de](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/c5661de4a0ebf4b33211f5a391840dcf744656b7))
|
||||||
|
* **reflection:** add GameWritebackEventDto to native reflection configuration ([1aee39c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/1aee39c1ad286984501ac4b47da2b72d60b58a6f))
|
||||||
|
* **reflection:** add native reflection configuration for tournament classes ([65bc6a7](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/65bc6a759937543df2d29905688bfa9e68d0c9d4))
|
||||||
|
* **tournament:** auto-join external tournaments and publish created ones ([#77](https://git.janis-eccarius.de/NowChess/NowChessSystems/issues/77)) ([9978b7e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/9978b7ea78eb658a225a461b9cd339386c0c14f3))
|
||||||
|
* **tournament:** federate tournaments across clusters with DB replication ([5b000a6](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/5b000a6e5f04ea6770d1c7ab6bfdaded77a99172))
|
||||||
|
* **tournament:** remove dynamic server add/remove endpoints ([6d06edd](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/6d06edda69a50de65cd9efa27f26a4cc6b437f9d))
|
||||||
|
* **tournament:** seed external server registry from env var on startup ([845dc9c](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/845dc9c2935c8bc1be42541dfaf31c9a861d3272))
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tournament:** replace scala.util.Random singleton with UUID for native image ([a50884a](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/a50884a11b1de500e74c18fd08d2d102d53cc3e9))
|
||||||
|
* **tournament:** use HS256 director token for native tournament-server calls ([b98bdd2](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/b98bdd2a64eb6c8279bd3cfe15d70628025ef0e5))
|
||||||
|
* **tournament:** use Optional[String] for selfUrl ConfigProperty to avoid startup failure ([28cbc2e](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/28cbc2e18447aa8a04a5868889a49b555075d0c6))
|
||||||
|
* wrap server list response in ExternalTournamentServerList ([f2d79e4](https://git.janis-eccarius.de/NowChess/NowChessSystems/commit/f2d79e4952aea6bde762c294eb202474b7827054))
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ nowchess:
|
|||||||
tournament:
|
tournament:
|
||||||
self-url: ""
|
self-url: ""
|
||||||
external-servers: ""
|
external-servers: ""
|
||||||
|
native-server-url: ${TOURNAMENT_NATIVE_SERVER_URL:http://141.37.123.132:8086}
|
||||||
|
director-name: ${TOURNAMENT_DIRECTOR_NAME:NowChess System}
|
||||||
|
|
||||||
mp:
|
mp:
|
||||||
jwt:
|
jwt:
|
||||||
@@ -53,6 +55,8 @@ mp:
|
|||||||
tournament:
|
tournament:
|
||||||
self-url: ${TOURNAMENT_SELF_URL:}
|
self-url: ${TOURNAMENT_SELF_URL:}
|
||||||
external-servers: ${TOURNAMENT_EXTERNAL_SERVERS:}
|
external-servers: ${TOURNAMENT_EXTERNAL_SERVERS:}
|
||||||
|
native-server-url: ${TOURNAMENT_NATIVE_SERVER_URL:http://141.37.123.132:8086}
|
||||||
|
director-name: ${TOURNAMENT_DIRECTOR_NAME:NowChess System}
|
||||||
|
|
||||||
"%test":
|
"%test":
|
||||||
quarkus:
|
quarkus:
|
||||||
|
|||||||
+29
@@ -40,6 +40,12 @@ class TournamentResource:
|
|||||||
|
|
||||||
@ConfigProperty(name = "nowchess.tournament.self-url")
|
@ConfigProperty(name = "nowchess.tournament.self-url")
|
||||||
var selfUrl: Optional[String] = uninitialized
|
var selfUrl: Optional[String] = uninitialized
|
||||||
|
|
||||||
|
@ConfigProperty(name = "nowchess.tournament.native-server-url", defaultValue = "http://141.37.123.132:8086")
|
||||||
|
var nativeServerUrl: String = uninitialized
|
||||||
|
|
||||||
|
@ConfigProperty(name = "nowchess.tournament.director-name", defaultValue = "NowChess System")
|
||||||
|
var directorName: String = uninitialized
|
||||||
// scalafix:on
|
// scalafix:on
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@@ -95,8 +101,31 @@ class TournamentResource:
|
|||||||
log.warnf("Failed to replicate tournament %s to %s", t.id, remoteUrl)
|
log.warnf("Failed to replicate tournament %s to %s", t.id, remoteUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
publishToNativeServer(t)
|
||||||
Response.status(Response.Status.CREATED).entity(tournamentService.toDto(t)).build()
|
Response.status(Response.Status.CREATED).entity(tournamentService.toDto(t)).build()
|
||||||
|
|
||||||
|
private def publishToNativeServer(t: de.nowchess.tournament.domain.Tournament): Unit =
|
||||||
|
if nativeServerUrl.nonEmpty then
|
||||||
|
val form = encodeForm(
|
||||||
|
Map(
|
||||||
|
"name" -> t.fullName,
|
||||||
|
"nbRounds" -> t.nbRounds.toString,
|
||||||
|
"clockLimit" -> t.clockLimit.toString,
|
||||||
|
"clockIncrement" -> t.clockIncrement.toString,
|
||||||
|
"rated" -> t.rated.toString,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if !externalClient.publishNative(nativeServerUrl, directorName, form) then
|
||||||
|
log.warnf("Failed to publish tournament %s to native server %s", t.id, nativeServerUrl)
|
||||||
|
|
||||||
|
private def encodeForm(params: Map[String, String]): String =
|
||||||
|
params
|
||||||
|
.map((k, v) => s"${enc(k)}=${enc(v)}")
|
||||||
|
.mkString("&")
|
||||||
|
|
||||||
|
private def enc(s: String): String =
|
||||||
|
java.net.URLEncoder.encode(s, "UTF-8")
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
@Path("/{id}")
|
@Path("/{id}")
|
||||||
@PermitAll
|
@PermitAll
|
||||||
|
|||||||
+74
-2
@@ -6,6 +6,7 @@ import jakarta.enterprise.context.ApplicationScoped
|
|||||||
import jakarta.inject.Inject
|
import jakarta.inject.Inject
|
||||||
import jakarta.ws.rs.client.{Client, ClientBuilder, Entity}
|
import jakarta.ws.rs.client.{Client, ClientBuilder, Entity}
|
||||||
import jakarta.ws.rs.core.MediaType
|
import jakarta.ws.rs.core.MediaType
|
||||||
|
import org.eclipse.microprofile.config.inject.ConfigProperty
|
||||||
import scala.compiletime.uninitialized
|
import scala.compiletime.uninitialized
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
|
||||||
@@ -14,10 +15,81 @@ class ExternalTournamentClient:
|
|||||||
|
|
||||||
// scalafix:off DisableSyntax.var
|
// scalafix:off DisableSyntax.var
|
||||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||||
|
@volatile private var directorToken: Option[String] = None
|
||||||
|
|
||||||
|
@ConfigProperty(name = "nowchess.tournament.native-server-url", defaultValue = "http://141.37.123.132:8086")
|
||||||
|
var nativeServerUrl: String = uninitialized
|
||||||
|
|
||||||
|
@ConfigProperty(name = "nowchess.tournament.director-name", defaultValue = "NowChess System")
|
||||||
|
var directorName: String = uninitialized
|
||||||
// scalafix:on
|
// scalafix:on
|
||||||
|
|
||||||
private def buildClient(): Client = ClientBuilder.newClient()
|
private def buildClient(): Client = ClientBuilder.newClient()
|
||||||
|
|
||||||
|
// The tournament-server only accepts HS256 tokens it issued. Never forward a NowChessSystems
|
||||||
|
// RS256 user token to it — swap in the director token registered on that server.
|
||||||
|
private def normalize(url: String): String = url.stripSuffix("/")
|
||||||
|
|
||||||
|
private def isNativeServer(serverUrl: String): Boolean =
|
||||||
|
nativeServerUrl.nonEmpty && normalize(serverUrl) == normalize(nativeServerUrl)
|
||||||
|
|
||||||
|
private def directorBearer(): Option[String] =
|
||||||
|
directorToken
|
||||||
|
.orElse {
|
||||||
|
val fresh = registerDirector(nativeServerUrl, directorName)
|
||||||
|
directorToken = fresh
|
||||||
|
fresh
|
||||||
|
}
|
||||||
|
.map(t => s"Bearer $t")
|
||||||
|
|
||||||
|
private def authFor(serverUrl: String, userAuth: Option[String]): Option[String] =
|
||||||
|
if isNativeServer(serverUrl) then directorBearer() else userAuth
|
||||||
|
|
||||||
|
def publishNative(serverUrl: String, directorName: String, form: String): Boolean =
|
||||||
|
val token = directorToken.orElse {
|
||||||
|
val fresh = registerDirector(serverUrl, directorName)
|
||||||
|
directorToken = fresh
|
||||||
|
fresh
|
||||||
|
}
|
||||||
|
token.exists { tok =>
|
||||||
|
if createNative(serverUrl, tok, form) then true
|
||||||
|
else
|
||||||
|
val refreshed = registerDirector(serverUrl, directorName)
|
||||||
|
directorToken = refreshed
|
||||||
|
refreshed.exists(createNative(serverUrl, _, form))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def registerDirector(serverUrl: String, name: String): Option[String] =
|
||||||
|
Try {
|
||||||
|
val client = buildClient()
|
||||||
|
val body = s"""{"name":"${name.replace("\"", "\\\"")}","isBot":false}"""
|
||||||
|
val response = client
|
||||||
|
.target(s"$serverUrl/api/auth/register")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.post(Entity.entity(body, MediaType.APPLICATION_JSON))
|
||||||
|
try
|
||||||
|
if response.getStatus / 100 == 2 then
|
||||||
|
Option(objectMapper.readTree(response.readEntity(classOf[String])).path("token").asText()).filter(_.nonEmpty)
|
||||||
|
else None
|
||||||
|
finally
|
||||||
|
response.close()
|
||||||
|
client.close()
|
||||||
|
}.getOrElse(None)
|
||||||
|
|
||||||
|
private def createNative(serverUrl: String, token: String, form: String): Boolean =
|
||||||
|
Try {
|
||||||
|
val client = buildClient()
|
||||||
|
val response = client
|
||||||
|
.target(s"$serverUrl/api/tournament")
|
||||||
|
.request(MediaType.APPLICATION_JSON)
|
||||||
|
.header("Authorization", s"Bearer $token")
|
||||||
|
.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED))
|
||||||
|
try response.getStatus / 100 == 2
|
||||||
|
finally
|
||||||
|
response.close()
|
||||||
|
client.close()
|
||||||
|
}.getOrElse(false)
|
||||||
|
|
||||||
def fetchList(serverUrl: String): Option[JsonNode] =
|
def fetchList(serverUrl: String): Option[JsonNode] =
|
||||||
Try {
|
Try {
|
||||||
val client = buildClient()
|
val client = buildClient()
|
||||||
@@ -59,7 +131,7 @@ class ExternalTournamentClient:
|
|||||||
Try {
|
Try {
|
||||||
val client = buildClient()
|
val client = buildClient()
|
||||||
val builder = client.target(s"$serverUrl/$path").request(MediaType.APPLICATION_JSON)
|
val builder = client.target(s"$serverUrl/$path").request(MediaType.APPLICATION_JSON)
|
||||||
val withAuth = authHeader.fold(builder)(h => builder.header("Authorization", h))
|
val withAuth = authFor(serverUrl, authHeader).fold(builder)(h => builder.header("Authorization", h))
|
||||||
val response = withAuth.post(Entity.json(""))
|
val response = withAuth.post(Entity.json(""))
|
||||||
try (response.getStatus, response.readEntity(classOf[String]))
|
try (response.getStatus, response.readEntity(classOf[String]))
|
||||||
finally
|
finally
|
||||||
@@ -86,7 +158,7 @@ class ExternalTournamentClient:
|
|||||||
Try {
|
Try {
|
||||||
val client = buildClient()
|
val client = buildClient()
|
||||||
val builder = client.target(s"$serverUrl/$path").request("application/x-ndjson")
|
val builder = client.target(s"$serverUrl/$path").request("application/x-ndjson")
|
||||||
val withAuth = authHeader.fold(builder)(h => builder.header("Authorization", h))
|
val withAuth = authFor(serverUrl, authHeader).fold(builder)(h => builder.header("Authorization", h))
|
||||||
val response = withAuth.get()
|
val response = withAuth.get()
|
||||||
if response.getStatus == 200 then Some(response.readEntity(classOf[java.io.InputStream]))
|
if response.getStatus == 200 then Some(response.readEntity(classOf[java.io.InputStream]))
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
MAJOR=0
|
MAJOR=0
|
||||||
MINOR=5
|
MINOR=7
|
||||||
PATCH=0
|
PATCH=0
|
||||||
|
|||||||
Reference in New Issue
Block a user