From 55feb5b740474eddb8e592d389efa293ecd5f5e1 Mon Sep 17 00:00:00 2001 From: wayhope Date: Mon, 7 Sep 2026 08:42:49 +0900 Subject: [PATCH] InitProject Co-authored-by: Cursor --- .dockerignore | 25 + .env.example | 41 + .gitattributes | 1 + .github/workflows/test.yml | 16 + .gitignore | 13 + Dockerfile.asset | 12 + Dockerfile.clockwork | 11 + Dockerfile.core | 11 + Dockerfile.duel | 11 + Dockerfile.nexus | 11 + Dockerfile.overseer | 12 + README.md | 345 +- app/asset-app/asset-app.ts | 61 + .../source/loader/plain-express-boot.ts | 10 + app/clockwork-app/clockwork-app.ts | 58 + .../source/routine/cycle-clock.ts | 17 + .../source/routine/duel-stage-clock.ts | 18 + .../source/routine/ip-occupancy-clock.ts | 20 + .../source/routine/parcel-lapse-clock.ts | 16 + .../source/routine/push-dispatch-clock.ts | 14 + app/core-app/core-app.ts | 81 + .../source/handler/alias-shift-arranger.ts | 53 + .../source/handler/grant-gadget-arranger.ts | 40 + .../source/handler/grant-stock-arranger.ts | 65 + .../source/handler/member-admit-arranger.ts | 243 + .../source/handler/open-crate-arranger.ts | 72 + .../source/loader/tls-express-boot-spec.ts | 223 + .../source/loader/tls-express-boot.ts | 58 + .../source/manager/crate-odds-ledger.ts | 15 + .../source/router/account-link-gateway.ts | 20 + .../source/router/account-retire-gateway.ts | 20 + .../source/router/ad-bounty-gateway.ts | 20 + app/core-app/source/router/admit-gateway.ts | 29 + .../source/router/alias-shift-gateway.ts | 19 + .../source/router/bazaar-checkout-gateway.ts | 20 + .../source/router/campaign-clear-gateway.ts | 20 + .../source/router/campaign-gateway.ts | 20 + .../router/claim-spinner-bounty-gateway.ts | 20 + .../source/router/client-pack-gateway.ts | 20 + .../source/router/craft-stock-gateway.ts | 19 + .../source/router/enroll-callback-gateway.ts | 19 + app/core-app/source/router/enroll-gateway.ts | 23 + .../router/expand-gadget-bay-gateway.ts | 19 + .../source/router/gadget-refine-gateway.ts | 20 + .../source/router/new-member-setup-gateway.ts | 20 + .../source/router/occupancy-check-gateway.ts | 18 + .../source/router/open-crate-gateway.ts | 20 + .../router/open-instant-crate-gateway.ts | 20 + .../source/router/open-parcel-gateway.ts | 20 + .../source/router/park-crate-gateway.ts | 20 + app/core-app/source/router/primer-gateway.ts | 31 + .../source/router/push-opt-gateway.ts | 20 + .../source/router/read-cycle-gateway.ts | 20 + .../source/router/read-delta-gateway.ts | 19 + .../source/router/read-duel-stage-gateway.ts | 20 + .../source/router/read-host-health-gateway.ts | 20 + .../source/router/read-masterdata-gateway.ts | 20 + .../source/router/shatter-gadget-gateway.ts | 20 + .../router/storefront-checkout-gateway.ts | 20 + .../source/router/swap-azure-token-gateway.ts | 25 + .../router/vessel-gadget-loadout-gateway.ts | 21 + .../source/router/vessel-line-gateway.ts | 21 + .../source/router/vessel-refine-gateway.ts | 20 + .../source/service/account-link-usecase.ts | 84 + .../source/service/account-retire-usecase.ts | 11 + .../source/service/ad-bounty-usecase.ts | 16 + app/core-app/source/service/admit-usecase.ts | 123 + .../source/service/alias-shift-usecase.ts | 24 + .../source/service/bazaar-checkout-usecase.ts | 66 + .../source/service/campaign-clear-usecase.ts | 121 + .../source/service/campaign-usecase.ts | 40 + .../service/claim-spinner-bounty-usecase.ts | 51 + .../source/service/client-pack-usecase.ts | 23 + .../source/service/craft-stock-usecase.ts | 59 + app/core-app/source/service/enroll-usecase.ts | 39 + .../service/expand-gadget-bay-usecase.ts | 22 + .../source/service/gadget-refine-usecase.ts | 41 + .../service/new-member-setup-usecase.ts | 64 + .../service/open-crate-instant-usecase.ts | 33 + .../source/service/open-crate-usecase.ts | 22 + .../source/service/open-parcel-usecase.ts | 52 + .../source/service/park-crate-usecase.ts | 42 + app/core-app/source/service/primer-usecase.ts | 31 + .../source/service/push-opt-usecase.ts | 8 + .../source/service/read-cycle-usecase.ts | 10 + .../source/service/read-delta-usecase.ts | 30 + .../source/service/read-duel-stage-usecase.ts | 10 + .../service/read-host-health-usecase.ts | 11 + .../source/service/read-masterdata-usecase.ts | 25 + .../source/service/shatter-gadget-usecase.ts | 28 + .../service/storefront-checkout-usecase.ts | 45 + .../service/swap-azure-token-usecase.ts | 33 + .../service/vessel-gadget-loadout-usecase.ts | 23 + .../source/service/vessel-line-usecase.ts | 17 + .../source/service/vessel-refine-usecase.ts | 41 + app/duel-app/duel-app.ts | 42 + .../source/module/duel-runner-factory.ts | 83 + .../source/module/duel-runner-refork.ts | 3 + app/duel-runner/duel-runner.ts | 44 + .../source/loader/duel-runner-boot.ts | 17 + .../source/manager/battle-pocket-ledger.ts | 44 + .../module/duel-arena-unit/bout-state-kind.ts | 3 + .../duel-arena-unit/close-bout-verdict.ts | 70 + .../module/duel-arena-unit/duel-arena.ts | 382 ++ .../module/duel-arena-unit/duel-spec.ts | 10 + .../duel-arena-unit/duel-stream-gate.ts | 29 + .../duel-socket-io-spec.ts | 5 + .../duel-socket-io-module/duel-socket-io.ts | 99 + .../source/module/halt-clock/halt-clock.ts | 25 + .../parent-pipe-unit/parent-pipe-arranger.ts | 12 + .../parent-pipe-unit/parent-pipe-keeper.ts | 15 + .../source/router/close-bout-gateway.ts | 4 + .../source/router/close-phase-gateway.ts | 5 + .../source/router/enter-bout-gateway.ts | 4 + .../source/router/forward-stream-gateway.ts | 4 + .../source/router/leave-bout-gateway.ts | 4 + .../source/service/close-bout-usecase.ts | 13 + .../source/service/close-phase-usecase.ts | 12 + .../source/service/enter-bout-usecase.ts | 12 + .../source/service/forward-stream-usecase.ts | 12 + .../source/service/leave-bout-usecase.ts | 13 + app/nexus-app/nexus-app.ts | 56 + .../member-battle-integrity-arranger.ts | 46 + .../handler/member-battle-integrity-policy.ts | 56 + .../handler/member-battle-payload-arranger.ts | 81 + .../handler/occupancy-expired-arranger.ts | 10 + .../source/handler/pairing-arranger.ts | 98 + app/nexus-app/source/loader/nexus-hub-boot.ts | 14 + .../duel-arena-spawn-keeper.ts | 28 + .../duel-arena-spawn-spec.ts | 17 + .../member-socket-keeper.ts | 4 + .../member-socket-unit/member-socket-spec.ts | 15 + .../module/socket-io-module/socket-io-app.ts | 92 + .../socket-io-module/socket-io-config.ts | 7 + .../router/enter-bracket-pairing-gateway.ts | 6 + .../router/enter-casual-pairing-gateway.ts | 6 + .../router/leave-bracket-pairing-gateway.ts | 6 + .../router/leave-casual-pairing-gateway.ts | 6 + .../source/routine/bracket-pairing-clock.ts | 24 + .../source/routine/casual-pairing-clock.ts | 24 + .../source/routine/occupancy-clock.ts | 21 + .../service/enter-bracket-pairing-usecase.ts | 12 + .../service/enter-casual-pairing-usecase.ts | 12 + .../service/leave-bracket-pairing-usecase.ts | 12 + .../service/leave-casual-pairing-usecase.ts | 12 + app/overseer-app/overseer-app.ts | 28 + .../source/loader/overseer-boot.ts | 30 + .../source/loader/overseer-command-boot.ts | 187 + .../source/module/console-line-unit.ts | 42 + .../source/module/overseer-verb.ts | 159 + .../source/service/grant-gadget-usecase.ts | 30 + .../source/service/grant-relic-usecase.ts | 27 + .../source/service/grant-vessel-usecase.ts | 28 + config/axios-config.ts | 8 + config/duel-host-spec.ts | 1 + config/enroll-unit-spec/enroll-spec.ts | 14 + config/enroll-unit-spec/facebook-auth-spec.ts | 2 + config/enroll-unit-spec/google-auth-spec.ts | 17 + config/gateway-spec.ts | 7 + config/host-spec.ts | 50 + config/play-baseline-spec.ts | 47 + config/pocket-link-spec.ts | 23 + config/runtime-spec.ts | 15 + config/sql-link-spec.ts | 13 + config/trace-store-spec.ts | 9 + data/dto/core-packet.ts | 74 + data/dto/duel-packet.ts | 34 + data/dto/hub-packet.ts | 178 + data/dto/nexus-hub-packet.ts | 10 + data/dto/server2server-dto.ts | 20 + data/entity/nexforge_db_record.ts | 509 ++ data/model/duel-shape/duel-arena-shape.ts | 20 + data/model/duel-shape/duel-runner-shape.ts | 7 + data/model/member-shape/BattleShape.ts | 12 + data/model/member-shape/BotCombatantShape.ts | 12 + data/model/member-shape/CombatantShape.ts | 11 + data/model/member-shape/MemberShape.ts | 19 + data/model/member-shape/SharedBoutShape.ts | 5 + data/model/member-shape/member-data-shape.ts | 99 + data/model/play-shape/PlayShape.ts | 20 + data/model/play-shape/play-data-shape.ts | 145 + .../model/pocket-shape/member-pocket-shape.ts | 25 + docker-compose-build.yml | 63 + docker_compose/db_server/docker-compose.yml | 27 + docker_compose/main_server/docker-compose.yml | 25 + docker_compose/mem_server/docker-compose.yml | 19 + .../resource_server/docker-compose.yml | 11 + handler/band-shift-arranger.ts | 36 + handler/campaign-clear-policy.ts | 103 + handler/close-bout-grant-bounty-arranger.ts | 228 + handler/grade-shift-arranger.ts | 45 + handler/phase-bounty-ratio.ts | 14 + .../ad-bounty-keeper/ad-bounty-keeper-spec.ts | 18 + .../ad-bounty-keeper/ad-bounty-keeper.ts | 3 + .../bracket-pairing-keeper.ts | 157 + .../bracket-pairing-spec.ts | 30 + .../campaign-bout-keeper.ts | 40 + .../campaign-pocket-keeper.ts | 117 + .../casual-pairing-keeper.ts | 88 + .../casual-pairing-spec.ts | 5 + .../cycle-keeper/cycle-keeper-spec.ts | 1 + .../cycle-keeper/cycle-keeper.ts | 29 + .../duel-arena-keeper-spec.ts | 2 + .../duel-arena-keeper/duel-arena-keeper.ts | 32 + .../duel-stage-keeper/duel-stage-keeper.ts | 54 + .../feat-trace-keeper/feat-trace-keeper.ts | 31 + .../feat-trace-keeper/feat-trace-spec.ts | 14 + .../featured-offer-keeper.ts | 59 + .../host-hold-keeper/host-hold-keeper.ts | 18 + .../host-hold-keeper/host-hold-spec.ts | 1 + .../ip-block-keeper/ip-block-keeper.ts | 24 + .../ip-block-keeper/ip-block-spec.ts | 2 + .../ip-occupancy-keeper-spec.ts | 22 + .../ip-occupancy-keeper.ts | 76 + .../master-pocket-keeper.ts | 470 ++ .../master-pocket-spec.ts | 151 + .../member-pocket-keeper.ts | 422 ++ .../member-pocket-spec.ts | 401 ++ .../occupancy-credit-policy.ts | 57 + .../occupancy-keeper/occupancy-keeper-spec.ts | 50 + .../occupancy-keeper/occupancy-keeper.ts | 146 + .../pocket-keeper/pairing-scan-lock-policy.ts | 10 + manager/pocket-keeper/pairing-scan-lock.ts | 25 + .../parcel-keeper/parcel-keeper-spec.ts | 17 + .../parcel-keeper/parcel-keeper.ts | 70 + .../push-keeper/push-keeper-spec.ts | 22 + .../pocket-keeper/push-keeper/push-keeper.ts | 69 + .../refine-pocket-keeper.ts | 53 + .../refine-pocket-spec.ts | 34 + manager/pocket-shape-base/BlobPocketStore.ts | 23 + manager/pocket-shape-base/HashPocketStore.ts | 34 + .../master-data-keeper/avatar-master-store.ts | 9 + .../band-bounty-master-store.ts | 9 + .../master-data-keeper/band-master-store.ts | 9 + .../baseline-master-store.ts | 9 + .../bazaar-offer-master-store.ts | 9 + .../bot-gadget-master-store.ts | 9 + .../master-data-keeper/bot-master-store.ts | 9 + .../bot-vessel-master-store.ts | 9 + .../campaign-bot-master-store.ts | 9 + .../campaign-master-store.ts | 9 + .../craft-recipe-master-store.ts | 9 + .../crate-band-master-store.ts | 9 + .../crate-group-odds-master-store.ts | 9 + .../crate-odds-master-store.ts | 9 + .../duel-default-bounty-store.ts | 9 + .../duel-loss-bounty-store.ts | 9 + .../duel-win-bounty-store.ts | 9 + .../master-data-keeper/feat-master-store.ts | 9 + .../master-data-keeper/gadget-master-store.ts | 9 + .../gadget-refine-cost-store.ts | 9 + .../gadget-refine-odds-store.ts | 9 + .../gadget-shatter-master-store.ts | 9 + .../gadget-trait-master-store.ts | 9 + .../master-data-keeper/relic-master-store.ts | 9 + .../master-data-keeper/stage-store.ts | 9 + .../master-data-keeper/stock-band-store.ts | 9 + .../trace-code-master-store.ts | 9 + .../master-data-keeper/vessel-master-store.ts | 9 + .../vessel-refine-shard-store.ts | 9 + .../member-data-keeper/member-alias-store.ts | 17 + .../member-data-keeper/member-avatar-store.ts | 15 + .../member-bazaar-trace-store.ts | 15 + .../member-data-keeper/member-block-store.ts | 15 + .../member-bracket-store.ts | 15 + .../member-campaign-store.ts | 17 + .../member-checkout-store.ts | 14 + .../member-consent-store.ts | 14 + .../member-data-keeper/member-core-store.ts | 15 + .../member-data-keeper/member-crate-store.ts | 15 + .../member-data-keeper/member-feat-store.ts | 16 + .../member-data-keeper/member-gadget-store.ts | 15 + .../member-data-keeper/member-parcel-store.ts | 18 + .../member-pocket-persist-store.ts | 14 + .../member-data-keeper/member-relic-store.ts | 20 + .../member-data-keeper/member-side-store.ts | 15 + .../member-data-keeper/member-store.ts | 15 + .../member-data-keeper/member-ticket-store.ts | 14 + .../member-data-keeper/member-trace-store.ts | 17 + .../member-data-keeper/member-vessel-store.ts | 15 + .../ops-data-keeper/ip-block-store.ts | 14 + .../sql-keeper/ops-data-keeper/stage-store.ts | 1 + .../trace-store-keeper/host-trace-keeper.ts | 22 + .../trace-store-keeper/member-trace-keeper.ts | 26 + .../trace-store-keeper/trace-store-spec.ts | 11 + middleware/bounce-gate.ts | 15 + middleware/client-license-gate.ts | 25 + .../gate-spec/license-auth-md-config.ts | 3 + middleware/gate-spec/occupancy-gate-spec.ts | 18 + .../gate-spec/socket-occupancy-gate-spec.ts | 11 + middleware/host-health-gate.ts | 25 + middleware/host-license-gate.ts | 26 + middleware/hub-occupancy-gate.ts | 33 + middleware/ip-occupancy-gate.ts | 18 + middleware/socket-member-gate.ts | 41 + middleware/socket-occupancy-gate.ts | 41 + modules/admit-jwt-unit/admit-jwt-spec.ts | 21 + modules/admit-jwt-unit/admit-jwt-unit.ts | 85 + .../admit-ticket-unit/admit-ticket-unit.ts | 8 + modules/cipher-unit/key-weave-unit.ts | 59 + modules/cipher-unit/scramble-shape.ts | 4 + modules/cipher-unit/scramble-unit.ts | 63 + modules/document-link/document-link.ts | 13 + modules/duel-jwt-unit/duel-jwt-spec.ts | 10 + modules/duel-jwt-unit/duel-jwt-unit.ts | 50 + modules/duel-load-balancer/duel-host-pick.ts | 50 + .../duel-load-balancer-spec.ts | 5 + .../duel-load-balancer/duel-load-balancer.ts | 53 + .../duel-occupancy-count.ts | 4 + modules/duel-ticket-unit/duel-ticket-unit.ts | 8 + modules/enroll-unit/anon-auth-unit.ts | 31 + modules/enroll-unit/facebook-auth-unit.ts | 35 + modules/enroll-unit/google-auth-unit.ts | 32 + modules/id-mint-unit/id-mint.ts | 5 + modules/kit-unit/chance-unit.ts | 5 + modules/kit-unit/request-ip.ts | 17 + modules/kit-unit/secret-equal.ts | 10 + modules/openapi-unit/openapi-spec.ts | 18 + modules/openapi-unit/openapi-unit.ts | 7 + modules/parse-unit/json-reader.ts | 7 + modules/parse-unit/path-leaf-reader.ts | 5 + modules/parse-unit/yaml-reader.ts | 8 + modules/plain-auth-unit/plain-auth-unit.ts | 5 + .../pocket-link/pocket-boot-flush-policy.ts | 10 + modules/pocket-link/pocket-boot-flush.ts | 15 + modules/pocket-link/pocket-link.ts | 29 + modules/sql-unit/MasterRecordKeeperBase.ts | 16 + .../sql-unit/MemberPlayRecordKeeperBase.ts | 200 + modules/sql-unit/MemberRecordKeeperBase.ts | 142 + modules/sql-unit/sql-amount-bind.ts | 15 + modules/sql-unit/sql-link.ts | 57 + modules/storefront-unit/storefront-api.ts | 52 + .../storefront-unit/storefront-product-map.ts | 13 + .../storefront-receipt-policy.ts | 23 + modules/trace-unit/fatal-process-guard.ts | 20 + modules/trace-unit/fault-dispatch.ts | 21 + modules/trace-unit/host-label-keeper.ts | 12 + modules/trace-unit/stamp-log-printer.ts | 38 + modules/trace-unit/stamp-log.ts | 12 + modules/trace-unit/trace-file-writer.ts | 29 + .../trace-unit-spec/fault-dispatch-spec.ts | 26 + .../trace-unit-spec/trace-file-writer-spec.ts | 1 + package.json | 101 + redis-local/Dockerfile | 4 + redis-local/redis.conf | 1378 ++++++ redis/Dockerfile | 4 + redis/redis.conf | 2276 +++++++++ resource/private/firebase/.gitkeep | 0 resource/private/global_ssl/.gitkeep | 0 resource/private/google_key/.gitkeep | 0 resource/public/admin_page/.gitignore | 23 + resource/public/admin_page/package.json | 40 + resource/public/admin_page/public/favicon.ico | Bin 0 -> 3870 bytes resource/public/admin_page/public/index.html | 44 + resource/public/admin_page/public/logo192.png | Bin 0 -> 5347 bytes resource/public/admin_page/public/logo512.png | Bin 0 -> 9664 bytes .../public/admin_page/public/manifest.json | 25 + resource/public/admin_page/public/robots.txt | 3 + resource/public/admin_page/src/App.css | 52 + resource/public/admin_page/src/App.js | 20 + .../src/component/command/Command.css | 27 + .../src/component/command/Command.jsx | 104 + .../src/component/console/Console.css | 50 + .../src/component/console/Console.jsx | 28 + .../public/admin_page/src/component/index.js | 4 + .../src/component/navigation/Navigation.css | 55 + .../src/component/navigation/Navigation.jsx | 32 + resource/public/admin_page/src/index.jsx | 5 + .../src/pages/dashBoard/DashBoard.css | 85 + .../src/pages/dashBoard/DashBoard.jsx | 25 + resource/public/admin_page/src/pages/index.js | 1 + .../000c20a22c200bc14f269b6cae56a0ca.bundle | 3 + .../0087d86b9bd6a16f60ae00994179556d.bundle | 3 + .../0222cb564519e8ce64802db77dbfeb0d.bundle | 3 + .../02496e6db9dd1373c7b1e47a81bdc174.bundle | 3 + .../0268a10dacbfd3633dfc7a958bca719e.bundle | 3 + .../040717f82175be6b212a8f85d0d94abd.bundle | 3 + .../058bb14101e4e4431be2f39bfa5d1453.bundle | 3 + .../05d23240b31e89566f190f869a983c7b.bundle | 3 + .../0660d643b9483ee464e72622acfd2f81.bundle | 3 + .../06f10ced33343ca46d0ce9b13434694b.bundle | 3 + .../071e67540c8f0095433b9abf8c43d363.bundle | 3 + .../074714a3f0683231fb6b2efe5b80eaca.bundle | 3 + .../0759959f5d1bd39dda8e19d69fca0c24.bundle | 3 + .../07cf39258e9f13134753e56cad880dd4.bundle | 3 + .../0898aded60e814a1a219daa58e5c2c2f.bundle | 3 + .../09349b1dd18ddca0a46f87f614cbbbae.bundle | 3 + .../0946dccfa481a5395392701b6fbc83d3.bundle | 3 + .../0a9baf3ca7cdfc770f38b7975b68fbef.bundle | 3 + .../0bf11957dae33265ea8b339eb1920392.bundle | 3 + .../0c6cc8f66403685f644b0133104b943b.bundle | 3 + .../0cc18e6add137e5182c6a3c9cf70c5de.bundle | 3 + .../0d31735126b359d6986980aee64e0207.bundle | 3 + .../0d430fee81db5f1635f979ffba6167ba.bundle | 3 + .../0d85e99d7ddcbb07be1856c55935c450.bundle | 3 + .../0e7cc7ab411f698607c1cda077f51a43.bundle | 3 + .../0f058420fb6ea2f686eabca5786ce28b.bundle | 3 + .../0f065ebcc0d2ad00d60a15486e2cc473.bundle | 3 + .../0f40ef6c84de15c2146545761dcdcc32.bundle | 3 + .../0fca24ad01049cd43eb116726406596e.bundle | 3 + .../0fdb4d1652d816bdb7716fa660d7e84b.bundle | 3 + .../0ff78bc8f2ecf6d29d7d4ae1f4ad98e3.bundle | 3 + .../1121575ea83aba3b4efeb17aec480e3a.bundle | 3 + .../115dcdb9ebf98d80096f9a8d0afe4e27.bundle | 3 + .../1204ebe541503ecb74556cf89308fc79.bundle | 3 + .../13d52dfec9c449a4f4647b787f410ebb.bundle | 3 + .../13db5b25900ab6fbd5b0b23ef356e627.bundle | 3 + .../13f5f41e84d89c15a78b2c452ba0ba73.bundle | 3 + .../14b87532678240bcf97b6d570e8e4251.bundle | 3 + .../14bc4de314be9abf37b3131179234bfe.bundle | 3 + .../15837a506baba672a6679e572f534c8e.bundle | 3 + .../15b973382de3f9565e2fa45878200369.bundle | 3 + .../1627ec58a02423ff8ad9889d2f79929b.bundle | 3 + .../169038fb8072bba0d278a1185f609c38.bundle | 3 + .../17482e8856b038dbe07a351462d76071.bundle | 3 + .../17ebb3300543d7b11e5a1ea169c8de63.bundle | 3 + .../17fa10466aac3e8f7e891dc05458c2d0.bundle | 3 + .../181067db6ad28848bc0c86c8e605537d.bundle | 3 + .../184f9fec1687605c2c7a489373b2bb02.bundle | 3 + .../184ff4c050b3bf863440d1922152961c.bundle | 3 + .../1898f164bb316952fca2b36b033d348d.bundle | 3 + .../18efa6b269010736b837676c281ff104.bundle | 3 + .../1a1fba77c66a56561293636ba66795d8.bundle | 3 + .../1a4bfdb3d7b2f3d0c688b2d8d3153c4a.bundle | 3 + .../1a818550d701d5154d6013b486066ca1.bundle | 3 + .../1b2d83f98e2751a928de32886c57a8e6.bundle | 3 + .../1b93abd4b7224a274ee09a92ed142de9.bundle | 3 + .../1c0c488887fee4546ec46e79ab455282.bundle | 3 + .../1c9d4fdb8f3b72f59b44bebd48091572.bundle | 3 + .../1ca5e5d2d988dcffadbc2c86161f6b79.bundle | 3 + .../1d3afb129c662a91409e916684e009ad.bundle | 3 + .../1d5ce4e1092719f21148cd44aaad7cbd.bundle | 3 + .../1d77c2d2d82c35f2156ee39d161c7d96.bundle | 3 + .../1d8ee19f61b897a7718d76272f344ccf.bundle | 3 + .../1db79106c60ed7218b05ea454ba08714.bundle | 3 + .../1e9faa4630d9bc149dda630307fd791f.bundle | 3 + .../1f652d267d45d760e23031f38ca88e69.bundle | 3 + .../20401debb396cf8db96316a256b8e688.bundle | 3 + .../2066e4e2076572e689a63f464c03a121.bundle | 3 + .../2177438fbfb9a1eca1936f0de1424b56.bundle | 3 + .../2190689258faf0e0aa4ab26201e9680f.bundle | 3 + .../21b4f6829592c093031b95d79fbd6dcc.bundle | 3 + .../21c140de93b1045f8bfcb6dd1646ae20.bundle | 3 + .../2212cc2810742e79973e30f8e30c386e.bundle | 3 + .../22e9c79dca019cec4cbecde9f3f12383.bundle | 3 + .../23347c565110cd490fe51d5fea7507bb.bundle | 3 + .../2388bf7a01a586e0527762c9e4268048.bundle | 3 + .../23f98778050fc8ed67d72051bb98ae9e.bundle | 3 + .../242355d71dfa6f2fbb5f656e619016f5.bundle | 3 + .../249c3ae135e020a4ffcf81be9b60c77f.bundle | 3 + .../257e19c808d0144c657ba22f132c43ff.bundle | 3 + .../25e3f7f5b113458d258755e516459f36.bundle | 3 + .../26feb311b852e25eb3821dab302c8af2.bundle | 3 + .../270a32df6c15b0d2319396cd0e786f0b.bundle | 3 + .../27957d2ea775b4e647113dd9893d4f53.bundle | 3 + .../27cf0ad0996adce1d810f0eaa33a90fd.bundle | 3 + .../28a1071a613bb3252f58b08806011f99.bundle | 3 + .../28f5630a99e8ea4fa2824d570a93afce.bundle | 3 + .../29e97f0052980afc6ccc17ab6b84309f.bundle | 3 + .../2a87a94f891630ad79b524ea7f67d987.bundle | 3 + .../2b86e365f186f797b231c648c4084813.bundle | 3 + .../2c59ede472361628f479e27307b8c4e1.bundle | 3 + .../2c8cb3f0f8b29c242044a06d45802208.bundle | 3 + .../2cb3644bf0b75ee54d52a88339c07fbb.bundle | 3 + .../2e0538377ce38f47633d420971562dc6.bundle | 3 + .../2e0fc7b27af644d4ca9c555365d919b0.bundle | 3 + .../2e5ba1321c41188d98467ad93de7f7c9.bundle | 3 + .../2eb89e363cd72a45e1e215eefef9b329.bundle | 3 + .../2f08c2ead7a9cd4c198ee687bb02b398.bundle | 3 + .../2f9b06f0ce738ca5d038a1321c53ef5b.bundle | 3 + .../2fa203e0f1603d8a86abcfd147d6b8b8.bundle | 3 + .../3039b8aa3a9d0188b85329d8b34db45c.bundle | 3 + .../306a0629fec572090c239fc6221ac818.bundle | 3 + .../30fb22da1b914e28c51db87d59a57c9d.bundle | 3 + .../321551daa71eb0b370da8b4786fcb10a.bundle | 3 + .../327cd802e87c87669455177391f10d98.bundle | 3 + .../3346a5ea8109a1b4b0a87e7f008b8071.bundle | 3 + .../334acd221397bf0f3e6c761d22322fb3.bundle | 3 + .../33a9da239c36af6dff70dce8e49e1e51.bundle | 3 + .../33e4ab3a9c4d8197ddece0007301e928.bundle | 3 + .../340c0c72ae372b5ecfd6198a75d4fb28.bundle | 3 + .../341e4ab4b3102cc844eb496b791d1386.bundle | 3 + .../346887503f056c9fa40f8759f2270921.bundle | 3 + .../34c3e1e6973b6024d9dece7be682d60a.bundle | 3 + .../351646f28c5488d36b3c3928ba364b24.bundle | 3 + .../355eff79683baaf5039ebe81d2887660.bundle | 3 + .../36099b95dc139e03cdd91255879efaa1.bundle | 3 + .../364b2d76d4d343f2e838cc5ec6021b9a.bundle | 3 + .../36de2b3099d63e81de1bda500e8213a1.bundle | 3 + .../380b465bd68029aafd3530135cca7978.bundle | 3 + .../38c8b56699ab43781b5f39403d555ac1.bundle | 3 + .../38f5b4416b745a52c0f29b8e6d2e3b3a.bundle | 3 + .../3903e747cf14c1501721107855ead8d9.bundle | 3 + .../39423acd77dbd5b7f336351e5114d0e5.bundle | 3 + .../3955662debfaf653e2e7cd7d5d73ad4b.bundle | 3 + .../39c90b73199ac8863a6e853fb88f3007.bundle | 3 + .../3a7db4e26c8101adc7bec4bdf25acf0a.bundle | 3 + .../3a8bd1fc43d5e672ea45519d8952b479.bundle | 3 + .../3b1e5a9ba6efab74c625efe2edd7e7b6.bundle | 3 + .../3b3bd9f57222687014c531ceaedd147d.bundle | 3 + .../3b67eaf699595d94ea56d8dbc80ccf27.bundle | 3 + .../3d0caa5885771b63d82227438f20251c.bundle | 3 + .../3d3f706bd68d9a049809d24d95e08b10.bundle | 3 + .../3d614584ea110e6df1fff4c1d7e1ced2.bundle | 3 + .../3de8e88cc128b365ce016a5789ddc07f.bundle | 3 + .../3e36ffdf49773050314f7431f9b983ae.bundle | 3 + .../3f81e71d0aeb077e828986c95ad8688b.bundle | 3 + .../3fb903e024fd5400861f12a1c234a3fd.bundle | 3 + .../3fccdc8c6aa87cb0dc6cd7bf70d58be2.bundle | 3 + .../3ff625aaab19a9171e0b3de3e8486585.bundle | 3 + .../407b620c8dc23eefd650c5486e0b0cb8.bundle | 3 + .../41236a2c279da83eb523ae57f5164c31.bundle | 3 + .../41e1ab85f1c532d49f2fbe703e6998d6.bundle | 3 + .../424bde686a2d040bb42b2695eec79993.bundle | 3 + .../428954cf45000dfc14b553ed4c43a30b.bundle | 3 + .../42fb4cc61ee6912f19b08f75d95f9ac3.bundle | 3 + .../43fff9e88ede3bd7f5c0e6b0cc891e8f.bundle | 3 + .../4400440ee6a2fc0cd7dbb138cb4815c0.bundle | 3 + .../4428ffe17f11815916fbfbf4ece90c25.bundle | 3 + .../44583da2cd73f05defaff798df4a028f.bundle | 3 + .../446fea04036e0ff08dca288ce42a7c27.bundle | 3 + .../4538477184b9113e259e74ea4d509d92.bundle | 3 + .../45b3f5f3158d9ff5f073dad755fd24c9.bundle | 3 + .../45d64366d622ec73e87effd29affcc97.bundle | 3 + .../467ec9bfddea2bae81788657436626a9.bundle | 3 + .../4686ddd9e588645e5e547ac6438eb92c.bundle | 3 + .../4829a54613d112b202f59b5488fefc8f.bundle | 3 + .../488ab2f692a64d9c0266a0a891a6de1e.bundle | 3 + .../488b9e9a58fe421831e72d7a27316ab5.bundle | 3 + .../488f96124a155ebba38572c4a453c2f1.bundle | 3 + .../4901515224ef9cd8900ba8eb7a7ca1b0.bundle | 3 + .../49b54b87ce5cf9e12879e868c1befb1e.bundle | 3 + .../49f63cd849b7a1a7ab82dae8718ddab0.bundle | 3 + .../4a9143e92e14b74298e4b9be4d31d6f9.bundle | 3 + .../4ae4b85f2f857d6c165f3b289a7acdd2.bundle | 3 + .../4b553c49bdfa15b7554bb6bd74ffd50a.bundle | 3 + .../4b719bfa4d56604bacb7b8ac353f6a10.bundle | 3 + .../4c101c5deebaffea232f2efb9298b0a2.bundle | 3 + .../4cbdefb4aaab52cdd845f1d3cd195b5a.bundle | 3 + .../4d5959b65eefeabe33c322d95220a91b.bundle | 3 + .../4e454ca051847cf71819e10b92b7a8ac.bundle | 3 + .../4e5dcf0a9e08372d02e83b86c243b673.bundle | 3 + .../4e9b6fa19161c3c38ff728e0e0a91427.bundle | 3 + .../4f48524fed2956b2d3c7805cf92e01ce.bundle | 3 + .../4faf8c0958047e25d26c3c9662b4051f.bundle | 3 + .../4fd5b68acbd482a7bb9722dedf12e3ec.bundle | 3 + .../4fe4ece4de8dbb8c37d35f172ed9fe72.bundle | 3 + .../50d3a3fcb68d0101fcd034bacc8b64b9.bundle | 3 + .../50ea45fc996b20afb63eec4845f2003d.bundle | 3 + .../512791c190d71a8fc0cf0c1e1a43ac38.bundle | 3 + .../51e71ed4d4006faea58e269124e4dc07.bundle | 3 + .../52a6a5256638611eff56f421c3c51807.bundle | 3 + .../52c83e2e59b8c1b544be64950b684cf9.bundle | 3 + .../52d547b2e1107c8021e22f2794028b96.bundle | 3 + .../52e972bc354baafd06c7cee990cd6a37.bundle | 3 + .../53363ab6875701684530a32b786c70d6.bundle | 3 + .../534e10974c4fd09dc016d5d53d9a7f93.bundle | 3 + .../53b2bdfd42325ddd8f4c770135588af6.bundle | 3 + .../53dddf2c7ba9faa4775b1f068fafeb25.bundle | 3 + .../5422be348f4a049153ff98fe392f391d.bundle | 3 + .../54aeda9e3a48a6fa17902ff91cb59c48.bundle | 3 + .../5524619133ab0fd12fa2440078e74016.bundle | 3 + .../561647845f89c11d1d8650b64f5c799f.bundle | 3 + .../567ac5ebd4a96b871f0a92eb9af38dc4.bundle | 3 + .../5794dc5c4d0b22345adddab100737ebd.bundle | 3 + .../57de1bd6cb70b43455667bdcb7943269.bundle | 3 + .../588090de7ef6042b41f4b4edd0bcbd32.bundle | 3 + .../58c77d29e23c3ac3ae4f85a2d191c10b.bundle | 3 + .../58cd8a7c53ef07814cd21682235ca886.bundle | 3 + .../591dfdd6900ef1df7e0feb6bda421af1.bundle | 3 + .../59238f1cf68f6ce858bb3d89e553c40c.bundle | 3 + .../598dea29469dd230ba2092e3bdab4971.bundle | 3 + .../59963ec4067ab23bcd22c8c9dc69a970.bundle | 3 + .../59aa47c2d1a78c8d4003f36c21c85132.bundle | 3 + .../59c9abf11814910a7b441ef55c3c932b.bundle | 3 + .../59da93b2707b1ce25462254d2ffb1750.bundle | 3 + .../59dd116b46a82eb432383ca84f841562.bundle | 3 + .../5a947a82f216ac4dc1ba2e0bfef5c976.bundle | 3 + .../5b2d0125100779775a2c14e9765a683e.bundle | 3 + .../5b3fdab7777ffd4e8d90087c455a10a2.bundle | 3 + .../5bc32f28e9a4c5b5d7a124b3b9613843.bundle | 3 + .../5c4e265f4fe8862fdac51db6c09e7bf6.bundle | 3 + .../5c61879688899a2e47309ed8601382cd.bundle | 3 + .../5cc31579ce2ae289b71e621955d072e4.bundle | 3 + .../5d50c8fad030aa2bc6f55bf19503466a.bundle | 3 + .../5ee0d501e717d1eb6d44dc0a4cd2c216.bundle | 3 + .../5f0752e55986bfc09e41a69c96e13953.bundle | 3 + .../5f2cb1bad7d2f903f5af6d09be79fdfc.bundle | 3 + .../5f76daf8df4cfe35769d93685ec29fd8.bundle | 3 + .../5fce3ab83f3426b53e52d1648ed55ecd.bundle | 3 + .../5fd1253a793db5ca9399d49039eedb85.bundle | 3 + .../5fde474644c89925f78ee0a3ad0c7167.bundle | 3 + .../5ffa0f34af9ac3301b9418b541d6968b.bundle | 3 + .../60273f2c3b651dfbf3845db012b630e3.bundle | 3 + .../60f657747a5df145bad2c29ca9edc278.bundle | 3 + .../61048e99e9db4b20704ee91eea71db28.bundle | 3 + .../611344a22093d6b4a72b89628d51aebc.bundle | 3 + .../613480d49a3701a27ac24f2a59438ec9.bundle | 3 + .../61a6691cea9b6aa45a176e8259071993.bundle | 3 + .../61c028e61a12166b87e6e203247e0cb1.bundle | 3 + .../61cdc8fa4d0f0755491d291e3d414156.bundle | 3 + .../624c5dcfc0ace3e6b5251b684fb359e3.bundle | 3 + .../625c8b4fa9b67a24bb958cda3c2fe346.bundle | 3 + .../62a559c0c49c347f033d9969474955cc.bundle | 3 + .../63292a912a6a0372dfa4cf2b7d3bfaa1.bundle | 3 + .../638bfe8c84e8b49954f406a0d00d9477.bundle | 3 + .../6439586c170e7bed3033c6efb7b5f127.bundle | 3 + .../64746bd7b69dacb68af90531fe9ae2b9.bundle | 3 + .../64a02f82107e0f31a8bc120895bdb4e9.bundle | 3 + .../64d301c700fb0bbbb85a3d5b8fd98fb4.bundle | 3 + .../65434d0ce26edd900fb6d4e04d0cd648.bundle | 3 + .../65500b047aeb3edac3be031cd12f0976.bundle | 3 + .../65e7b4df03b86dd574248056070f7341.bundle | 3 + .../6648322a0a9e1c5bf411521902912f74.bundle | 3 + .../6651befb613357b33c396d0feed21da6.bundle | 3 + .../66a63df26fba3d79fd2f2793002bd84c.bundle | 3 + .../67642f1a7d3ff6131f5b7b5eb4a7b06f.bundle | 3 + .../684dcc8a988cae7f8cc9a50b6f4b5d06.bundle | 3 + .../684ff8f1810e216e005d24415704135b.bundle | 3 + .../6891762da1d293025f52d4201daffd8b.bundle | 3 + .../68becc4478f96ab3bbd3684a2b073c92.bundle | 3 + .../693ded411298063e45f78fadbe08101b.bundle | 3 + .../6aa7a8f3dad7df97abf59e3390ba093e.bundle | 3 + .../6adbd4d61d40c721f5fcdc376fb4a0fc.bundle | 3 + .../6b7e9ae05e3bdc1e6404d04ae48801c2.bundle | 3 + .../6c4dd69793c4b413b2941a788e4e0442.bundle | 3 + .../6c7a68a631659f32825b0c02919d8315.bundle | 3 + .../6cafa0428e912fd34cb11e720b57ee5c.bundle | 3 + .../6cc3d615aa3c6897fa760de4617d9f4d.bundle | 3 + .../6d256d675032b53375f0f174fafcfb78.bundle | 3 + .../6d2d855743f8623707ccc0b9fa7b35fb.bundle | 3 + .../6d489116d21b5a3fadb14379f9838b1c.bundle | 3 + .../6eeb1e8736e498b589e854e06c811165.bundle | 3 + .../6f500842d23c0744a75debc384a89816.bundle | 3 + .../6fdaf7ea382921975e980d6c59641639.bundle | 3 + .../70033cdf37c25deeee10e3c6d8d67119.bundle | 3 + .../70318d85ca1dd3f99f894e846c785c80.bundle | 3 + .../703e53d5724dcf25add177bc0fc009ae.bundle | 3 + .../70512d80b4d4446c984ec437c073ef28.bundle | 3 + .../7091ed9cf189e7f851bc4441efd97f58.bundle | 3 + .../719367eecf4ddce830cf9885da463be0.bundle | 3 + .../724822f703caa06b7e1bb8cc110a1740.bundle | 3 + .../7257a867952c9a0f5ad0839d9ea1285d.bundle | 3 + .../726d36d1ed278acc043ccdcd4835f012.bundle | 3 + .../73e215a012f2943e62376b918414cd39.bundle | 3 + .../74f6a90084b358528fe8600a909aaa4f.bundle | 3 + .../75640a8c409ee43862817c3697355785.bundle | 3 + .../75807ba142dfe571ccfc81ed5980fc57.bundle | 3 + .../75aca1502eea3cf21604d2a503240ae1.bundle | 3 + .../75edf43068546a1d5910a7a52bb0e7eb.bundle | 3 + .../76143826d8d2c6efdb60c85036655355.bundle | 3 + .../7632b5e7487684b5657582d668ff7454.bundle | 3 + .../76c1a4a66d14cda7e9736e97d42975e3.bundle | 3 + .../76e63c3e1bf1efdd258bc352a9902c7a.bundle | 3 + .../76feee4ade04704293f289a22914d279.bundle | 3 + .../786e49703be6402d7225b2f0269dc8e3.bundle | 3 + .../7890460bf84a842c4b904a3e177325b6.bundle | 3 + .../78beb3c47734561419811fc321bbdeda.bundle | 3 + .../78f102ae97f80163134a1db187b78607.bundle | 3 + .../79378838b3b38b5be1edf40cf1c33927.bundle | 3 + .../798c18c057c3a47ea62b8dea31e032cc.bundle | 3 + .../799b0153e6ff2cf33afd558c3de6b463.bundle | 3 + .../7a38442fcf67799e379f969bc9d61d16.bundle | 3 + .../7a48dfa16f0c0086047ef5603dc8e560.bundle | 3 + .../7af50e07d67e2244b10eb0cbb3a3a498.bundle | 3 + .../7b215e251a1ac2b22fb73a73e1b45f66.bundle | 3 + .../7b416c929633003b271bf77a64e8a7e5.bundle | 3 + .../7ccd2e288fc992afebfe4106606c0847.bundle | 3 + .../7d7073e000764ee8299c0aaf118afb6e.bundle | 3 + .../7d8e2ff8ce1090a7f23f6de7c29b9028.bundle | 3 + .../7e77819523879c4e23028ba5fb4dc502.bundle | 3 + .../7f78dd706c112a5b8fa4942411864128.bundle | 3 + .../7fcb10bbe3d89460072902c6af8ab8e2.bundle | 3 + .../801215800ecbb0235a9e19c9f5d629c6.bundle | 3 + .../806e4a90bc6392d0a446883cdca2c53c.bundle | 3 + .../814c73ea8c49668d36bc9097322a4516.bundle | 3 + .../834bccc9515405a47e06f0cb20ddf894.bundle | 3 + .../834c88817cecf58cf71724851194f65e.bundle | 3 + .../8429607746268154f4a2c560fd0e3886.bundle | 3 + .../84951cb18491be20fbb63596a16ce1f4.bundle | 3 + .../84ab3826b308923d6c737cdb39e8ce22.bundle | 3 + .../8511c01cf0fb9b3d86f731e49ea6a013.bundle | 3 + .../851bad9c631c9431672000fc8dccaccc.bundle | 3 + .../85a803eeef735d5b076ff87968b12307.bundle | 3 + .../865a0e2988eeb13e9cd00cc2d91b9491.bundle | 3 + .../86d416f935f97f031c76c75733bd18b2.bundle | 3 + .../86f1b8fd0a6e6720a0f7eccbe4626c07.bundle | 3 + .../87bca01a76ef205fa840826ae60e8b9e.bundle | 3 + .../88dceab91b628e4d16a3c750f2481221.bundle | 3 + .../8925832f2bbe38b0f303511eb80b232c.bundle | 3 + .../89467f5efd08f4c15a653d7b3811641f.bundle | 3 + .../894eec9c38ccece5d108d67d60432286.bundle | 3 + .../8a357a9737dfdc4fd3534facf130e8b3.bundle | 3 + .../8a6bfd0b943758fea7b8ea141d5fb545.bundle | 3 + .../8ac0fb641fd2821eff2dc2251148c7df.bundle | 3 + .../8b6a9d09fc58f59be2772abee6b6c31f.bundle | 3 + .../8b97832491a12022413503651e51d1f6.bundle | 3 + .../8c7bef873a991a711de4b44ecb0a2dcc.bundle | 3 + .../8c8801ede531147aa748a35858727f5d.bundle | 3 + .../8ca6d615f1c1ef872595f295166c7751.bundle | 3 + .../8ce00e0d0a533108bc170b65b8c9ff17.bundle | 3 + .../8ce1de61cd200bf46f2f593d1a32cf46.bundle | 3 + .../8d5d06d9dbcf832ec6f87622135bd46e.bundle | 3 + .../8da42f12bd5cafec4b8b21661d69a600.bundle | 3 + .../8dbabe36ad498920c39bcaa431d3720a.bundle | 3 + .../8df2ea253f4442f148fb37925ecf8230.bundle | 3 + .../8e42010fba499ce83547bd59ec160829.bundle | 3 + .../8e4eeb6ed920e87c6d49687ab96dd1e2.bundle | 3 + .../8ec230607f3c2ac29a7520593efc66c2.bundle | 3 + .../8ed569e9780e72b36c42972bf81acd14.bundle | 3 + .../8f3f572b3d4ec4d95072dafbd26bf350.bundle | 3 + .../8f6e3cc4f455cd813586773063a5c30f.bundle | 3 + .../8f7f7b500e8f0db0b94f22bbc4298d34.bundle | 3 + .../9003244ae50ccc3090ea547fa3e92281.bundle | 3 + .../908085ab897ca6708c8a0d5d2f927daf.bundle | 3 + .../911036b71aca5f4e36c6b5395e00f46a.bundle | 3 + .../91e3bf6bf7ed49f7850cfe5a502cae55.bundle | 3 + .../9295638a0cd08e7a09102e8c8d3bf9dd.bundle | 3 + .../92a60c311e854edba704e3c502549351.bundle | 3 + .../92b178c1ae6a78866253449bb5091c46.bundle | 3 + .../92f5df2b76e6f2f5c882d498887c740c.bundle | 3 + .../9335d55fd85f835aa17cde4410497b3e.bundle | 3 + .../943ae6f22b38e0467e7b584427c379e4.bundle | 3 + .../94d5c5ec88639bd16b5183ff0547a427.bundle | 3 + .../95211aef5b4bac9df9e696e83e3dc0f7.bundle | 3 + .../95a5282b75c4c532182a5094de285f32.bundle | 3 + .../965d84001b13747e73aac88153fd717e.bundle | 3 + .../968434fe0715f1d5d3421a7349828eca.bundle | 3 + .../96d8b6d08fd7ae668206029e617ae825.bundle | 3 + .../96f93304052ec0ff61e9352035585972.bundle | 3 + .../97d651e5336f047ccc7465ff4e7a3ef4.bundle | 3 + .../986f21816d43659ab19233a141781e18.bundle | 3 + .../9959921242774df4ab21f9b21c8505e6.bundle | 3 + .../9a5a945d56f01fe031624d3a66b9d07f.bundle | 3 + .../9b2ecb61c8fd66dcbd62d9e265f8aac4.bundle | 3 + .../9b46ad4d8cdfd0637f04e35b22b544f6.bundle | 3 + .../9b47a7e6395a198ab04bfe049673da4c.bundle | 3 + .../9c3a011b2a35c3ebd28dac182192f1d6.bundle | 3 + .../9cef34232bbf042e9c6b4217750bd09e.bundle | 3 + .../9d53faed37ccc928e9f70ac5c008b426.bundle | 3 + .../9d586bbe9b0ed1e63f36a828c60f97a4.bundle | 3 + .../9e8e2e0bbc5ac0990c521217e8ac1da1.bundle | 3 + .../9ed5b63c4fd1de7fa25dd8c13614ab94.bundle | 3 + .../9edef5311f481475064ef5820f22aee4.bundle | 3 + .../9f1d875c2598984db7bc59a6f15338ed.bundle | 3 + .../9f203fce6f391078ecbe8c0dd14e09cb.bundle | 3 + .../9f24e9e05e72d2f2e1ff9ce136e8cb49.bundle | 3 + .../a01a4734f72fd1a55264c2581880b0c8.bundle | 3 + .../a02fbde60718e9ef2260a629654edb69.bundle | 3 + .../a06749998bb32f321be7898ee16311e6.bundle | 3 + .../a0bb83b1fbfd48397dd6282234afeb86.bundle | 3 + .../a0ed3c7e113f884646327124b474c7d1.bundle | 3 + .../a151fd22a5dbc452da43fdecce7762b0.bundle | 3 + .../a1aa31d8de9c4f2153e50d89524ced3d.bundle | 3 + .../a1bfaa0a486232f2dbdffdd848bb0406.bundle | 3 + .../a1ffb7fd37c1854ccaf6a3588d8ed466.bundle | 3 + .../a20f0a0731e7dfe055b8d0abb64a8247.bundle | 3 + .../a2ea69bd45a897b0e5f44bda744f8c7a.bundle | 3 + .../a3170dc28f228be5fdd1eb38a02843ed.bundle | 3 + .../a34bed4b67088ec39f3defb5a421f179.bundle | 3 + .../a3a24bae6479adad3a368fa329a40841.bundle | 3 + .../a3d38bded3e709b4ae2779a613dea79c.bundle | 3 + .../a45b46e5348c2a305c12a90101a7a66e.bundle | 3 + .../a46066f3e05393d3f058e304adae9866.bundle | 3 + .../a49a90b77fc9db37107122244102ba9b.bundle | 3 + .../a4a7b93f25d653c42f6b2656c2771ebb.bundle | 3 + .../a4e7ded93d2dd4afa0d39fe154b0c913.bundle | 3 + .../a645cd7b4debf74b1d5f2654980285e9.bundle | 3 + .../a6fa993e723b8b5804fa0c69661ef902.bundle | 3 + .../a8466eb80185017edc7542c3db502c29.bundle | 3 + .../a96ac16ab483434f13e5e3688db76dd2.bundle | 3 + .../a96cf58223b429cc99a09c4f534a4682.bundle | 3 + .../a9e542dbdcb32754e2082e55034f506f.bundle | 3 + .../aa7bac9f52559202d3ebfd2e7e64c269.bundle | 3 + .../ab63bda370c1cc7589548f86ef7a2bcf.bundle | 3 + .../ac1190b5fe085e29ca66c16f87ef43bc.bundle | 3 + .../ac350b4aae94b008983960e4a0d79f1d.bundle | 3 + .../ac4f88f9c496ebb30b936fadd7bd9608.bundle | 3 + .../acd2501a6166d461a5b6d68bae9c7b9e.bundle | 3 + .../ad18b500db3bf832683d749eb0725bdd.bundle | 3 + .../ad1b62c0448f1da998c0c2091b5e4af8.bundle | 3 + .../ad3f2766ad3c1f90985c6b9183c83244.bundle | 3 + .../ad731f461e2a50e7cee51a90f157f280.bundle | 3 + .../ae99e3ad54241be49190ae97f600c52c.bundle | 3 + .../b06d16c2f77caf0d02c995b9706ad03a.bundle | 3 + .../b0d532f58cacc09150318b1a63d65156.bundle | 3 + .../b137f20358e9ae4806f2fe6f1c1c7d14.bundle | 3 + .../b1872715579e8858839abbcd072b5272.bundle | 3 + .../b259e6f803490d1e137bc83a926f5871.bundle | 3 + .../b29c09a870090df8400bc00838c5a76e.bundle | 3 + .../b3473e04adea1b53417260b85d023275.bundle | 3 + .../b39ed9df65a4e7dba165e381d61bb533.bundle | 3 + .../b3b3e57fb2f2f3f4733fb676b8da900a.bundle | 3 + .../b45a5de269249ba986cb5b3563533a5f.bundle | 3 + .../b4aedccaefd930e4e2f5cc16ad66c3c1.bundle | 3 + .../b6173419e1566b83737b649e58b18fab.bundle | 3 + .../b6208858ffca06b7b0e2566f78b4b544.bundle | 3 + .../b64f1587050fb1e8155a674ecf4832fb.bundle | 3 + .../b66c2aed9875fd706f6e74703294689c.bundle | 3 + .../b6800faa1e42d9e46049d6ada0db892a.bundle | 3 + .../b68567c661ca95ea66a5014de6af89c3.bundle | 3 + .../b6a2c61aa0b3bedbc9104333e7e5e560.bundle | 3 + .../b6b6f52e5052fea3cb4357617dea7483.bundle | 3 + .../b6df3df94fe102d1df5373e22b1e33bc.bundle | 3 + .../b73d40ebcddded7b9f181b74cca38b51.bundle | 3 + .../b8fe1bdf547f833991fb000f64f9fe48.bundle | 3 + .../b9b508d7fdcd6868e7f638013e5f946e.bundle | 3 + .../b9ee1f149d0da25815cb2b95d7d1e486.bundle | 3 + .../b9fef8ef1f3a5b4fcd10ad2b105ead43.bundle | 3 + .../ba7e1117f20065aa3311b84a701fd072.bundle | 3 + .../ba912d1a432f2bee0e1a9ccabc5ed545.bundle | 3 + .../bb88c66c115d78028340441375c5951b.bundle | 3 + .../bb93de231a039f364c2e6d2130ba60c3.bundle | 3 + .../bbd01cee6241e52a197bcf2197002b6e.bundle | 3 + .../bbee50bf3daf0032d9325ffed1c8ab1a.bundle | 3 + .../bc1d1f8a6d59f79f91fe71acdba2c990.bundle | 3 + .../bc22e83b225e4f10b55d4210072cfc0a.bundle | 3 + .../bc50d022fb19854157712bba7e50e0a3.bundle | 3 + .../bc805ff0837af71fbeee0658ef69fe75.bundle | 3 + .../bd13d701ba57f133190d1316442bc95d.bundle | 3 + .../bd96e97920989a0d375d45560db31d7c.bundle | 3 + .../be386d0131cdef74c5e78fdb732d42ea.bundle | 3 + .../bf7f86107335d44697d44bba0e4e0002.bundle | 3 + .../bfbe5ebfd0bdb2679742aea959e245d3.bundle | 3 + .../bfd901be2e92606e9e33b07f11696234.bundle | 3 + .../c06420184e6e9adcc4684c678fcecc34.bundle | 3 + .../c064d6402dcb262e100fa7ae096976b3.bundle | 3 + .../c176876c3b60da876e02705de9ecd82f.bundle | 3 + .../c1827799762fbe4f2a3a3e7ccabfce4d.bundle | 3 + .../c1c25448b4d12e2a148ed0d9c1b266e9.bundle | 3 + .../c1d05764647e7e7a7010082365482ca9.bundle | 3 + .../c1ffd7a31700a8b1aa5b96a8368cc0d0.bundle | 3 + .../c257907b857f7cb5c65dc31d5ea0b209.bundle | 3 + .../c27d7c385aa072f73c316e694c49bd82.bundle | 3 + .../c281680b86e8316c2e35d7f1d07e8b1c.bundle | 3 + .../c29c2b84c33a7ed53837e9d31ec89e30.bundle | 3 + .../c2bcc655e1dcb64604a84712d8367fff.bundle | 3 + .../c2cc79b0f8eabd9e3a08634082e2b282.bundle | 3 + .../c3436d3b290c738a45dc62ee1c0b1e81.bundle | 3 + .../c37135a687e76adbee834924a8db243d.bundle | 3 + .../c4a170ecb505bdcff03b24dcc2cba3db.bundle | 3 + .../c4e296a91790834f8df3b2172fbd1244.bundle | 3 + .../c545e16855a5f1af4b943978cfaa53c5.bundle | 3 + .../c5800d00852d88fbef244b77c469dd14.bundle | 3 + .../c58091e2480fd48894d36959380f5f0d.bundle | 3 + .../c595ae74e976244f49407b0ee4b8c4cb.bundle | 3 + .../c5f6d71ccb6e79daf1274f1c81bc44f2.bundle | 3 + .../c5feed7eb42057098543648742703dfb.bundle | 3 + .../c61b94a33649811bb16064586e21bcd7.bundle | 3 + .../c6501148ff8f65989698a0c05ee24dd6.bundle | 3 + .../c6b104acf56885d024221729e798bb97.bundle | 3 + .../c7159471d3aca3e1286924916f48472d.bundle | 3 + .../c75cbdb67bd2848cbf4badbf427e341d.bundle | 3 + .../c8aecec2bf66b11fc044fc55198075c0.bundle | 3 + .../c9a596298f8c2611425d5ce0da1cd2bb.bundle | 3 + .../c9dbbbd0d1ce7200ecc4219ae9629469.bundle | 3 + .../ca73f8c6bb88bafd75db759fcc2aaaab.bundle | 3 + .../ca8c1fa799e7fa848eecab942ed4a439.bundle | 3 + .../cac4a33d4c198594dc8f72551be3166b.bundle | 3 + .../caf476439c9e0912ddc3358e3c33d2f0.bundle | 3 + .../cb5a8e2beaa3549b665f530e1d1e4180.bundle | 3 + .../cc3a1de3461a18f2144ed6256a79a3b3.bundle | 3 + .../cc628dea5c1bf1e3ab14de5a0c205c99.bundle | 3 + .../ccd400ef11989114d421215a431d4f47.bundle | 3 + .../ccf1af4df4f8d78e16d6eb2da339f00b.bundle | 3 + .../cde9c10a88bbd86f5868c6d73f78fb40.bundle | 3 + .../ce10b6221043c8fcfc96d326c46a7603.bundle | 3 + .../ceec5f6249b395acf0309524324881fb.bundle | 3 + .../cf59d7fe3f03cf68416abbe375d746ba.bundle | 3 + .../cf6e9dc58a86c93e4b23e07b758022e0.bundle | 3 + .../d0eb7023fc1f492215d74c308e55ac8b.bundle | 3 + .../d1590920aaafd12c86f895d9e5e3af80.bundle | 3 + .../d180adc63f9e0a340f818701c08272a7.bundle | 3 + .../d1f03e9ab92cbc2057b23fc9cda433d7.bundle | 3 + .../d23151b9022491b6fcdda2a1ef8d3c3a.bundle | 3 + .../d3148da4b46d360181fe05728039887f.bundle | 3 + .../d3f26efa53df62877a53f990a223bf36.bundle | 3 + .../d435bf31b9f662d451e27d68c05cc052.bundle | 3 + .../d4ef8f9cbaf309613c0b158a773c8d98.bundle | 3 + .../d60804099498c8dff72e5f8a3ab3ef58.bundle | 3 + .../d7422a6152e9f8240d47e62504212f16.bundle | 3 + .../d792c2590f192e31f972113085d43d40.bundle | 3 + .../d7b6252e2b6bb66bda968a9d17c5c24f.bundle | 3 + .../d80f5fa8f93d60c947c39b3250e3c541.bundle | 3 + .../d840b3c4a3184f51fad176ded79d910a.bundle | 3 + .../d8571cf5fd335d180a1c5987ba1be7eb.bundle | 3 + .../d8594a1e07ec16a7ce4df6bcf11b8a26.bundle | 3 + .../d88f44568e10854f880d5b897baec6dc.bundle | 3 + .../d89b54777a09f30c6af53564da128c2f.bundle | 3 + .../d8a139b4ec06ebd5ce48e04e763fd242.bundle | 3 + .../d8a4e556ea3b13379ac47f980f8669c9.bundle | 3 + .../d91ba84c0b2b3d42ed887468a3b45f32.bundle | 3 + .../da22b8d6397feb44cd1dbfc6035c5836.bundle | 3 + .../da5e6a52e2bd1a1445b4c311ade563b5.bundle | 3 + .../db5cf30a6b3324b7c58d3b804a1129e4.bundle | 3 + .../db6f6322248634ae93c5cf791b5833ee.bundle | 3 + .../dcad102fd06c205deb0083c00000ca67.bundle | 3 + .../dcd1383fefc5f32d2e80493034b9cfa6.bundle | 3 + .../dcd343f1d4ed8842ad2801f245c0898b.bundle | 3 + .../dd1815b66e489c5ce409e42d34977a52.bundle | 3 + .../deb214afe4ecce908fac2a1ec3a8a29d.bundle | 3 + .../deb40ba9999f6431575f1047f8b22e96.bundle | 3 + .../debb121d84e545a7a60b1325dcd0d8b3.bundle | 3 + .../def31765338ee38a38d89fb8c84df08e.bundle | 3 + .../df287da2df5e1b1750bb8575b2e5c7e5.bundle | 3 + .../df78ae2d0a600e0c0a849b74640a5a6e.bundle | 3 + .../dfb60bc02a9923cd116a665bb52654da.bundle | 3 + .../dfd1bf7a13876bec2b51a48ae78296d3.bundle | 3 + .../e07a32353cabe5b2096465c1e5d9ab6c.bundle | 3 + .../e07ac118580390f1a251c3c272baf7bf.bundle | 3 + .../e0abf7a9092eb25f8db9e1bbd778b2d7.bundle | 3 + .../e1a543c8ddc987b6dc2eddd5a64abc28.bundle | 3 + .../e1abd8ac5c0a1fa0dc86bad0db30a675.bundle | 3 + .../e1f8d76de8f1f4848826ab6821a7e27c.bundle | 3 + .../e2a613f816c596c3291da1f81bce5307.bundle | 3 + .../e355ddc92ba40ce5c9cac072ec466376.bundle | 3 + .../e372516d7dde4825331be22b664fdb49.bundle | 3 + .../e3c8cb542865cecf6e08e7d0bdd54bc8.bundle | 3 + .../e4a722498228e1719ed733a6807ace29.bundle | 3 + .../e5ada50693c0dbc1946cafe4da7933cf.bundle | 3 + .../e684ce96412dfc05fbec5bdda92cae86.bundle | 3 + .../e840151402882933e998df5e97fbe3a8.bundle | 3 + .../e858c3bb8df251729fcc45fd6956966c.bundle | 3 + .../e8933487e3ad386c45a50b8d2f99f729.bundle | 3 + .../e8ce6863d26aea3b4ba6941ffa182499.bundle | 3 + .../e8d36cb9dfb307668313ea70da0360d5.bundle | 3 + .../e959e73d09c40982adc2cf9d3880a922.bundle | 3 + .../e9b91027f4d8ead29a5d7de17e81dfef.bundle | 3 + .../ea2ffa85bc26e41de870c122360093d1.bundle | 3 + .../eb190994fc77e9581004b6340a501e29.bundle | 3 + .../eb391515f8b054332631bca271fbd83b.bundle | 3 + .../ed05c4da123abef8b93d72288b25fd05.bundle | 3 + .../ed4937c77af57857166449fa1165b4ee.bundle | 3 + .../edf6fa37afd4f64fa397f985e3528d9e.bundle | 3 + .../ee36043a9a8e86d01e0461187d61afb4.bundle | 3 + .../ee9bf9a0e8802e3eb18de06e93123e94.bundle | 3 + .../eea1e0e7195d4aed9729c95858defe59.bundle | 3 + .../ef0c19c9e79f7d999e2f1a99ce4ace31.bundle | 3 + .../efbbdb728ef17a066f6ec3091039cecb.bundle | 3 + .../efc54c9ad97afcd51d0af87ccbddb343.bundle | 3 + .../f0fef50cb64cf41b5d94f3d991ff23e8.bundle | 3 + .../f24f963a62e51c80603b4d555425802e.bundle | 3 + .../f268f99b1c11b056504efd843cfabb25.bundle | 3 + .../f270d1dbbc0a0fae815a3b5069e35ae7.bundle | 3 + .../f2c537d80098d9dfaec9021c33e6867c.bundle | 3 + .../f30e9a5a1b011b5efb484e7015cdb73f.bundle | 3 + .../f403a34e0eef9048faa732e96d5b8199.bundle | 3 + .../f5104948742b7fdc78a801dc3e52f038.bundle | 3 + .../f53055faa85248b0f2c9f9bff689114c.bundle | 3 + .../f5c2b9d1218810efa4600947a2ca6c49.bundle | 3 + .../f6090aa6f4ea09a39ffc104a5d0d2f1e.bundle | 3 + .../f6294ec8111a5748e0b77754907c04e4.bundle | 3 + .../f6c056174ab2e0f06f4a0b6b3bec218c.bundle | 3 + .../f6dbe9a2f7ac17160b526b638d2d0e1f.bundle | 3 + .../f6de88ae631d818328191c6c270d9ab9.bundle | 3 + .../f712d795f4188fc916e1bf523769fb2c.bundle | 3 + .../f72712b559a3d83875c45b2e06338428.bundle | 3 + .../f76b4e7b28f92ab53322ad7957e966b4.bundle | 3 + .../f79e8942a042893dfe8c84b4fa7461e0.bundle | 3 + .../f7dac927e2e4bdf1c5fc7af725f3f776.bundle | 3 + .../f84a2eaf252763b5ff6c9d4d257e2538.bundle | 3 + .../f87707a74b78049d733ca989c2133203.bundle | 3 + .../f8979c721c375c29666d4a5de6f80d47.bundle | 3 + .../f96f8e9f3300e4b5fe3a0329bb472192.bundle | 3 + .../f9cf0c9270cc4c86c3bbe1c6a59cedfb.bundle | 3 + .../fa604cc14d2fa84d873f8beae59a98d7.bundle | 3 + .../fa7506a9e195e2a61b8d0d4c39736590.bundle | 3 + .../faadbe503ae3220703fda89ae8e31684.bundle | 3 + .../fab2a2a59c80d448c7a0d4dd8943270d.bundle | 3 + .../fabff72fe9327d045b8d2763fe22480a.bundle | 3 + .../fba92f686914fced10370bcdf751ce89.bundle | 3 + .../fbb0369690560661ad96cb7be3848b50.bundle | 3 + .../fce99cc5c4661ea08a1df037a2f408f0.bundle | 3 + .../fd1b94cd75c602131e932a7d11e7537a.bundle | 3 + .../fdaeb2886c4ce48f41c0ceb6b7c9d81c.bundle | 3 + .../fddcc24b84605d1e0ec358bb6b892acd.bundle | 3 + .../fe355f0d364e651b274fdb96459b275e.bundle | 3 + .../fe556cd5c94846a97fc4a87e0b1b5111.bundle | 3 + .../fecd0294f765299a6136479a5da1f235.bundle | 3 + .../fee3bbfcd2cb271b0848cf0ec4c9dbe4.bundle | 3 + .../ff253e3fab719c02d0972807322e86e2.bundle | 3 + .../ff37b93a2d2af9ac77bc7ed1bef775c4.bundle | 3 + resource/public/web/account_help.html | 64 + .../public/web/assets/animatecss/animate.css | 4074 +++++++++++++++++ .../bootstrap/css/bootstrap-grid.min.css | 6 + .../bootstrap/css/bootstrap-reboot.min.css | 7 + .../assets/bootstrap/css/bootstrap.min.css | 6 + .../bootstrap/js/bootstrap.bundle.min.js | 6 + .../public/web/assets/dropdown/css/style.css | 273 ++ .../web/assets/dropdown/js/navbar-dropdown.js | 111 + resource/public/web/assets/embla/embla.min.js | 1 + resource/public/web/assets/embla/script.js | 167 + .../public/web/assets/formoid/formoid.min.js | 242 + resource/public/web/assets/images/logo5.png | Bin 0 -> 3297 bytes .../photo-1471014706828-7f1b12e6d88b.jpeg | Bin 0 -> 127754 bytes .../photo-1483478550801-ceba5fe50e8e.jpeg | Bin 0 -> 93440 bytes .../photo-1488509082528-cefbba5ad692.jpeg | Bin 0 -> 44671 bytes .../photo-1493711662062-fa541adb3fc8.jpeg | Bin 0 -> 90875 bytes .../photo-1512429234305-12fe5b0b0f07.jpeg | Bin 0 -> 123839 bytes .../photo-1522091066250-665186289043.jpeg | Bin 0 -> 88082 bytes .../photo-1527335480088-278dbeec0ad5.jpeg | Bin 0 -> 124243 bytes .../photo-1528870884180-5649b20f6435.jpeg | Bin 0 -> 101028 bytes .../images/photo-1563208964-a455770abf67.jpeg | Bin 0 -> 137999 bytes .../photo-1564049489314-60d154ff107d.jpeg | Bin 0 -> 131056 bytes .../photo-1565870100382-f0a510db3cd1.jpeg | Bin 0 -> 118759 bytes .../photo-1568915567812-757e15929fe9.jpeg | Bin 0 -> 101080 bytes .../photo-1569078449082-d264d9e239c5.jpeg | Bin 0 -> 53806 bytes .../photo-1570942948935-8c1956d8284a.jpeg | Bin 0 -> 109281 bytes .../photo-1581803118522-7b72a50f7e9f.jpeg | Bin 0 -> 42946 bytes .../photo-1586763749650-70d7996310d0.jpeg | Bin 0 -> 110068 bytes .../photo-1587377838987-42165934161d.jpeg | Bin 0 -> 81125 bytes .../photo-1590589712122-59c2bf506bf5.jpeg | Bin 0 -> 81479 bytes .../photo-1591084728795-1149f32d9866.jpeg | Bin 0 -> 44849 bytes .../photo-1615680022647-99c397cbcaea.jpeg | Bin 0 -> 153314 bytes .../photo-1617224908579-c92008fc08bb.jpeg | Bin 0 -> 27544 bytes .../photo-1623743993875-03d6a5c7c709.jpeg | Bin 0 -> 64885 bytes .../photo-1623934199716-dc28818a6ec7.jpeg | Bin 0 -> 107282 bytes .../photo-1626240130051-68871c71de47.jpeg | Bin 0 -> 55998 bytes .../photo-1636624498233-0c3c5ab4186e.jpeg | Bin 0 -> 36796 bytes .../photo-1639656333400-ee5240f757a0.jpeg | Bin 0 -> 134860 bytes .../photo-1648747066670-034eab81a631.jpeg | Bin 0 -> 63537 bytes .../mbr-switch-arrow/mbr-switch-arrow.js | 153 + .../web/assets/mobirise/css/additional.css | 772 ++++ .../public/web/assets/parallax/jarallax.css | 15 + .../public/web/assets/parallax/jarallax.js | 1033 +++++ .../assets/scrollgallery/scroll-gallery.js | 136 + .../web/assets/smoothscroll/smooth-scroll.js | 790 ++++ .../public/web/assets/socicon/css/styles.css | 934 ++++ .../web/assets/socicon/fonts/socicon.eot | Bin 0 -> 116416 bytes .../web/assets/socicon/fonts/socicon.svg | 307 ++ .../web/assets/socicon/fonts/socicon.ttf | Bin 0 -> 116252 bytes .../web/assets/socicon/fonts/socicon.woff | Bin 0 -> 116328 bytes .../web/assets/socicon/fonts/socicon.woff2 | Bin 0 -> 64512 bytes .../public/web/assets/theme/css/style.css | 958 ++++ resource/public/web/assets/theme/js/script.js | 1698 +++++++ .../web/assets/mobirise-icons2/mobirise2.css | 498 ++ .../web/assets/mobirise-icons2/mobirise2.eot | Bin 0 -> 25468 bytes .../web/assets/mobirise-icons2/mobirise2.svg | 167 + .../web/assets/mobirise-icons2/mobirise2.ttf | Bin 0 -> 25280 bytes .../web/assets/mobirise-icons2/mobirise2.woff | Bin 0 -> 25356 bytes resource/public/web/assets/ytplayer/index.js | 737 +++ resource/public/web/images/bg.png | Bin 0 -> 5821 bytes .../public/web/images/delete-account-1.png | Bin 0 -> 554410 bytes .../public/web/images/delete-account-2.png | Bin 0 -> 317326 bytes .../public/web/images/delete-account-3.png | Bin 0 -> 212835 bytes resource/public/web/images/lily.png | Bin 0 -> 386758 bytes resource/public/web/images/main-view.png | Bin 0 -> 5464634 bytes resource/public/web/images/mekapang_logo.png | Bin 0 -> 298365 bytes resource/public/web/index.html | 16 + resource/public/web/policy.css | 109 + resource/public/web/privacy_policy.html | 390 ++ resource/public/web/service_policy.html | 540 +++ resource/public/web/unit-info.html | 0 test/deduction-fixes.test.ts | 469 ++ tsconfig.json | 98 + 1055 files changed, 33727 insertions(+), 57 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitattributes create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 Dockerfile.asset create mode 100644 Dockerfile.clockwork create mode 100644 Dockerfile.core create mode 100644 Dockerfile.duel create mode 100644 Dockerfile.nexus create mode 100644 Dockerfile.overseer create mode 100644 app/asset-app/asset-app.ts create mode 100644 app/asset-app/source/loader/plain-express-boot.ts create mode 100644 app/clockwork-app/clockwork-app.ts create mode 100644 app/clockwork-app/source/routine/cycle-clock.ts create mode 100644 app/clockwork-app/source/routine/duel-stage-clock.ts create mode 100644 app/clockwork-app/source/routine/ip-occupancy-clock.ts create mode 100644 app/clockwork-app/source/routine/parcel-lapse-clock.ts create mode 100644 app/clockwork-app/source/routine/push-dispatch-clock.ts create mode 100644 app/core-app/core-app.ts create mode 100644 app/core-app/source/handler/alias-shift-arranger.ts create mode 100644 app/core-app/source/handler/grant-gadget-arranger.ts create mode 100644 app/core-app/source/handler/grant-stock-arranger.ts create mode 100644 app/core-app/source/handler/member-admit-arranger.ts create mode 100644 app/core-app/source/handler/open-crate-arranger.ts create mode 100644 app/core-app/source/loader/tls-express-boot-spec.ts create mode 100644 app/core-app/source/loader/tls-express-boot.ts create mode 100644 app/core-app/source/manager/crate-odds-ledger.ts create mode 100644 app/core-app/source/router/account-link-gateway.ts create mode 100644 app/core-app/source/router/account-retire-gateway.ts create mode 100644 app/core-app/source/router/ad-bounty-gateway.ts create mode 100644 app/core-app/source/router/admit-gateway.ts create mode 100644 app/core-app/source/router/alias-shift-gateway.ts create mode 100644 app/core-app/source/router/bazaar-checkout-gateway.ts create mode 100644 app/core-app/source/router/campaign-clear-gateway.ts create mode 100644 app/core-app/source/router/campaign-gateway.ts create mode 100644 app/core-app/source/router/claim-spinner-bounty-gateway.ts create mode 100644 app/core-app/source/router/client-pack-gateway.ts create mode 100644 app/core-app/source/router/craft-stock-gateway.ts create mode 100644 app/core-app/source/router/enroll-callback-gateway.ts create mode 100644 app/core-app/source/router/enroll-gateway.ts create mode 100644 app/core-app/source/router/expand-gadget-bay-gateway.ts create mode 100644 app/core-app/source/router/gadget-refine-gateway.ts create mode 100644 app/core-app/source/router/new-member-setup-gateway.ts create mode 100644 app/core-app/source/router/occupancy-check-gateway.ts create mode 100644 app/core-app/source/router/open-crate-gateway.ts create mode 100644 app/core-app/source/router/open-instant-crate-gateway.ts create mode 100644 app/core-app/source/router/open-parcel-gateway.ts create mode 100644 app/core-app/source/router/park-crate-gateway.ts create mode 100644 app/core-app/source/router/primer-gateway.ts create mode 100644 app/core-app/source/router/push-opt-gateway.ts create mode 100644 app/core-app/source/router/read-cycle-gateway.ts create mode 100644 app/core-app/source/router/read-delta-gateway.ts create mode 100644 app/core-app/source/router/read-duel-stage-gateway.ts create mode 100644 app/core-app/source/router/read-host-health-gateway.ts create mode 100644 app/core-app/source/router/read-masterdata-gateway.ts create mode 100644 app/core-app/source/router/shatter-gadget-gateway.ts create mode 100644 app/core-app/source/router/storefront-checkout-gateway.ts create mode 100644 app/core-app/source/router/swap-azure-token-gateway.ts create mode 100644 app/core-app/source/router/vessel-gadget-loadout-gateway.ts create mode 100644 app/core-app/source/router/vessel-line-gateway.ts create mode 100644 app/core-app/source/router/vessel-refine-gateway.ts create mode 100644 app/core-app/source/service/account-link-usecase.ts create mode 100644 app/core-app/source/service/account-retire-usecase.ts create mode 100644 app/core-app/source/service/ad-bounty-usecase.ts create mode 100644 app/core-app/source/service/admit-usecase.ts create mode 100644 app/core-app/source/service/alias-shift-usecase.ts create mode 100644 app/core-app/source/service/bazaar-checkout-usecase.ts create mode 100644 app/core-app/source/service/campaign-clear-usecase.ts create mode 100644 app/core-app/source/service/campaign-usecase.ts create mode 100644 app/core-app/source/service/claim-spinner-bounty-usecase.ts create mode 100644 app/core-app/source/service/client-pack-usecase.ts create mode 100644 app/core-app/source/service/craft-stock-usecase.ts create mode 100644 app/core-app/source/service/enroll-usecase.ts create mode 100644 app/core-app/source/service/expand-gadget-bay-usecase.ts create mode 100644 app/core-app/source/service/gadget-refine-usecase.ts create mode 100644 app/core-app/source/service/new-member-setup-usecase.ts create mode 100644 app/core-app/source/service/open-crate-instant-usecase.ts create mode 100644 app/core-app/source/service/open-crate-usecase.ts create mode 100644 app/core-app/source/service/open-parcel-usecase.ts create mode 100644 app/core-app/source/service/park-crate-usecase.ts create mode 100644 app/core-app/source/service/primer-usecase.ts create mode 100644 app/core-app/source/service/push-opt-usecase.ts create mode 100644 app/core-app/source/service/read-cycle-usecase.ts create mode 100644 app/core-app/source/service/read-delta-usecase.ts create mode 100644 app/core-app/source/service/read-duel-stage-usecase.ts create mode 100644 app/core-app/source/service/read-host-health-usecase.ts create mode 100644 app/core-app/source/service/read-masterdata-usecase.ts create mode 100644 app/core-app/source/service/shatter-gadget-usecase.ts create mode 100644 app/core-app/source/service/storefront-checkout-usecase.ts create mode 100644 app/core-app/source/service/swap-azure-token-usecase.ts create mode 100644 app/core-app/source/service/vessel-gadget-loadout-usecase.ts create mode 100644 app/core-app/source/service/vessel-line-usecase.ts create mode 100644 app/core-app/source/service/vessel-refine-usecase.ts create mode 100644 app/duel-app/duel-app.ts create mode 100644 app/duel-app/source/module/duel-runner-factory.ts create mode 100644 app/duel-app/source/module/duel-runner-refork.ts create mode 100644 app/duel-runner/duel-runner.ts create mode 100644 app/duel-runner/source/loader/duel-runner-boot.ts create mode 100644 app/duel-runner/source/manager/battle-pocket-ledger.ts create mode 100644 app/duel-runner/source/module/duel-arena-unit/bout-state-kind.ts create mode 100644 app/duel-runner/source/module/duel-arena-unit/close-bout-verdict.ts create mode 100644 app/duel-runner/source/module/duel-arena-unit/duel-arena.ts create mode 100644 app/duel-runner/source/module/duel-arena-unit/duel-spec.ts create mode 100644 app/duel-runner/source/module/duel-arena-unit/duel-stream-gate.ts create mode 100644 app/duel-runner/source/module/duel-socket-io-module/duel-socket-io-spec.ts create mode 100644 app/duel-runner/source/module/duel-socket-io-module/duel-socket-io.ts create mode 100644 app/duel-runner/source/module/halt-clock/halt-clock.ts create mode 100644 app/duel-runner/source/module/parent-pipe-unit/parent-pipe-arranger.ts create mode 100644 app/duel-runner/source/module/parent-pipe-unit/parent-pipe-keeper.ts create mode 100644 app/duel-runner/source/router/close-bout-gateway.ts create mode 100644 app/duel-runner/source/router/close-phase-gateway.ts create mode 100644 app/duel-runner/source/router/enter-bout-gateway.ts create mode 100644 app/duel-runner/source/router/forward-stream-gateway.ts create mode 100644 app/duel-runner/source/router/leave-bout-gateway.ts create mode 100644 app/duel-runner/source/service/close-bout-usecase.ts create mode 100644 app/duel-runner/source/service/close-phase-usecase.ts create mode 100644 app/duel-runner/source/service/enter-bout-usecase.ts create mode 100644 app/duel-runner/source/service/forward-stream-usecase.ts create mode 100644 app/duel-runner/source/service/leave-bout-usecase.ts create mode 100644 app/nexus-app/nexus-app.ts create mode 100644 app/nexus-app/source/handler/member-battle-integrity-arranger.ts create mode 100644 app/nexus-app/source/handler/member-battle-integrity-policy.ts create mode 100644 app/nexus-app/source/handler/member-battle-payload-arranger.ts create mode 100644 app/nexus-app/source/handler/occupancy-expired-arranger.ts create mode 100644 app/nexus-app/source/handler/pairing-arranger.ts create mode 100644 app/nexus-app/source/loader/nexus-hub-boot.ts create mode 100644 app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-keeper.ts create mode 100644 app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-spec.ts create mode 100644 app/nexus-app/source/module/member-socket-unit/member-socket-keeper.ts create mode 100644 app/nexus-app/source/module/member-socket-unit/member-socket-spec.ts create mode 100644 app/nexus-app/source/module/socket-io-module/socket-io-app.ts create mode 100644 app/nexus-app/source/module/socket-io-module/socket-io-config.ts create mode 100644 app/nexus-app/source/router/enter-bracket-pairing-gateway.ts create mode 100644 app/nexus-app/source/router/enter-casual-pairing-gateway.ts create mode 100644 app/nexus-app/source/router/leave-bracket-pairing-gateway.ts create mode 100644 app/nexus-app/source/router/leave-casual-pairing-gateway.ts create mode 100644 app/nexus-app/source/routine/bracket-pairing-clock.ts create mode 100644 app/nexus-app/source/routine/casual-pairing-clock.ts create mode 100644 app/nexus-app/source/routine/occupancy-clock.ts create mode 100644 app/nexus-app/source/service/enter-bracket-pairing-usecase.ts create mode 100644 app/nexus-app/source/service/enter-casual-pairing-usecase.ts create mode 100644 app/nexus-app/source/service/leave-bracket-pairing-usecase.ts create mode 100644 app/nexus-app/source/service/leave-casual-pairing-usecase.ts create mode 100644 app/overseer-app/overseer-app.ts create mode 100644 app/overseer-app/source/loader/overseer-boot.ts create mode 100644 app/overseer-app/source/loader/overseer-command-boot.ts create mode 100644 app/overseer-app/source/module/console-line-unit.ts create mode 100644 app/overseer-app/source/module/overseer-verb.ts create mode 100644 app/overseer-app/source/service/grant-gadget-usecase.ts create mode 100644 app/overseer-app/source/service/grant-relic-usecase.ts create mode 100644 app/overseer-app/source/service/grant-vessel-usecase.ts create mode 100644 config/axios-config.ts create mode 100644 config/duel-host-spec.ts create mode 100644 config/enroll-unit-spec/enroll-spec.ts create mode 100644 config/enroll-unit-spec/facebook-auth-spec.ts create mode 100644 config/enroll-unit-spec/google-auth-spec.ts create mode 100644 config/gateway-spec.ts create mode 100644 config/host-spec.ts create mode 100644 config/play-baseline-spec.ts create mode 100644 config/pocket-link-spec.ts create mode 100644 config/runtime-spec.ts create mode 100644 config/sql-link-spec.ts create mode 100644 config/trace-store-spec.ts create mode 100644 data/dto/core-packet.ts create mode 100644 data/dto/duel-packet.ts create mode 100644 data/dto/hub-packet.ts create mode 100644 data/dto/nexus-hub-packet.ts create mode 100644 data/dto/server2server-dto.ts create mode 100644 data/entity/nexforge_db_record.ts create mode 100644 data/model/duel-shape/duel-arena-shape.ts create mode 100644 data/model/duel-shape/duel-runner-shape.ts create mode 100644 data/model/member-shape/BattleShape.ts create mode 100644 data/model/member-shape/BotCombatantShape.ts create mode 100644 data/model/member-shape/CombatantShape.ts create mode 100644 data/model/member-shape/MemberShape.ts create mode 100644 data/model/member-shape/SharedBoutShape.ts create mode 100644 data/model/member-shape/member-data-shape.ts create mode 100644 data/model/play-shape/PlayShape.ts create mode 100644 data/model/play-shape/play-data-shape.ts create mode 100644 data/model/pocket-shape/member-pocket-shape.ts create mode 100644 docker-compose-build.yml create mode 100644 docker_compose/db_server/docker-compose.yml create mode 100644 docker_compose/main_server/docker-compose.yml create mode 100644 docker_compose/mem_server/docker-compose.yml create mode 100644 docker_compose/resource_server/docker-compose.yml create mode 100644 handler/band-shift-arranger.ts create mode 100644 handler/campaign-clear-policy.ts create mode 100644 handler/close-bout-grant-bounty-arranger.ts create mode 100644 handler/grade-shift-arranger.ts create mode 100644 handler/phase-bounty-ratio.ts create mode 100644 manager/pocket-keeper/ad-bounty-keeper/ad-bounty-keeper-spec.ts create mode 100644 manager/pocket-keeper/ad-bounty-keeper/ad-bounty-keeper.ts create mode 100644 manager/pocket-keeper/bracket-pairing-keeper/bracket-pairing-keeper.ts create mode 100644 manager/pocket-keeper/bracket-pairing-keeper/bracket-pairing-spec.ts create mode 100644 manager/pocket-keeper/campaign-bout-keeper/campaign-bout-keeper.ts create mode 100644 manager/pocket-keeper/campaign-pocket-keeper/campaign-pocket-keeper.ts create mode 100644 manager/pocket-keeper/casual-pairing-keeper/casual-pairing-keeper.ts create mode 100644 manager/pocket-keeper/casual-pairing-keeper/casual-pairing-spec.ts create mode 100644 manager/pocket-keeper/cycle-keeper/cycle-keeper-spec.ts create mode 100644 manager/pocket-keeper/cycle-keeper/cycle-keeper.ts create mode 100644 manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper-spec.ts create mode 100644 manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper.ts create mode 100644 manager/pocket-keeper/duel-stage-keeper/duel-stage-keeper.ts create mode 100644 manager/pocket-keeper/feat-trace-keeper/feat-trace-keeper.ts create mode 100644 manager/pocket-keeper/feat-trace-keeper/feat-trace-spec.ts create mode 100644 manager/pocket-keeper/featured-offer-keeper/featured-offer-keeper.ts create mode 100644 manager/pocket-keeper/host-hold-keeper/host-hold-keeper.ts create mode 100644 manager/pocket-keeper/host-hold-keeper/host-hold-spec.ts create mode 100644 manager/pocket-keeper/ip-block-keeper/ip-block-keeper.ts create mode 100644 manager/pocket-keeper/ip-block-keeper/ip-block-spec.ts create mode 100644 manager/pocket-keeper/ip-occupancy-keeper/ip-occupancy-keeper-spec.ts create mode 100644 manager/pocket-keeper/ip-occupancy-keeper/ip-occupancy-keeper.ts create mode 100644 manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper.ts create mode 100644 manager/pocket-keeper/master-pocket-keeper/master-pocket-spec.ts create mode 100644 manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper.ts create mode 100644 manager/pocket-keeper/member-pocket-keeper/member-pocket-spec.ts create mode 100644 manager/pocket-keeper/occupancy-keeper/occupancy-credit-policy.ts create mode 100644 manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec.ts create mode 100644 manager/pocket-keeper/occupancy-keeper/occupancy-keeper.ts create mode 100644 manager/pocket-keeper/pairing-scan-lock-policy.ts create mode 100644 manager/pocket-keeper/pairing-scan-lock.ts create mode 100644 manager/pocket-keeper/parcel-keeper/parcel-keeper-spec.ts create mode 100644 manager/pocket-keeper/parcel-keeper/parcel-keeper.ts create mode 100644 manager/pocket-keeper/push-keeper/push-keeper-spec.ts create mode 100644 manager/pocket-keeper/push-keeper/push-keeper.ts create mode 100644 manager/pocket-keeper/refine-pocket-keeper/refine-pocket-keeper.ts create mode 100644 manager/pocket-keeper/refine-pocket-keeper/refine-pocket-spec.ts create mode 100644 manager/pocket-shape-base/BlobPocketStore.ts create mode 100644 manager/pocket-shape-base/HashPocketStore.ts create mode 100644 manager/sql-keeper/master-data-keeper/avatar-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/band-bounty-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/band-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/baseline-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/bazaar-offer-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/bot-gadget-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/bot-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/bot-vessel-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/campaign-bot-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/campaign-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/craft-recipe-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/crate-band-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/crate-group-odds-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/crate-odds-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/duel-default-bounty-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/duel-loss-bounty-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/duel-win-bounty-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/feat-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/gadget-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/gadget-refine-cost-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/gadget-refine-odds-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/gadget-shatter-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/gadget-trait-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/relic-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/stage-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/stock-band-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/trace-code-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/vessel-master-store.ts create mode 100644 manager/sql-keeper/master-data-keeper/vessel-refine-shard-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-alias-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-avatar-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-bazaar-trace-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-block-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-bracket-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-campaign-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-checkout-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-consent-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-core-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-crate-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-feat-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-gadget-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-parcel-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-pocket-persist-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-relic-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-side-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-ticket-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-trace-store.ts create mode 100644 manager/sql-keeper/member-data-keeper/member-vessel-store.ts create mode 100644 manager/sql-keeper/ops-data-keeper/ip-block-store.ts create mode 100644 manager/sql-keeper/ops-data-keeper/stage-store.ts create mode 100644 manager/trace-store-keeper/host-trace-keeper.ts create mode 100644 manager/trace-store-keeper/member-trace-keeper.ts create mode 100644 manager/trace-store-keeper/trace-store-spec.ts create mode 100644 middleware/bounce-gate.ts create mode 100644 middleware/client-license-gate.ts create mode 100644 middleware/gate-spec/license-auth-md-config.ts create mode 100644 middleware/gate-spec/occupancy-gate-spec.ts create mode 100644 middleware/gate-spec/socket-occupancy-gate-spec.ts create mode 100644 middleware/host-health-gate.ts create mode 100644 middleware/host-license-gate.ts create mode 100644 middleware/hub-occupancy-gate.ts create mode 100644 middleware/ip-occupancy-gate.ts create mode 100644 middleware/socket-member-gate.ts create mode 100644 middleware/socket-occupancy-gate.ts create mode 100644 modules/admit-jwt-unit/admit-jwt-spec.ts create mode 100644 modules/admit-jwt-unit/admit-jwt-unit.ts create mode 100644 modules/admit-ticket-unit/admit-ticket-unit.ts create mode 100644 modules/cipher-unit/key-weave-unit.ts create mode 100644 modules/cipher-unit/scramble-shape.ts create mode 100644 modules/cipher-unit/scramble-unit.ts create mode 100644 modules/document-link/document-link.ts create mode 100644 modules/duel-jwt-unit/duel-jwt-spec.ts create mode 100644 modules/duel-jwt-unit/duel-jwt-unit.ts create mode 100644 modules/duel-load-balancer/duel-host-pick.ts create mode 100644 modules/duel-load-balancer/duel-load-balancer-spec.ts create mode 100644 modules/duel-load-balancer/duel-load-balancer.ts create mode 100644 modules/duel-load-balancer/duel-occupancy-count.ts create mode 100644 modules/duel-ticket-unit/duel-ticket-unit.ts create mode 100644 modules/enroll-unit/anon-auth-unit.ts create mode 100644 modules/enroll-unit/facebook-auth-unit.ts create mode 100644 modules/enroll-unit/google-auth-unit.ts create mode 100644 modules/id-mint-unit/id-mint.ts create mode 100644 modules/kit-unit/chance-unit.ts create mode 100644 modules/kit-unit/request-ip.ts create mode 100644 modules/kit-unit/secret-equal.ts create mode 100644 modules/openapi-unit/openapi-spec.ts create mode 100644 modules/openapi-unit/openapi-unit.ts create mode 100644 modules/parse-unit/json-reader.ts create mode 100644 modules/parse-unit/path-leaf-reader.ts create mode 100644 modules/parse-unit/yaml-reader.ts create mode 100644 modules/plain-auth-unit/plain-auth-unit.ts create mode 100644 modules/pocket-link/pocket-boot-flush-policy.ts create mode 100644 modules/pocket-link/pocket-boot-flush.ts create mode 100644 modules/pocket-link/pocket-link.ts create mode 100644 modules/sql-unit/MasterRecordKeeperBase.ts create mode 100644 modules/sql-unit/MemberPlayRecordKeeperBase.ts create mode 100644 modules/sql-unit/MemberRecordKeeperBase.ts create mode 100644 modules/sql-unit/sql-amount-bind.ts create mode 100644 modules/sql-unit/sql-link.ts create mode 100644 modules/storefront-unit/storefront-api.ts create mode 100644 modules/storefront-unit/storefront-product-map.ts create mode 100644 modules/storefront-unit/storefront-receipt-policy.ts create mode 100644 modules/trace-unit/fatal-process-guard.ts create mode 100644 modules/trace-unit/fault-dispatch.ts create mode 100644 modules/trace-unit/host-label-keeper.ts create mode 100644 modules/trace-unit/stamp-log-printer.ts create mode 100644 modules/trace-unit/stamp-log.ts create mode 100644 modules/trace-unit/trace-file-writer.ts create mode 100644 modules/trace-unit/trace-unit-spec/fault-dispatch-spec.ts create mode 100644 modules/trace-unit/trace-unit-spec/trace-file-writer-spec.ts create mode 100644 package.json create mode 100644 redis-local/Dockerfile create mode 100644 redis-local/redis.conf create mode 100644 redis/Dockerfile create mode 100644 redis/redis.conf create mode 100644 resource/private/firebase/.gitkeep create mode 100644 resource/private/global_ssl/.gitkeep create mode 100644 resource/private/google_key/.gitkeep create mode 100644 resource/public/admin_page/.gitignore create mode 100644 resource/public/admin_page/package.json create mode 100644 resource/public/admin_page/public/favicon.ico create mode 100644 resource/public/admin_page/public/index.html create mode 100644 resource/public/admin_page/public/logo192.png create mode 100644 resource/public/admin_page/public/logo512.png create mode 100644 resource/public/admin_page/public/manifest.json create mode 100644 resource/public/admin_page/public/robots.txt create mode 100644 resource/public/admin_page/src/App.css create mode 100644 resource/public/admin_page/src/App.js create mode 100644 resource/public/admin_page/src/component/command/Command.css create mode 100644 resource/public/admin_page/src/component/command/Command.jsx create mode 100644 resource/public/admin_page/src/component/console/Console.css create mode 100644 resource/public/admin_page/src/component/console/Console.jsx create mode 100644 resource/public/admin_page/src/component/index.js create mode 100644 resource/public/admin_page/src/component/navigation/Navigation.css create mode 100644 resource/public/admin_page/src/component/navigation/Navigation.jsx create mode 100644 resource/public/admin_page/src/index.jsx create mode 100644 resource/public/admin_page/src/pages/dashBoard/DashBoard.css create mode 100644 resource/public/admin_page/src/pages/dashBoard/DashBoard.jsx create mode 100644 resource/public/admin_page/src/pages/index.js create mode 100644 resource/public/game_resource/Android/000c20a22c200bc14f269b6cae56a0ca.bundle create mode 100644 resource/public/game_resource/Android/0087d86b9bd6a16f60ae00994179556d.bundle create mode 100644 resource/public/game_resource/Android/0222cb564519e8ce64802db77dbfeb0d.bundle create mode 100644 resource/public/game_resource/Android/02496e6db9dd1373c7b1e47a81bdc174.bundle create mode 100644 resource/public/game_resource/Android/0268a10dacbfd3633dfc7a958bca719e.bundle create mode 100644 resource/public/game_resource/Android/040717f82175be6b212a8f85d0d94abd.bundle create mode 100644 resource/public/game_resource/Android/058bb14101e4e4431be2f39bfa5d1453.bundle create mode 100644 resource/public/game_resource/Android/05d23240b31e89566f190f869a983c7b.bundle create mode 100644 resource/public/game_resource/Android/0660d643b9483ee464e72622acfd2f81.bundle create mode 100644 resource/public/game_resource/Android/06f10ced33343ca46d0ce9b13434694b.bundle create mode 100644 resource/public/game_resource/Android/071e67540c8f0095433b9abf8c43d363.bundle create mode 100644 resource/public/game_resource/Android/074714a3f0683231fb6b2efe5b80eaca.bundle create mode 100644 resource/public/game_resource/Android/0759959f5d1bd39dda8e19d69fca0c24.bundle create mode 100644 resource/public/game_resource/Android/07cf39258e9f13134753e56cad880dd4.bundle create mode 100644 resource/public/game_resource/Android/0898aded60e814a1a219daa58e5c2c2f.bundle create mode 100644 resource/public/game_resource/Android/09349b1dd18ddca0a46f87f614cbbbae.bundle create mode 100644 resource/public/game_resource/Android/0946dccfa481a5395392701b6fbc83d3.bundle create mode 100644 resource/public/game_resource/Android/0a9baf3ca7cdfc770f38b7975b68fbef.bundle create mode 100644 resource/public/game_resource/Android/0bf11957dae33265ea8b339eb1920392.bundle create mode 100644 resource/public/game_resource/Android/0c6cc8f66403685f644b0133104b943b.bundle create mode 100644 resource/public/game_resource/Android/0cc18e6add137e5182c6a3c9cf70c5de.bundle create mode 100644 resource/public/game_resource/Android/0d31735126b359d6986980aee64e0207.bundle create mode 100644 resource/public/game_resource/Android/0d430fee81db5f1635f979ffba6167ba.bundle create mode 100644 resource/public/game_resource/Android/0d85e99d7ddcbb07be1856c55935c450.bundle create mode 100644 resource/public/game_resource/Android/0e7cc7ab411f698607c1cda077f51a43.bundle create mode 100644 resource/public/game_resource/Android/0f058420fb6ea2f686eabca5786ce28b.bundle create mode 100644 resource/public/game_resource/Android/0f065ebcc0d2ad00d60a15486e2cc473.bundle create mode 100644 resource/public/game_resource/Android/0f40ef6c84de15c2146545761dcdcc32.bundle create mode 100644 resource/public/game_resource/Android/0fca24ad01049cd43eb116726406596e.bundle create mode 100644 resource/public/game_resource/Android/0fdb4d1652d816bdb7716fa660d7e84b.bundle create mode 100644 resource/public/game_resource/Android/0ff78bc8f2ecf6d29d7d4ae1f4ad98e3.bundle create mode 100644 resource/public/game_resource/Android/1121575ea83aba3b4efeb17aec480e3a.bundle create mode 100644 resource/public/game_resource/Android/115dcdb9ebf98d80096f9a8d0afe4e27.bundle create mode 100644 resource/public/game_resource/Android/1204ebe541503ecb74556cf89308fc79.bundle create mode 100644 resource/public/game_resource/Android/13d52dfec9c449a4f4647b787f410ebb.bundle create mode 100644 resource/public/game_resource/Android/13db5b25900ab6fbd5b0b23ef356e627.bundle create mode 100644 resource/public/game_resource/Android/13f5f41e84d89c15a78b2c452ba0ba73.bundle create mode 100644 resource/public/game_resource/Android/14b87532678240bcf97b6d570e8e4251.bundle create mode 100644 resource/public/game_resource/Android/14bc4de314be9abf37b3131179234bfe.bundle create mode 100644 resource/public/game_resource/Android/15837a506baba672a6679e572f534c8e.bundle create mode 100644 resource/public/game_resource/Android/15b973382de3f9565e2fa45878200369.bundle create mode 100644 resource/public/game_resource/Android/1627ec58a02423ff8ad9889d2f79929b.bundle create mode 100644 resource/public/game_resource/Android/169038fb8072bba0d278a1185f609c38.bundle create mode 100644 resource/public/game_resource/Android/17482e8856b038dbe07a351462d76071.bundle create mode 100644 resource/public/game_resource/Android/17ebb3300543d7b11e5a1ea169c8de63.bundle create mode 100644 resource/public/game_resource/Android/17fa10466aac3e8f7e891dc05458c2d0.bundle create mode 100644 resource/public/game_resource/Android/181067db6ad28848bc0c86c8e605537d.bundle create mode 100644 resource/public/game_resource/Android/184f9fec1687605c2c7a489373b2bb02.bundle create mode 100644 resource/public/game_resource/Android/184ff4c050b3bf863440d1922152961c.bundle create mode 100644 resource/public/game_resource/Android/1898f164bb316952fca2b36b033d348d.bundle create mode 100644 resource/public/game_resource/Android/18efa6b269010736b837676c281ff104.bundle create mode 100644 resource/public/game_resource/Android/1a1fba77c66a56561293636ba66795d8.bundle create mode 100644 resource/public/game_resource/Android/1a4bfdb3d7b2f3d0c688b2d8d3153c4a.bundle create mode 100644 resource/public/game_resource/Android/1a818550d701d5154d6013b486066ca1.bundle create mode 100644 resource/public/game_resource/Android/1b2d83f98e2751a928de32886c57a8e6.bundle create mode 100644 resource/public/game_resource/Android/1b93abd4b7224a274ee09a92ed142de9.bundle create mode 100644 resource/public/game_resource/Android/1c0c488887fee4546ec46e79ab455282.bundle create mode 100644 resource/public/game_resource/Android/1c9d4fdb8f3b72f59b44bebd48091572.bundle create mode 100644 resource/public/game_resource/Android/1ca5e5d2d988dcffadbc2c86161f6b79.bundle create mode 100644 resource/public/game_resource/Android/1d3afb129c662a91409e916684e009ad.bundle create mode 100644 resource/public/game_resource/Android/1d5ce4e1092719f21148cd44aaad7cbd.bundle create mode 100644 resource/public/game_resource/Android/1d77c2d2d82c35f2156ee39d161c7d96.bundle create mode 100644 resource/public/game_resource/Android/1d8ee19f61b897a7718d76272f344ccf.bundle create mode 100644 resource/public/game_resource/Android/1db79106c60ed7218b05ea454ba08714.bundle create mode 100644 resource/public/game_resource/Android/1e9faa4630d9bc149dda630307fd791f.bundle create mode 100644 resource/public/game_resource/Android/1f652d267d45d760e23031f38ca88e69.bundle create mode 100644 resource/public/game_resource/Android/20401debb396cf8db96316a256b8e688.bundle create mode 100644 resource/public/game_resource/Android/2066e4e2076572e689a63f464c03a121.bundle create mode 100644 resource/public/game_resource/Android/2177438fbfb9a1eca1936f0de1424b56.bundle create mode 100644 resource/public/game_resource/Android/2190689258faf0e0aa4ab26201e9680f.bundle create mode 100644 resource/public/game_resource/Android/21b4f6829592c093031b95d79fbd6dcc.bundle create mode 100644 resource/public/game_resource/Android/21c140de93b1045f8bfcb6dd1646ae20.bundle create mode 100644 resource/public/game_resource/Android/2212cc2810742e79973e30f8e30c386e.bundle create mode 100644 resource/public/game_resource/Android/22e9c79dca019cec4cbecde9f3f12383.bundle create mode 100644 resource/public/game_resource/Android/23347c565110cd490fe51d5fea7507bb.bundle create mode 100644 resource/public/game_resource/Android/2388bf7a01a586e0527762c9e4268048.bundle create mode 100644 resource/public/game_resource/Android/23f98778050fc8ed67d72051bb98ae9e.bundle create mode 100644 resource/public/game_resource/Android/242355d71dfa6f2fbb5f656e619016f5.bundle create mode 100644 resource/public/game_resource/Android/249c3ae135e020a4ffcf81be9b60c77f.bundle create mode 100644 resource/public/game_resource/Android/257e19c808d0144c657ba22f132c43ff.bundle create mode 100644 resource/public/game_resource/Android/25e3f7f5b113458d258755e516459f36.bundle create mode 100644 resource/public/game_resource/Android/26feb311b852e25eb3821dab302c8af2.bundle create mode 100644 resource/public/game_resource/Android/270a32df6c15b0d2319396cd0e786f0b.bundle create mode 100644 resource/public/game_resource/Android/27957d2ea775b4e647113dd9893d4f53.bundle create mode 100644 resource/public/game_resource/Android/27cf0ad0996adce1d810f0eaa33a90fd.bundle create mode 100644 resource/public/game_resource/Android/28a1071a613bb3252f58b08806011f99.bundle create mode 100644 resource/public/game_resource/Android/28f5630a99e8ea4fa2824d570a93afce.bundle create mode 100644 resource/public/game_resource/Android/29e97f0052980afc6ccc17ab6b84309f.bundle create mode 100644 resource/public/game_resource/Android/2a87a94f891630ad79b524ea7f67d987.bundle create mode 100644 resource/public/game_resource/Android/2b86e365f186f797b231c648c4084813.bundle create mode 100644 resource/public/game_resource/Android/2c59ede472361628f479e27307b8c4e1.bundle create mode 100644 resource/public/game_resource/Android/2c8cb3f0f8b29c242044a06d45802208.bundle create mode 100644 resource/public/game_resource/Android/2cb3644bf0b75ee54d52a88339c07fbb.bundle create mode 100644 resource/public/game_resource/Android/2e0538377ce38f47633d420971562dc6.bundle create mode 100644 resource/public/game_resource/Android/2e0fc7b27af644d4ca9c555365d919b0.bundle create mode 100644 resource/public/game_resource/Android/2e5ba1321c41188d98467ad93de7f7c9.bundle create mode 100644 resource/public/game_resource/Android/2eb89e363cd72a45e1e215eefef9b329.bundle create mode 100644 resource/public/game_resource/Android/2f08c2ead7a9cd4c198ee687bb02b398.bundle create mode 100644 resource/public/game_resource/Android/2f9b06f0ce738ca5d038a1321c53ef5b.bundle create mode 100644 resource/public/game_resource/Android/2fa203e0f1603d8a86abcfd147d6b8b8.bundle create mode 100644 resource/public/game_resource/Android/3039b8aa3a9d0188b85329d8b34db45c.bundle create mode 100644 resource/public/game_resource/Android/306a0629fec572090c239fc6221ac818.bundle create mode 100644 resource/public/game_resource/Android/30fb22da1b914e28c51db87d59a57c9d.bundle create mode 100644 resource/public/game_resource/Android/321551daa71eb0b370da8b4786fcb10a.bundle create mode 100644 resource/public/game_resource/Android/327cd802e87c87669455177391f10d98.bundle create mode 100644 resource/public/game_resource/Android/3346a5ea8109a1b4b0a87e7f008b8071.bundle create mode 100644 resource/public/game_resource/Android/334acd221397bf0f3e6c761d22322fb3.bundle create mode 100644 resource/public/game_resource/Android/33a9da239c36af6dff70dce8e49e1e51.bundle create mode 100644 resource/public/game_resource/Android/33e4ab3a9c4d8197ddece0007301e928.bundle create mode 100644 resource/public/game_resource/Android/340c0c72ae372b5ecfd6198a75d4fb28.bundle create mode 100644 resource/public/game_resource/Android/341e4ab4b3102cc844eb496b791d1386.bundle create mode 100644 resource/public/game_resource/Android/346887503f056c9fa40f8759f2270921.bundle create mode 100644 resource/public/game_resource/Android/34c3e1e6973b6024d9dece7be682d60a.bundle create mode 100644 resource/public/game_resource/Android/351646f28c5488d36b3c3928ba364b24.bundle create mode 100644 resource/public/game_resource/Android/355eff79683baaf5039ebe81d2887660.bundle create mode 100644 resource/public/game_resource/Android/36099b95dc139e03cdd91255879efaa1.bundle create mode 100644 resource/public/game_resource/Android/364b2d76d4d343f2e838cc5ec6021b9a.bundle create mode 100644 resource/public/game_resource/Android/36de2b3099d63e81de1bda500e8213a1.bundle create mode 100644 resource/public/game_resource/Android/380b465bd68029aafd3530135cca7978.bundle create mode 100644 resource/public/game_resource/Android/38c8b56699ab43781b5f39403d555ac1.bundle create mode 100644 resource/public/game_resource/Android/38f5b4416b745a52c0f29b8e6d2e3b3a.bundle create mode 100644 resource/public/game_resource/Android/3903e747cf14c1501721107855ead8d9.bundle create mode 100644 resource/public/game_resource/Android/39423acd77dbd5b7f336351e5114d0e5.bundle create mode 100644 resource/public/game_resource/Android/3955662debfaf653e2e7cd7d5d73ad4b.bundle create mode 100644 resource/public/game_resource/Android/39c90b73199ac8863a6e853fb88f3007.bundle create mode 100644 resource/public/game_resource/Android/3a7db4e26c8101adc7bec4bdf25acf0a.bundle create mode 100644 resource/public/game_resource/Android/3a8bd1fc43d5e672ea45519d8952b479.bundle create mode 100644 resource/public/game_resource/Android/3b1e5a9ba6efab74c625efe2edd7e7b6.bundle create mode 100644 resource/public/game_resource/Android/3b3bd9f57222687014c531ceaedd147d.bundle create mode 100644 resource/public/game_resource/Android/3b67eaf699595d94ea56d8dbc80ccf27.bundle create mode 100644 resource/public/game_resource/Android/3d0caa5885771b63d82227438f20251c.bundle create mode 100644 resource/public/game_resource/Android/3d3f706bd68d9a049809d24d95e08b10.bundle create mode 100644 resource/public/game_resource/Android/3d614584ea110e6df1fff4c1d7e1ced2.bundle create mode 100644 resource/public/game_resource/Android/3de8e88cc128b365ce016a5789ddc07f.bundle create mode 100644 resource/public/game_resource/Android/3e36ffdf49773050314f7431f9b983ae.bundle create mode 100644 resource/public/game_resource/Android/3f81e71d0aeb077e828986c95ad8688b.bundle create mode 100644 resource/public/game_resource/Android/3fb903e024fd5400861f12a1c234a3fd.bundle create mode 100644 resource/public/game_resource/Android/3fccdc8c6aa87cb0dc6cd7bf70d58be2.bundle create mode 100644 resource/public/game_resource/Android/3ff625aaab19a9171e0b3de3e8486585.bundle create mode 100644 resource/public/game_resource/Android/407b620c8dc23eefd650c5486e0b0cb8.bundle create mode 100644 resource/public/game_resource/Android/41236a2c279da83eb523ae57f5164c31.bundle create mode 100644 resource/public/game_resource/Android/41e1ab85f1c532d49f2fbe703e6998d6.bundle create mode 100644 resource/public/game_resource/Android/424bde686a2d040bb42b2695eec79993.bundle create mode 100644 resource/public/game_resource/Android/428954cf45000dfc14b553ed4c43a30b.bundle create mode 100644 resource/public/game_resource/Android/42fb4cc61ee6912f19b08f75d95f9ac3.bundle create mode 100644 resource/public/game_resource/Android/43fff9e88ede3bd7f5c0e6b0cc891e8f.bundle create mode 100644 resource/public/game_resource/Android/4400440ee6a2fc0cd7dbb138cb4815c0.bundle create mode 100644 resource/public/game_resource/Android/4428ffe17f11815916fbfbf4ece90c25.bundle create mode 100644 resource/public/game_resource/Android/44583da2cd73f05defaff798df4a028f.bundle create mode 100644 resource/public/game_resource/Android/446fea04036e0ff08dca288ce42a7c27.bundle create mode 100644 resource/public/game_resource/Android/4538477184b9113e259e74ea4d509d92.bundle create mode 100644 resource/public/game_resource/Android/45b3f5f3158d9ff5f073dad755fd24c9.bundle create mode 100644 resource/public/game_resource/Android/45d64366d622ec73e87effd29affcc97.bundle create mode 100644 resource/public/game_resource/Android/467ec9bfddea2bae81788657436626a9.bundle create mode 100644 resource/public/game_resource/Android/4686ddd9e588645e5e547ac6438eb92c.bundle create mode 100644 resource/public/game_resource/Android/4829a54613d112b202f59b5488fefc8f.bundle create mode 100644 resource/public/game_resource/Android/488ab2f692a64d9c0266a0a891a6de1e.bundle create mode 100644 resource/public/game_resource/Android/488b9e9a58fe421831e72d7a27316ab5.bundle create mode 100644 resource/public/game_resource/Android/488f96124a155ebba38572c4a453c2f1.bundle create mode 100644 resource/public/game_resource/Android/4901515224ef9cd8900ba8eb7a7ca1b0.bundle create mode 100644 resource/public/game_resource/Android/49b54b87ce5cf9e12879e868c1befb1e.bundle create mode 100644 resource/public/game_resource/Android/49f63cd849b7a1a7ab82dae8718ddab0.bundle create mode 100644 resource/public/game_resource/Android/4a9143e92e14b74298e4b9be4d31d6f9.bundle create mode 100644 resource/public/game_resource/Android/4ae4b85f2f857d6c165f3b289a7acdd2.bundle create mode 100644 resource/public/game_resource/Android/4b553c49bdfa15b7554bb6bd74ffd50a.bundle create mode 100644 resource/public/game_resource/Android/4b719bfa4d56604bacb7b8ac353f6a10.bundle create mode 100644 resource/public/game_resource/Android/4c101c5deebaffea232f2efb9298b0a2.bundle create mode 100644 resource/public/game_resource/Android/4cbdefb4aaab52cdd845f1d3cd195b5a.bundle create mode 100644 resource/public/game_resource/Android/4d5959b65eefeabe33c322d95220a91b.bundle create mode 100644 resource/public/game_resource/Android/4e454ca051847cf71819e10b92b7a8ac.bundle create mode 100644 resource/public/game_resource/Android/4e5dcf0a9e08372d02e83b86c243b673.bundle create mode 100644 resource/public/game_resource/Android/4e9b6fa19161c3c38ff728e0e0a91427.bundle create mode 100644 resource/public/game_resource/Android/4f48524fed2956b2d3c7805cf92e01ce.bundle create mode 100644 resource/public/game_resource/Android/4faf8c0958047e25d26c3c9662b4051f.bundle create mode 100644 resource/public/game_resource/Android/4fd5b68acbd482a7bb9722dedf12e3ec.bundle create mode 100644 resource/public/game_resource/Android/4fe4ece4de8dbb8c37d35f172ed9fe72.bundle create mode 100644 resource/public/game_resource/Android/50d3a3fcb68d0101fcd034bacc8b64b9.bundle create mode 100644 resource/public/game_resource/Android/50ea45fc996b20afb63eec4845f2003d.bundle create mode 100644 resource/public/game_resource/Android/512791c190d71a8fc0cf0c1e1a43ac38.bundle create mode 100644 resource/public/game_resource/Android/51e71ed4d4006faea58e269124e4dc07.bundle create mode 100644 resource/public/game_resource/Android/52a6a5256638611eff56f421c3c51807.bundle create mode 100644 resource/public/game_resource/Android/52c83e2e59b8c1b544be64950b684cf9.bundle create mode 100644 resource/public/game_resource/Android/52d547b2e1107c8021e22f2794028b96.bundle create mode 100644 resource/public/game_resource/Android/52e972bc354baafd06c7cee990cd6a37.bundle create mode 100644 resource/public/game_resource/Android/53363ab6875701684530a32b786c70d6.bundle create mode 100644 resource/public/game_resource/Android/534e10974c4fd09dc016d5d53d9a7f93.bundle create mode 100644 resource/public/game_resource/Android/53b2bdfd42325ddd8f4c770135588af6.bundle create mode 100644 resource/public/game_resource/Android/53dddf2c7ba9faa4775b1f068fafeb25.bundle create mode 100644 resource/public/game_resource/Android/5422be348f4a049153ff98fe392f391d.bundle create mode 100644 resource/public/game_resource/Android/54aeda9e3a48a6fa17902ff91cb59c48.bundle create mode 100644 resource/public/game_resource/Android/5524619133ab0fd12fa2440078e74016.bundle create mode 100644 resource/public/game_resource/Android/561647845f89c11d1d8650b64f5c799f.bundle create mode 100644 resource/public/game_resource/Android/567ac5ebd4a96b871f0a92eb9af38dc4.bundle create mode 100644 resource/public/game_resource/Android/5794dc5c4d0b22345adddab100737ebd.bundle create mode 100644 resource/public/game_resource/Android/57de1bd6cb70b43455667bdcb7943269.bundle create mode 100644 resource/public/game_resource/Android/588090de7ef6042b41f4b4edd0bcbd32.bundle create mode 100644 resource/public/game_resource/Android/58c77d29e23c3ac3ae4f85a2d191c10b.bundle create mode 100644 resource/public/game_resource/Android/58cd8a7c53ef07814cd21682235ca886.bundle create mode 100644 resource/public/game_resource/Android/591dfdd6900ef1df7e0feb6bda421af1.bundle create mode 100644 resource/public/game_resource/Android/59238f1cf68f6ce858bb3d89e553c40c.bundle create mode 100644 resource/public/game_resource/Android/598dea29469dd230ba2092e3bdab4971.bundle create mode 100644 resource/public/game_resource/Android/59963ec4067ab23bcd22c8c9dc69a970.bundle create mode 100644 resource/public/game_resource/Android/59aa47c2d1a78c8d4003f36c21c85132.bundle create mode 100644 resource/public/game_resource/Android/59c9abf11814910a7b441ef55c3c932b.bundle create mode 100644 resource/public/game_resource/Android/59da93b2707b1ce25462254d2ffb1750.bundle create mode 100644 resource/public/game_resource/Android/59dd116b46a82eb432383ca84f841562.bundle create mode 100644 resource/public/game_resource/Android/5a947a82f216ac4dc1ba2e0bfef5c976.bundle create mode 100644 resource/public/game_resource/Android/5b2d0125100779775a2c14e9765a683e.bundle create mode 100644 resource/public/game_resource/Android/5b3fdab7777ffd4e8d90087c455a10a2.bundle create mode 100644 resource/public/game_resource/Android/5bc32f28e9a4c5b5d7a124b3b9613843.bundle create mode 100644 resource/public/game_resource/Android/5c4e265f4fe8862fdac51db6c09e7bf6.bundle create mode 100644 resource/public/game_resource/Android/5c61879688899a2e47309ed8601382cd.bundle create mode 100644 resource/public/game_resource/Android/5cc31579ce2ae289b71e621955d072e4.bundle create mode 100644 resource/public/game_resource/Android/5d50c8fad030aa2bc6f55bf19503466a.bundle create mode 100644 resource/public/game_resource/Android/5ee0d501e717d1eb6d44dc0a4cd2c216.bundle create mode 100644 resource/public/game_resource/Android/5f0752e55986bfc09e41a69c96e13953.bundle create mode 100644 resource/public/game_resource/Android/5f2cb1bad7d2f903f5af6d09be79fdfc.bundle create mode 100644 resource/public/game_resource/Android/5f76daf8df4cfe35769d93685ec29fd8.bundle create mode 100644 resource/public/game_resource/Android/5fce3ab83f3426b53e52d1648ed55ecd.bundle create mode 100644 resource/public/game_resource/Android/5fd1253a793db5ca9399d49039eedb85.bundle create mode 100644 resource/public/game_resource/Android/5fde474644c89925f78ee0a3ad0c7167.bundle create mode 100644 resource/public/game_resource/Android/5ffa0f34af9ac3301b9418b541d6968b.bundle create mode 100644 resource/public/game_resource/Android/60273f2c3b651dfbf3845db012b630e3.bundle create mode 100644 resource/public/game_resource/Android/60f657747a5df145bad2c29ca9edc278.bundle create mode 100644 resource/public/game_resource/Android/61048e99e9db4b20704ee91eea71db28.bundle create mode 100644 resource/public/game_resource/Android/611344a22093d6b4a72b89628d51aebc.bundle create mode 100644 resource/public/game_resource/Android/613480d49a3701a27ac24f2a59438ec9.bundle create mode 100644 resource/public/game_resource/Android/61a6691cea9b6aa45a176e8259071993.bundle create mode 100644 resource/public/game_resource/Android/61c028e61a12166b87e6e203247e0cb1.bundle create mode 100644 resource/public/game_resource/Android/61cdc8fa4d0f0755491d291e3d414156.bundle create mode 100644 resource/public/game_resource/Android/624c5dcfc0ace3e6b5251b684fb359e3.bundle create mode 100644 resource/public/game_resource/Android/625c8b4fa9b67a24bb958cda3c2fe346.bundle create mode 100644 resource/public/game_resource/Android/62a559c0c49c347f033d9969474955cc.bundle create mode 100644 resource/public/game_resource/Android/63292a912a6a0372dfa4cf2b7d3bfaa1.bundle create mode 100644 resource/public/game_resource/Android/638bfe8c84e8b49954f406a0d00d9477.bundle create mode 100644 resource/public/game_resource/Android/6439586c170e7bed3033c6efb7b5f127.bundle create mode 100644 resource/public/game_resource/Android/64746bd7b69dacb68af90531fe9ae2b9.bundle create mode 100644 resource/public/game_resource/Android/64a02f82107e0f31a8bc120895bdb4e9.bundle create mode 100644 resource/public/game_resource/Android/64d301c700fb0bbbb85a3d5b8fd98fb4.bundle create mode 100644 resource/public/game_resource/Android/65434d0ce26edd900fb6d4e04d0cd648.bundle create mode 100644 resource/public/game_resource/Android/65500b047aeb3edac3be031cd12f0976.bundle create mode 100644 resource/public/game_resource/Android/65e7b4df03b86dd574248056070f7341.bundle create mode 100644 resource/public/game_resource/Android/6648322a0a9e1c5bf411521902912f74.bundle create mode 100644 resource/public/game_resource/Android/6651befb613357b33c396d0feed21da6.bundle create mode 100644 resource/public/game_resource/Android/66a63df26fba3d79fd2f2793002bd84c.bundle create mode 100644 resource/public/game_resource/Android/67642f1a7d3ff6131f5b7b5eb4a7b06f.bundle create mode 100644 resource/public/game_resource/Android/684dcc8a988cae7f8cc9a50b6f4b5d06.bundle create mode 100644 resource/public/game_resource/Android/684ff8f1810e216e005d24415704135b.bundle create mode 100644 resource/public/game_resource/Android/6891762da1d293025f52d4201daffd8b.bundle create mode 100644 resource/public/game_resource/Android/68becc4478f96ab3bbd3684a2b073c92.bundle create mode 100644 resource/public/game_resource/Android/693ded411298063e45f78fadbe08101b.bundle create mode 100644 resource/public/game_resource/Android/6aa7a8f3dad7df97abf59e3390ba093e.bundle create mode 100644 resource/public/game_resource/Android/6adbd4d61d40c721f5fcdc376fb4a0fc.bundle create mode 100644 resource/public/game_resource/Android/6b7e9ae05e3bdc1e6404d04ae48801c2.bundle create mode 100644 resource/public/game_resource/Android/6c4dd69793c4b413b2941a788e4e0442.bundle create mode 100644 resource/public/game_resource/Android/6c7a68a631659f32825b0c02919d8315.bundle create mode 100644 resource/public/game_resource/Android/6cafa0428e912fd34cb11e720b57ee5c.bundle create mode 100644 resource/public/game_resource/Android/6cc3d615aa3c6897fa760de4617d9f4d.bundle create mode 100644 resource/public/game_resource/Android/6d256d675032b53375f0f174fafcfb78.bundle create mode 100644 resource/public/game_resource/Android/6d2d855743f8623707ccc0b9fa7b35fb.bundle create mode 100644 resource/public/game_resource/Android/6d489116d21b5a3fadb14379f9838b1c.bundle create mode 100644 resource/public/game_resource/Android/6eeb1e8736e498b589e854e06c811165.bundle create mode 100644 resource/public/game_resource/Android/6f500842d23c0744a75debc384a89816.bundle create mode 100644 resource/public/game_resource/Android/6fdaf7ea382921975e980d6c59641639.bundle create mode 100644 resource/public/game_resource/Android/70033cdf37c25deeee10e3c6d8d67119.bundle create mode 100644 resource/public/game_resource/Android/70318d85ca1dd3f99f894e846c785c80.bundle create mode 100644 resource/public/game_resource/Android/703e53d5724dcf25add177bc0fc009ae.bundle create mode 100644 resource/public/game_resource/Android/70512d80b4d4446c984ec437c073ef28.bundle create mode 100644 resource/public/game_resource/Android/7091ed9cf189e7f851bc4441efd97f58.bundle create mode 100644 resource/public/game_resource/Android/719367eecf4ddce830cf9885da463be0.bundle create mode 100644 resource/public/game_resource/Android/724822f703caa06b7e1bb8cc110a1740.bundle create mode 100644 resource/public/game_resource/Android/7257a867952c9a0f5ad0839d9ea1285d.bundle create mode 100644 resource/public/game_resource/Android/726d36d1ed278acc043ccdcd4835f012.bundle create mode 100644 resource/public/game_resource/Android/73e215a012f2943e62376b918414cd39.bundle create mode 100644 resource/public/game_resource/Android/74f6a90084b358528fe8600a909aaa4f.bundle create mode 100644 resource/public/game_resource/Android/75640a8c409ee43862817c3697355785.bundle create mode 100644 resource/public/game_resource/Android/75807ba142dfe571ccfc81ed5980fc57.bundle create mode 100644 resource/public/game_resource/Android/75aca1502eea3cf21604d2a503240ae1.bundle create mode 100644 resource/public/game_resource/Android/75edf43068546a1d5910a7a52bb0e7eb.bundle create mode 100644 resource/public/game_resource/Android/76143826d8d2c6efdb60c85036655355.bundle create mode 100644 resource/public/game_resource/Android/7632b5e7487684b5657582d668ff7454.bundle create mode 100644 resource/public/game_resource/Android/76c1a4a66d14cda7e9736e97d42975e3.bundle create mode 100644 resource/public/game_resource/Android/76e63c3e1bf1efdd258bc352a9902c7a.bundle create mode 100644 resource/public/game_resource/Android/76feee4ade04704293f289a22914d279.bundle create mode 100644 resource/public/game_resource/Android/786e49703be6402d7225b2f0269dc8e3.bundle create mode 100644 resource/public/game_resource/Android/7890460bf84a842c4b904a3e177325b6.bundle create mode 100644 resource/public/game_resource/Android/78beb3c47734561419811fc321bbdeda.bundle create mode 100644 resource/public/game_resource/Android/78f102ae97f80163134a1db187b78607.bundle create mode 100644 resource/public/game_resource/Android/79378838b3b38b5be1edf40cf1c33927.bundle create mode 100644 resource/public/game_resource/Android/798c18c057c3a47ea62b8dea31e032cc.bundle create mode 100644 resource/public/game_resource/Android/799b0153e6ff2cf33afd558c3de6b463.bundle create mode 100644 resource/public/game_resource/Android/7a38442fcf67799e379f969bc9d61d16.bundle create mode 100644 resource/public/game_resource/Android/7a48dfa16f0c0086047ef5603dc8e560.bundle create mode 100644 resource/public/game_resource/Android/7af50e07d67e2244b10eb0cbb3a3a498.bundle create mode 100644 resource/public/game_resource/Android/7b215e251a1ac2b22fb73a73e1b45f66.bundle create mode 100644 resource/public/game_resource/Android/7b416c929633003b271bf77a64e8a7e5.bundle create mode 100644 resource/public/game_resource/Android/7ccd2e288fc992afebfe4106606c0847.bundle create mode 100644 resource/public/game_resource/Android/7d7073e000764ee8299c0aaf118afb6e.bundle create mode 100644 resource/public/game_resource/Android/7d8e2ff8ce1090a7f23f6de7c29b9028.bundle create mode 100644 resource/public/game_resource/Android/7e77819523879c4e23028ba5fb4dc502.bundle create mode 100644 resource/public/game_resource/Android/7f78dd706c112a5b8fa4942411864128.bundle create mode 100644 resource/public/game_resource/Android/7fcb10bbe3d89460072902c6af8ab8e2.bundle create mode 100644 resource/public/game_resource/Android/801215800ecbb0235a9e19c9f5d629c6.bundle create mode 100644 resource/public/game_resource/Android/806e4a90bc6392d0a446883cdca2c53c.bundle create mode 100644 resource/public/game_resource/Android/814c73ea8c49668d36bc9097322a4516.bundle create mode 100644 resource/public/game_resource/Android/834bccc9515405a47e06f0cb20ddf894.bundle create mode 100644 resource/public/game_resource/Android/834c88817cecf58cf71724851194f65e.bundle create mode 100644 resource/public/game_resource/Android/8429607746268154f4a2c560fd0e3886.bundle create mode 100644 resource/public/game_resource/Android/84951cb18491be20fbb63596a16ce1f4.bundle create mode 100644 resource/public/game_resource/Android/84ab3826b308923d6c737cdb39e8ce22.bundle create mode 100644 resource/public/game_resource/Android/8511c01cf0fb9b3d86f731e49ea6a013.bundle create mode 100644 resource/public/game_resource/Android/851bad9c631c9431672000fc8dccaccc.bundle create mode 100644 resource/public/game_resource/Android/85a803eeef735d5b076ff87968b12307.bundle create mode 100644 resource/public/game_resource/Android/865a0e2988eeb13e9cd00cc2d91b9491.bundle create mode 100644 resource/public/game_resource/Android/86d416f935f97f031c76c75733bd18b2.bundle create mode 100644 resource/public/game_resource/Android/86f1b8fd0a6e6720a0f7eccbe4626c07.bundle create mode 100644 resource/public/game_resource/Android/87bca01a76ef205fa840826ae60e8b9e.bundle create mode 100644 resource/public/game_resource/Android/88dceab91b628e4d16a3c750f2481221.bundle create mode 100644 resource/public/game_resource/Android/8925832f2bbe38b0f303511eb80b232c.bundle create mode 100644 resource/public/game_resource/Android/89467f5efd08f4c15a653d7b3811641f.bundle create mode 100644 resource/public/game_resource/Android/894eec9c38ccece5d108d67d60432286.bundle create mode 100644 resource/public/game_resource/Android/8a357a9737dfdc4fd3534facf130e8b3.bundle create mode 100644 resource/public/game_resource/Android/8a6bfd0b943758fea7b8ea141d5fb545.bundle create mode 100644 resource/public/game_resource/Android/8ac0fb641fd2821eff2dc2251148c7df.bundle create mode 100644 resource/public/game_resource/Android/8b6a9d09fc58f59be2772abee6b6c31f.bundle create mode 100644 resource/public/game_resource/Android/8b97832491a12022413503651e51d1f6.bundle create mode 100644 resource/public/game_resource/Android/8c7bef873a991a711de4b44ecb0a2dcc.bundle create mode 100644 resource/public/game_resource/Android/8c8801ede531147aa748a35858727f5d.bundle create mode 100644 resource/public/game_resource/Android/8ca6d615f1c1ef872595f295166c7751.bundle create mode 100644 resource/public/game_resource/Android/8ce00e0d0a533108bc170b65b8c9ff17.bundle create mode 100644 resource/public/game_resource/Android/8ce1de61cd200bf46f2f593d1a32cf46.bundle create mode 100644 resource/public/game_resource/Android/8d5d06d9dbcf832ec6f87622135bd46e.bundle create mode 100644 resource/public/game_resource/Android/8da42f12bd5cafec4b8b21661d69a600.bundle create mode 100644 resource/public/game_resource/Android/8dbabe36ad498920c39bcaa431d3720a.bundle create mode 100644 resource/public/game_resource/Android/8df2ea253f4442f148fb37925ecf8230.bundle create mode 100644 resource/public/game_resource/Android/8e42010fba499ce83547bd59ec160829.bundle create mode 100644 resource/public/game_resource/Android/8e4eeb6ed920e87c6d49687ab96dd1e2.bundle create mode 100644 resource/public/game_resource/Android/8ec230607f3c2ac29a7520593efc66c2.bundle create mode 100644 resource/public/game_resource/Android/8ed569e9780e72b36c42972bf81acd14.bundle create mode 100644 resource/public/game_resource/Android/8f3f572b3d4ec4d95072dafbd26bf350.bundle create mode 100644 resource/public/game_resource/Android/8f6e3cc4f455cd813586773063a5c30f.bundle create mode 100644 resource/public/game_resource/Android/8f7f7b500e8f0db0b94f22bbc4298d34.bundle create mode 100644 resource/public/game_resource/Android/9003244ae50ccc3090ea547fa3e92281.bundle create mode 100644 resource/public/game_resource/Android/908085ab897ca6708c8a0d5d2f927daf.bundle create mode 100644 resource/public/game_resource/Android/911036b71aca5f4e36c6b5395e00f46a.bundle create mode 100644 resource/public/game_resource/Android/91e3bf6bf7ed49f7850cfe5a502cae55.bundle create mode 100644 resource/public/game_resource/Android/9295638a0cd08e7a09102e8c8d3bf9dd.bundle create mode 100644 resource/public/game_resource/Android/92a60c311e854edba704e3c502549351.bundle create mode 100644 resource/public/game_resource/Android/92b178c1ae6a78866253449bb5091c46.bundle create mode 100644 resource/public/game_resource/Android/92f5df2b76e6f2f5c882d498887c740c.bundle create mode 100644 resource/public/game_resource/Android/9335d55fd85f835aa17cde4410497b3e.bundle create mode 100644 resource/public/game_resource/Android/943ae6f22b38e0467e7b584427c379e4.bundle create mode 100644 resource/public/game_resource/Android/94d5c5ec88639bd16b5183ff0547a427.bundle create mode 100644 resource/public/game_resource/Android/95211aef5b4bac9df9e696e83e3dc0f7.bundle create mode 100644 resource/public/game_resource/Android/95a5282b75c4c532182a5094de285f32.bundle create mode 100644 resource/public/game_resource/Android/965d84001b13747e73aac88153fd717e.bundle create mode 100644 resource/public/game_resource/Android/968434fe0715f1d5d3421a7349828eca.bundle create mode 100644 resource/public/game_resource/Android/96d8b6d08fd7ae668206029e617ae825.bundle create mode 100644 resource/public/game_resource/Android/96f93304052ec0ff61e9352035585972.bundle create mode 100644 resource/public/game_resource/Android/97d651e5336f047ccc7465ff4e7a3ef4.bundle create mode 100644 resource/public/game_resource/Android/986f21816d43659ab19233a141781e18.bundle create mode 100644 resource/public/game_resource/Android/9959921242774df4ab21f9b21c8505e6.bundle create mode 100644 resource/public/game_resource/Android/9a5a945d56f01fe031624d3a66b9d07f.bundle create mode 100644 resource/public/game_resource/Android/9b2ecb61c8fd66dcbd62d9e265f8aac4.bundle create mode 100644 resource/public/game_resource/Android/9b46ad4d8cdfd0637f04e35b22b544f6.bundle create mode 100644 resource/public/game_resource/Android/9b47a7e6395a198ab04bfe049673da4c.bundle create mode 100644 resource/public/game_resource/Android/9c3a011b2a35c3ebd28dac182192f1d6.bundle create mode 100644 resource/public/game_resource/Android/9cef34232bbf042e9c6b4217750bd09e.bundle create mode 100644 resource/public/game_resource/Android/9d53faed37ccc928e9f70ac5c008b426.bundle create mode 100644 resource/public/game_resource/Android/9d586bbe9b0ed1e63f36a828c60f97a4.bundle create mode 100644 resource/public/game_resource/Android/9e8e2e0bbc5ac0990c521217e8ac1da1.bundle create mode 100644 resource/public/game_resource/Android/9ed5b63c4fd1de7fa25dd8c13614ab94.bundle create mode 100644 resource/public/game_resource/Android/9edef5311f481475064ef5820f22aee4.bundle create mode 100644 resource/public/game_resource/Android/9f1d875c2598984db7bc59a6f15338ed.bundle create mode 100644 resource/public/game_resource/Android/9f203fce6f391078ecbe8c0dd14e09cb.bundle create mode 100644 resource/public/game_resource/Android/9f24e9e05e72d2f2e1ff9ce136e8cb49.bundle create mode 100644 resource/public/game_resource/Android/a01a4734f72fd1a55264c2581880b0c8.bundle create mode 100644 resource/public/game_resource/Android/a02fbde60718e9ef2260a629654edb69.bundle create mode 100644 resource/public/game_resource/Android/a06749998bb32f321be7898ee16311e6.bundle create mode 100644 resource/public/game_resource/Android/a0bb83b1fbfd48397dd6282234afeb86.bundle create mode 100644 resource/public/game_resource/Android/a0ed3c7e113f884646327124b474c7d1.bundle create mode 100644 resource/public/game_resource/Android/a151fd22a5dbc452da43fdecce7762b0.bundle create mode 100644 resource/public/game_resource/Android/a1aa31d8de9c4f2153e50d89524ced3d.bundle create mode 100644 resource/public/game_resource/Android/a1bfaa0a486232f2dbdffdd848bb0406.bundle create mode 100644 resource/public/game_resource/Android/a1ffb7fd37c1854ccaf6a3588d8ed466.bundle create mode 100644 resource/public/game_resource/Android/a20f0a0731e7dfe055b8d0abb64a8247.bundle create mode 100644 resource/public/game_resource/Android/a2ea69bd45a897b0e5f44bda744f8c7a.bundle create mode 100644 resource/public/game_resource/Android/a3170dc28f228be5fdd1eb38a02843ed.bundle create mode 100644 resource/public/game_resource/Android/a34bed4b67088ec39f3defb5a421f179.bundle create mode 100644 resource/public/game_resource/Android/a3a24bae6479adad3a368fa329a40841.bundle create mode 100644 resource/public/game_resource/Android/a3d38bded3e709b4ae2779a613dea79c.bundle create mode 100644 resource/public/game_resource/Android/a45b46e5348c2a305c12a90101a7a66e.bundle create mode 100644 resource/public/game_resource/Android/a46066f3e05393d3f058e304adae9866.bundle create mode 100644 resource/public/game_resource/Android/a49a90b77fc9db37107122244102ba9b.bundle create mode 100644 resource/public/game_resource/Android/a4a7b93f25d653c42f6b2656c2771ebb.bundle create mode 100644 resource/public/game_resource/Android/a4e7ded93d2dd4afa0d39fe154b0c913.bundle create mode 100644 resource/public/game_resource/Android/a645cd7b4debf74b1d5f2654980285e9.bundle create mode 100644 resource/public/game_resource/Android/a6fa993e723b8b5804fa0c69661ef902.bundle create mode 100644 resource/public/game_resource/Android/a8466eb80185017edc7542c3db502c29.bundle create mode 100644 resource/public/game_resource/Android/a96ac16ab483434f13e5e3688db76dd2.bundle create mode 100644 resource/public/game_resource/Android/a96cf58223b429cc99a09c4f534a4682.bundle create mode 100644 resource/public/game_resource/Android/a9e542dbdcb32754e2082e55034f506f.bundle create mode 100644 resource/public/game_resource/Android/aa7bac9f52559202d3ebfd2e7e64c269.bundle create mode 100644 resource/public/game_resource/Android/ab63bda370c1cc7589548f86ef7a2bcf.bundle create mode 100644 resource/public/game_resource/Android/ac1190b5fe085e29ca66c16f87ef43bc.bundle create mode 100644 resource/public/game_resource/Android/ac350b4aae94b008983960e4a0d79f1d.bundle create mode 100644 resource/public/game_resource/Android/ac4f88f9c496ebb30b936fadd7bd9608.bundle create mode 100644 resource/public/game_resource/Android/acd2501a6166d461a5b6d68bae9c7b9e.bundle create mode 100644 resource/public/game_resource/Android/ad18b500db3bf832683d749eb0725bdd.bundle create mode 100644 resource/public/game_resource/Android/ad1b62c0448f1da998c0c2091b5e4af8.bundle create mode 100644 resource/public/game_resource/Android/ad3f2766ad3c1f90985c6b9183c83244.bundle create mode 100644 resource/public/game_resource/Android/ad731f461e2a50e7cee51a90f157f280.bundle create mode 100644 resource/public/game_resource/Android/ae99e3ad54241be49190ae97f600c52c.bundle create mode 100644 resource/public/game_resource/Android/b06d16c2f77caf0d02c995b9706ad03a.bundle create mode 100644 resource/public/game_resource/Android/b0d532f58cacc09150318b1a63d65156.bundle create mode 100644 resource/public/game_resource/Android/b137f20358e9ae4806f2fe6f1c1c7d14.bundle create mode 100644 resource/public/game_resource/Android/b1872715579e8858839abbcd072b5272.bundle create mode 100644 resource/public/game_resource/Android/b259e6f803490d1e137bc83a926f5871.bundle create mode 100644 resource/public/game_resource/Android/b29c09a870090df8400bc00838c5a76e.bundle create mode 100644 resource/public/game_resource/Android/b3473e04adea1b53417260b85d023275.bundle create mode 100644 resource/public/game_resource/Android/b39ed9df65a4e7dba165e381d61bb533.bundle create mode 100644 resource/public/game_resource/Android/b3b3e57fb2f2f3f4733fb676b8da900a.bundle create mode 100644 resource/public/game_resource/Android/b45a5de269249ba986cb5b3563533a5f.bundle create mode 100644 resource/public/game_resource/Android/b4aedccaefd930e4e2f5cc16ad66c3c1.bundle create mode 100644 resource/public/game_resource/Android/b6173419e1566b83737b649e58b18fab.bundle create mode 100644 resource/public/game_resource/Android/b6208858ffca06b7b0e2566f78b4b544.bundle create mode 100644 resource/public/game_resource/Android/b64f1587050fb1e8155a674ecf4832fb.bundle create mode 100644 resource/public/game_resource/Android/b66c2aed9875fd706f6e74703294689c.bundle create mode 100644 resource/public/game_resource/Android/b6800faa1e42d9e46049d6ada0db892a.bundle create mode 100644 resource/public/game_resource/Android/b68567c661ca95ea66a5014de6af89c3.bundle create mode 100644 resource/public/game_resource/Android/b6a2c61aa0b3bedbc9104333e7e5e560.bundle create mode 100644 resource/public/game_resource/Android/b6b6f52e5052fea3cb4357617dea7483.bundle create mode 100644 resource/public/game_resource/Android/b6df3df94fe102d1df5373e22b1e33bc.bundle create mode 100644 resource/public/game_resource/Android/b73d40ebcddded7b9f181b74cca38b51.bundle create mode 100644 resource/public/game_resource/Android/b8fe1bdf547f833991fb000f64f9fe48.bundle create mode 100644 resource/public/game_resource/Android/b9b508d7fdcd6868e7f638013e5f946e.bundle create mode 100644 resource/public/game_resource/Android/b9ee1f149d0da25815cb2b95d7d1e486.bundle create mode 100644 resource/public/game_resource/Android/b9fef8ef1f3a5b4fcd10ad2b105ead43.bundle create mode 100644 resource/public/game_resource/Android/ba7e1117f20065aa3311b84a701fd072.bundle create mode 100644 resource/public/game_resource/Android/ba912d1a432f2bee0e1a9ccabc5ed545.bundle create mode 100644 resource/public/game_resource/Android/bb88c66c115d78028340441375c5951b.bundle create mode 100644 resource/public/game_resource/Android/bb93de231a039f364c2e6d2130ba60c3.bundle create mode 100644 resource/public/game_resource/Android/bbd01cee6241e52a197bcf2197002b6e.bundle create mode 100644 resource/public/game_resource/Android/bbee50bf3daf0032d9325ffed1c8ab1a.bundle create mode 100644 resource/public/game_resource/Android/bc1d1f8a6d59f79f91fe71acdba2c990.bundle create mode 100644 resource/public/game_resource/Android/bc22e83b225e4f10b55d4210072cfc0a.bundle create mode 100644 resource/public/game_resource/Android/bc50d022fb19854157712bba7e50e0a3.bundle create mode 100644 resource/public/game_resource/Android/bc805ff0837af71fbeee0658ef69fe75.bundle create mode 100644 resource/public/game_resource/Android/bd13d701ba57f133190d1316442bc95d.bundle create mode 100644 resource/public/game_resource/Android/bd96e97920989a0d375d45560db31d7c.bundle create mode 100644 resource/public/game_resource/Android/be386d0131cdef74c5e78fdb732d42ea.bundle create mode 100644 resource/public/game_resource/Android/bf7f86107335d44697d44bba0e4e0002.bundle create mode 100644 resource/public/game_resource/Android/bfbe5ebfd0bdb2679742aea959e245d3.bundle create mode 100644 resource/public/game_resource/Android/bfd901be2e92606e9e33b07f11696234.bundle create mode 100644 resource/public/game_resource/Android/c06420184e6e9adcc4684c678fcecc34.bundle create mode 100644 resource/public/game_resource/Android/c064d6402dcb262e100fa7ae096976b3.bundle create mode 100644 resource/public/game_resource/Android/c176876c3b60da876e02705de9ecd82f.bundle create mode 100644 resource/public/game_resource/Android/c1827799762fbe4f2a3a3e7ccabfce4d.bundle create mode 100644 resource/public/game_resource/Android/c1c25448b4d12e2a148ed0d9c1b266e9.bundle create mode 100644 resource/public/game_resource/Android/c1d05764647e7e7a7010082365482ca9.bundle create mode 100644 resource/public/game_resource/Android/c1ffd7a31700a8b1aa5b96a8368cc0d0.bundle create mode 100644 resource/public/game_resource/Android/c257907b857f7cb5c65dc31d5ea0b209.bundle create mode 100644 resource/public/game_resource/Android/c27d7c385aa072f73c316e694c49bd82.bundle create mode 100644 resource/public/game_resource/Android/c281680b86e8316c2e35d7f1d07e8b1c.bundle create mode 100644 resource/public/game_resource/Android/c29c2b84c33a7ed53837e9d31ec89e30.bundle create mode 100644 resource/public/game_resource/Android/c2bcc655e1dcb64604a84712d8367fff.bundle create mode 100644 resource/public/game_resource/Android/c2cc79b0f8eabd9e3a08634082e2b282.bundle create mode 100644 resource/public/game_resource/Android/c3436d3b290c738a45dc62ee1c0b1e81.bundle create mode 100644 resource/public/game_resource/Android/c37135a687e76adbee834924a8db243d.bundle create mode 100644 resource/public/game_resource/Android/c4a170ecb505bdcff03b24dcc2cba3db.bundle create mode 100644 resource/public/game_resource/Android/c4e296a91790834f8df3b2172fbd1244.bundle create mode 100644 resource/public/game_resource/Android/c545e16855a5f1af4b943978cfaa53c5.bundle create mode 100644 resource/public/game_resource/Android/c5800d00852d88fbef244b77c469dd14.bundle create mode 100644 resource/public/game_resource/Android/c58091e2480fd48894d36959380f5f0d.bundle create mode 100644 resource/public/game_resource/Android/c595ae74e976244f49407b0ee4b8c4cb.bundle create mode 100644 resource/public/game_resource/Android/c5f6d71ccb6e79daf1274f1c81bc44f2.bundle create mode 100644 resource/public/game_resource/Android/c5feed7eb42057098543648742703dfb.bundle create mode 100644 resource/public/game_resource/Android/c61b94a33649811bb16064586e21bcd7.bundle create mode 100644 resource/public/game_resource/Android/c6501148ff8f65989698a0c05ee24dd6.bundle create mode 100644 resource/public/game_resource/Android/c6b104acf56885d024221729e798bb97.bundle create mode 100644 resource/public/game_resource/Android/c7159471d3aca3e1286924916f48472d.bundle create mode 100644 resource/public/game_resource/Android/c75cbdb67bd2848cbf4badbf427e341d.bundle create mode 100644 resource/public/game_resource/Android/c8aecec2bf66b11fc044fc55198075c0.bundle create mode 100644 resource/public/game_resource/Android/c9a596298f8c2611425d5ce0da1cd2bb.bundle create mode 100644 resource/public/game_resource/Android/c9dbbbd0d1ce7200ecc4219ae9629469.bundle create mode 100644 resource/public/game_resource/Android/ca73f8c6bb88bafd75db759fcc2aaaab.bundle create mode 100644 resource/public/game_resource/Android/ca8c1fa799e7fa848eecab942ed4a439.bundle create mode 100644 resource/public/game_resource/Android/cac4a33d4c198594dc8f72551be3166b.bundle create mode 100644 resource/public/game_resource/Android/caf476439c9e0912ddc3358e3c33d2f0.bundle create mode 100644 resource/public/game_resource/Android/cb5a8e2beaa3549b665f530e1d1e4180.bundle create mode 100644 resource/public/game_resource/Android/cc3a1de3461a18f2144ed6256a79a3b3.bundle create mode 100644 resource/public/game_resource/Android/cc628dea5c1bf1e3ab14de5a0c205c99.bundle create mode 100644 resource/public/game_resource/Android/ccd400ef11989114d421215a431d4f47.bundle create mode 100644 resource/public/game_resource/Android/ccf1af4df4f8d78e16d6eb2da339f00b.bundle create mode 100644 resource/public/game_resource/Android/cde9c10a88bbd86f5868c6d73f78fb40.bundle create mode 100644 resource/public/game_resource/Android/ce10b6221043c8fcfc96d326c46a7603.bundle create mode 100644 resource/public/game_resource/Android/ceec5f6249b395acf0309524324881fb.bundle create mode 100644 resource/public/game_resource/Android/cf59d7fe3f03cf68416abbe375d746ba.bundle create mode 100644 resource/public/game_resource/Android/cf6e9dc58a86c93e4b23e07b758022e0.bundle create mode 100644 resource/public/game_resource/Android/d0eb7023fc1f492215d74c308e55ac8b.bundle create mode 100644 resource/public/game_resource/Android/d1590920aaafd12c86f895d9e5e3af80.bundle create mode 100644 resource/public/game_resource/Android/d180adc63f9e0a340f818701c08272a7.bundle create mode 100644 resource/public/game_resource/Android/d1f03e9ab92cbc2057b23fc9cda433d7.bundle create mode 100644 resource/public/game_resource/Android/d23151b9022491b6fcdda2a1ef8d3c3a.bundle create mode 100644 resource/public/game_resource/Android/d3148da4b46d360181fe05728039887f.bundle create mode 100644 resource/public/game_resource/Android/d3f26efa53df62877a53f990a223bf36.bundle create mode 100644 resource/public/game_resource/Android/d435bf31b9f662d451e27d68c05cc052.bundle create mode 100644 resource/public/game_resource/Android/d4ef8f9cbaf309613c0b158a773c8d98.bundle create mode 100644 resource/public/game_resource/Android/d60804099498c8dff72e5f8a3ab3ef58.bundle create mode 100644 resource/public/game_resource/Android/d7422a6152e9f8240d47e62504212f16.bundle create mode 100644 resource/public/game_resource/Android/d792c2590f192e31f972113085d43d40.bundle create mode 100644 resource/public/game_resource/Android/d7b6252e2b6bb66bda968a9d17c5c24f.bundle create mode 100644 resource/public/game_resource/Android/d80f5fa8f93d60c947c39b3250e3c541.bundle create mode 100644 resource/public/game_resource/Android/d840b3c4a3184f51fad176ded79d910a.bundle create mode 100644 resource/public/game_resource/Android/d8571cf5fd335d180a1c5987ba1be7eb.bundle create mode 100644 resource/public/game_resource/Android/d8594a1e07ec16a7ce4df6bcf11b8a26.bundle create mode 100644 resource/public/game_resource/Android/d88f44568e10854f880d5b897baec6dc.bundle create mode 100644 resource/public/game_resource/Android/d89b54777a09f30c6af53564da128c2f.bundle create mode 100644 resource/public/game_resource/Android/d8a139b4ec06ebd5ce48e04e763fd242.bundle create mode 100644 resource/public/game_resource/Android/d8a4e556ea3b13379ac47f980f8669c9.bundle create mode 100644 resource/public/game_resource/Android/d91ba84c0b2b3d42ed887468a3b45f32.bundle create mode 100644 resource/public/game_resource/Android/da22b8d6397feb44cd1dbfc6035c5836.bundle create mode 100644 resource/public/game_resource/Android/da5e6a52e2bd1a1445b4c311ade563b5.bundle create mode 100644 resource/public/game_resource/Android/db5cf30a6b3324b7c58d3b804a1129e4.bundle create mode 100644 resource/public/game_resource/Android/db6f6322248634ae93c5cf791b5833ee.bundle create mode 100644 resource/public/game_resource/Android/dcad102fd06c205deb0083c00000ca67.bundle create mode 100644 resource/public/game_resource/Android/dcd1383fefc5f32d2e80493034b9cfa6.bundle create mode 100644 resource/public/game_resource/Android/dcd343f1d4ed8842ad2801f245c0898b.bundle create mode 100644 resource/public/game_resource/Android/dd1815b66e489c5ce409e42d34977a52.bundle create mode 100644 resource/public/game_resource/Android/deb214afe4ecce908fac2a1ec3a8a29d.bundle create mode 100644 resource/public/game_resource/Android/deb40ba9999f6431575f1047f8b22e96.bundle create mode 100644 resource/public/game_resource/Android/debb121d84e545a7a60b1325dcd0d8b3.bundle create mode 100644 resource/public/game_resource/Android/def31765338ee38a38d89fb8c84df08e.bundle create mode 100644 resource/public/game_resource/Android/df287da2df5e1b1750bb8575b2e5c7e5.bundle create mode 100644 resource/public/game_resource/Android/df78ae2d0a600e0c0a849b74640a5a6e.bundle create mode 100644 resource/public/game_resource/Android/dfb60bc02a9923cd116a665bb52654da.bundle create mode 100644 resource/public/game_resource/Android/dfd1bf7a13876bec2b51a48ae78296d3.bundle create mode 100644 resource/public/game_resource/Android/e07a32353cabe5b2096465c1e5d9ab6c.bundle create mode 100644 resource/public/game_resource/Android/e07ac118580390f1a251c3c272baf7bf.bundle create mode 100644 resource/public/game_resource/Android/e0abf7a9092eb25f8db9e1bbd778b2d7.bundle create mode 100644 resource/public/game_resource/Android/e1a543c8ddc987b6dc2eddd5a64abc28.bundle create mode 100644 resource/public/game_resource/Android/e1abd8ac5c0a1fa0dc86bad0db30a675.bundle create mode 100644 resource/public/game_resource/Android/e1f8d76de8f1f4848826ab6821a7e27c.bundle create mode 100644 resource/public/game_resource/Android/e2a613f816c596c3291da1f81bce5307.bundle create mode 100644 resource/public/game_resource/Android/e355ddc92ba40ce5c9cac072ec466376.bundle create mode 100644 resource/public/game_resource/Android/e372516d7dde4825331be22b664fdb49.bundle create mode 100644 resource/public/game_resource/Android/e3c8cb542865cecf6e08e7d0bdd54bc8.bundle create mode 100644 resource/public/game_resource/Android/e4a722498228e1719ed733a6807ace29.bundle create mode 100644 resource/public/game_resource/Android/e5ada50693c0dbc1946cafe4da7933cf.bundle create mode 100644 resource/public/game_resource/Android/e684ce96412dfc05fbec5bdda92cae86.bundle create mode 100644 resource/public/game_resource/Android/e840151402882933e998df5e97fbe3a8.bundle create mode 100644 resource/public/game_resource/Android/e858c3bb8df251729fcc45fd6956966c.bundle create mode 100644 resource/public/game_resource/Android/e8933487e3ad386c45a50b8d2f99f729.bundle create mode 100644 resource/public/game_resource/Android/e8ce6863d26aea3b4ba6941ffa182499.bundle create mode 100644 resource/public/game_resource/Android/e8d36cb9dfb307668313ea70da0360d5.bundle create mode 100644 resource/public/game_resource/Android/e959e73d09c40982adc2cf9d3880a922.bundle create mode 100644 resource/public/game_resource/Android/e9b91027f4d8ead29a5d7de17e81dfef.bundle create mode 100644 resource/public/game_resource/Android/ea2ffa85bc26e41de870c122360093d1.bundle create mode 100644 resource/public/game_resource/Android/eb190994fc77e9581004b6340a501e29.bundle create mode 100644 resource/public/game_resource/Android/eb391515f8b054332631bca271fbd83b.bundle create mode 100644 resource/public/game_resource/Android/ed05c4da123abef8b93d72288b25fd05.bundle create mode 100644 resource/public/game_resource/Android/ed4937c77af57857166449fa1165b4ee.bundle create mode 100644 resource/public/game_resource/Android/edf6fa37afd4f64fa397f985e3528d9e.bundle create mode 100644 resource/public/game_resource/Android/ee36043a9a8e86d01e0461187d61afb4.bundle create mode 100644 resource/public/game_resource/Android/ee9bf9a0e8802e3eb18de06e93123e94.bundle create mode 100644 resource/public/game_resource/Android/eea1e0e7195d4aed9729c95858defe59.bundle create mode 100644 resource/public/game_resource/Android/ef0c19c9e79f7d999e2f1a99ce4ace31.bundle create mode 100644 resource/public/game_resource/Android/efbbdb728ef17a066f6ec3091039cecb.bundle create mode 100644 resource/public/game_resource/Android/efc54c9ad97afcd51d0af87ccbddb343.bundle create mode 100644 resource/public/game_resource/Android/f0fef50cb64cf41b5d94f3d991ff23e8.bundle create mode 100644 resource/public/game_resource/Android/f24f963a62e51c80603b4d555425802e.bundle create mode 100644 resource/public/game_resource/Android/f268f99b1c11b056504efd843cfabb25.bundle create mode 100644 resource/public/game_resource/Android/f270d1dbbc0a0fae815a3b5069e35ae7.bundle create mode 100644 resource/public/game_resource/Android/f2c537d80098d9dfaec9021c33e6867c.bundle create mode 100644 resource/public/game_resource/Android/f30e9a5a1b011b5efb484e7015cdb73f.bundle create mode 100644 resource/public/game_resource/Android/f403a34e0eef9048faa732e96d5b8199.bundle create mode 100644 resource/public/game_resource/Android/f5104948742b7fdc78a801dc3e52f038.bundle create mode 100644 resource/public/game_resource/Android/f53055faa85248b0f2c9f9bff689114c.bundle create mode 100644 resource/public/game_resource/Android/f5c2b9d1218810efa4600947a2ca6c49.bundle create mode 100644 resource/public/game_resource/Android/f6090aa6f4ea09a39ffc104a5d0d2f1e.bundle create mode 100644 resource/public/game_resource/Android/f6294ec8111a5748e0b77754907c04e4.bundle create mode 100644 resource/public/game_resource/Android/f6c056174ab2e0f06f4a0b6b3bec218c.bundle create mode 100644 resource/public/game_resource/Android/f6dbe9a2f7ac17160b526b638d2d0e1f.bundle create mode 100644 resource/public/game_resource/Android/f6de88ae631d818328191c6c270d9ab9.bundle create mode 100644 resource/public/game_resource/Android/f712d795f4188fc916e1bf523769fb2c.bundle create mode 100644 resource/public/game_resource/Android/f72712b559a3d83875c45b2e06338428.bundle create mode 100644 resource/public/game_resource/Android/f76b4e7b28f92ab53322ad7957e966b4.bundle create mode 100644 resource/public/game_resource/Android/f79e8942a042893dfe8c84b4fa7461e0.bundle create mode 100644 resource/public/game_resource/Android/f7dac927e2e4bdf1c5fc7af725f3f776.bundle create mode 100644 resource/public/game_resource/Android/f84a2eaf252763b5ff6c9d4d257e2538.bundle create mode 100644 resource/public/game_resource/Android/f87707a74b78049d733ca989c2133203.bundle create mode 100644 resource/public/game_resource/Android/f8979c721c375c29666d4a5de6f80d47.bundle create mode 100644 resource/public/game_resource/Android/f96f8e9f3300e4b5fe3a0329bb472192.bundle create mode 100644 resource/public/game_resource/Android/f9cf0c9270cc4c86c3bbe1c6a59cedfb.bundle create mode 100644 resource/public/game_resource/Android/fa604cc14d2fa84d873f8beae59a98d7.bundle create mode 100644 resource/public/game_resource/Android/fa7506a9e195e2a61b8d0d4c39736590.bundle create mode 100644 resource/public/game_resource/Android/faadbe503ae3220703fda89ae8e31684.bundle create mode 100644 resource/public/game_resource/Android/fab2a2a59c80d448c7a0d4dd8943270d.bundle create mode 100644 resource/public/game_resource/Android/fabff72fe9327d045b8d2763fe22480a.bundle create mode 100644 resource/public/game_resource/Android/fba92f686914fced10370bcdf751ce89.bundle create mode 100644 resource/public/game_resource/Android/fbb0369690560661ad96cb7be3848b50.bundle create mode 100644 resource/public/game_resource/Android/fce99cc5c4661ea08a1df037a2f408f0.bundle create mode 100644 resource/public/game_resource/Android/fd1b94cd75c602131e932a7d11e7537a.bundle create mode 100644 resource/public/game_resource/Android/fdaeb2886c4ce48f41c0ceb6b7c9d81c.bundle create mode 100644 resource/public/game_resource/Android/fddcc24b84605d1e0ec358bb6b892acd.bundle create mode 100644 resource/public/game_resource/Android/fe355f0d364e651b274fdb96459b275e.bundle create mode 100644 resource/public/game_resource/Android/fe556cd5c94846a97fc4a87e0b1b5111.bundle create mode 100644 resource/public/game_resource/Android/fecd0294f765299a6136479a5da1f235.bundle create mode 100644 resource/public/game_resource/Android/fee3bbfcd2cb271b0848cf0ec4c9dbe4.bundle create mode 100644 resource/public/game_resource/Android/ff253e3fab719c02d0972807322e86e2.bundle create mode 100644 resource/public/game_resource/Android/ff37b93a2d2af9ac77bc7ed1bef775c4.bundle create mode 100644 resource/public/web/account_help.html create mode 100644 resource/public/web/assets/animatecss/animate.css create mode 100644 resource/public/web/assets/bootstrap/css/bootstrap-grid.min.css create mode 100644 resource/public/web/assets/bootstrap/css/bootstrap-reboot.min.css create mode 100644 resource/public/web/assets/bootstrap/css/bootstrap.min.css create mode 100644 resource/public/web/assets/bootstrap/js/bootstrap.bundle.min.js create mode 100644 resource/public/web/assets/dropdown/css/style.css create mode 100644 resource/public/web/assets/dropdown/js/navbar-dropdown.js create mode 100644 resource/public/web/assets/embla/embla.min.js create mode 100644 resource/public/web/assets/embla/script.js create mode 100644 resource/public/web/assets/formoid/formoid.min.js create mode 100644 resource/public/web/assets/images/logo5.png create mode 100644 resource/public/web/assets/images/photo-1471014706828-7f1b12e6d88b.jpeg create mode 100644 resource/public/web/assets/images/photo-1483478550801-ceba5fe50e8e.jpeg create mode 100644 resource/public/web/assets/images/photo-1488509082528-cefbba5ad692.jpeg create mode 100644 resource/public/web/assets/images/photo-1493711662062-fa541adb3fc8.jpeg create mode 100644 resource/public/web/assets/images/photo-1512429234305-12fe5b0b0f07.jpeg create mode 100644 resource/public/web/assets/images/photo-1522091066250-665186289043.jpeg create mode 100644 resource/public/web/assets/images/photo-1527335480088-278dbeec0ad5.jpeg create mode 100644 resource/public/web/assets/images/photo-1528870884180-5649b20f6435.jpeg create mode 100644 resource/public/web/assets/images/photo-1563208964-a455770abf67.jpeg create mode 100644 resource/public/web/assets/images/photo-1564049489314-60d154ff107d.jpeg create mode 100644 resource/public/web/assets/images/photo-1565870100382-f0a510db3cd1.jpeg create mode 100644 resource/public/web/assets/images/photo-1568915567812-757e15929fe9.jpeg create mode 100644 resource/public/web/assets/images/photo-1569078449082-d264d9e239c5.jpeg create mode 100644 resource/public/web/assets/images/photo-1570942948935-8c1956d8284a.jpeg create mode 100644 resource/public/web/assets/images/photo-1581803118522-7b72a50f7e9f.jpeg create mode 100644 resource/public/web/assets/images/photo-1586763749650-70d7996310d0.jpeg create mode 100644 resource/public/web/assets/images/photo-1587377838987-42165934161d.jpeg create mode 100644 resource/public/web/assets/images/photo-1590589712122-59c2bf506bf5.jpeg create mode 100644 resource/public/web/assets/images/photo-1591084728795-1149f32d9866.jpeg create mode 100644 resource/public/web/assets/images/photo-1615680022647-99c397cbcaea.jpeg create mode 100644 resource/public/web/assets/images/photo-1617224908579-c92008fc08bb.jpeg create mode 100644 resource/public/web/assets/images/photo-1623743993875-03d6a5c7c709.jpeg create mode 100644 resource/public/web/assets/images/photo-1623934199716-dc28818a6ec7.jpeg create mode 100644 resource/public/web/assets/images/photo-1626240130051-68871c71de47.jpeg create mode 100644 resource/public/web/assets/images/photo-1636624498233-0c3c5ab4186e.jpeg create mode 100644 resource/public/web/assets/images/photo-1639656333400-ee5240f757a0.jpeg create mode 100644 resource/public/web/assets/images/photo-1648747066670-034eab81a631.jpeg create mode 100644 resource/public/web/assets/mbr-switch-arrow/mbr-switch-arrow.js create mode 100644 resource/public/web/assets/mobirise/css/additional.css create mode 100644 resource/public/web/assets/parallax/jarallax.css create mode 100644 resource/public/web/assets/parallax/jarallax.js create mode 100644 resource/public/web/assets/scrollgallery/scroll-gallery.js create mode 100644 resource/public/web/assets/smoothscroll/smooth-scroll.js create mode 100644 resource/public/web/assets/socicon/css/styles.css create mode 100644 resource/public/web/assets/socicon/fonts/socicon.eot create mode 100644 resource/public/web/assets/socicon/fonts/socicon.svg create mode 100644 resource/public/web/assets/socicon/fonts/socicon.ttf create mode 100644 resource/public/web/assets/socicon/fonts/socicon.woff create mode 100644 resource/public/web/assets/socicon/fonts/socicon.woff2 create mode 100644 resource/public/web/assets/theme/css/style.css create mode 100644 resource/public/web/assets/theme/js/script.js create mode 100644 resource/public/web/assets/web/assets/mobirise-icons2/mobirise2.css create mode 100644 resource/public/web/assets/web/assets/mobirise-icons2/mobirise2.eot create mode 100644 resource/public/web/assets/web/assets/mobirise-icons2/mobirise2.svg create mode 100644 resource/public/web/assets/web/assets/mobirise-icons2/mobirise2.ttf create mode 100644 resource/public/web/assets/web/assets/mobirise-icons2/mobirise2.woff create mode 100644 resource/public/web/assets/ytplayer/index.js create mode 100644 resource/public/web/images/bg.png create mode 100644 resource/public/web/images/delete-account-1.png create mode 100644 resource/public/web/images/delete-account-2.png create mode 100644 resource/public/web/images/delete-account-3.png create mode 100644 resource/public/web/images/lily.png create mode 100644 resource/public/web/images/main-view.png create mode 100644 resource/public/web/images/mekapang_logo.png create mode 100644 resource/public/web/index.html create mode 100644 resource/public/web/policy.css create mode 100644 resource/public/web/privacy_policy.html create mode 100644 resource/public/web/service_policy.html create mode 100644 resource/public/web/unit-info.html create mode 100644 test/deduction-fixes.test.ts create mode 100644 tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..bf8a76a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +#**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +#package-lock.json +LICENSE +README.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d593b86 --- /dev/null +++ b/.env.example @@ -0,0 +1,41 @@ +# Copy to .env or .env.dev and fill locally. Do not commit real values. + +# Mongo +mongodbURI= +mongoDBUserName= +mongoDBPassword= + +# MySQL +mysqlHost= +mysqlPort=3306 +mysqlUser= +mysqlPassword= +mysqlDBName= + +# Redis +redisGlobalHostName= +redisLocalHostName= +redisPortNumber=6379 +redisPassword= +redisGameDBIndex=0 +redisUserDBIndex=1 +redisLocalDBIndex=2 + +# Secrets +occupancyScrambleKey= +admitTicketSecret= +duelTicketSecret= +overseerUser=admin +overseerPassword= +googleClientID= +googleClientSecret= +facebookAppID= +facebookAppSecret= +androidPackageName=com.nexforge.app + +# License placeholders +appLicenseCode= +serverLicenseCode= + +# Redis FLUSHDB on clockwork/nexus boot. Defaults to true when NODE_ENV is not production. +allowPocketFlushOnBoot=false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b78f2e9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.bundle filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6f7576d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,16 @@ +name: test + +on: + push: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "18" + - run: npm install + - run: npm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..187e496 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/node_modules +/.vscode +package-lock.json +/dist +.env +.env.dev +.env.local +resource/private/google_key/*.json +resource/private/firebase/*.json +resource/private/global_ssl/*.key +resource/private/global_ssl/*.crt +resource/private/global_ssl/*.pem +!**/.gitkeep diff --git a/Dockerfile.asset b/Dockerfile.asset new file mode 100644 index 0000000..8b9596b --- /dev/null +++ b/Dockerfile.asset @@ -0,0 +1,12 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +COPY ["./resource/public","./resource/public"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +EXPOSE 80 +#RUN chown -R node /usr/src/app +CMD npm run start-asset diff --git a/Dockerfile.clockwork b/Dockerfile.clockwork new file mode 100644 index 0000000..4cff52a --- /dev/null +++ b/Dockerfile.clockwork @@ -0,0 +1,11 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +#RUN chown -R node /usr/src/app +CMD npm run start-clockwork + diff --git a/Dockerfile.core b/Dockerfile.core new file mode 100644 index 0000000..8a89dc3 --- /dev/null +++ b/Dockerfile.core @@ -0,0 +1,11 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +EXPOSE 443 +#RUN chown -R node /usr/src/app +CMD npm run start-core diff --git a/Dockerfile.duel b/Dockerfile.duel new file mode 100644 index 0000000..92e09e8 --- /dev/null +++ b/Dockerfile.duel @@ -0,0 +1,11 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +#RUN chown -R node /usr/src/app +CMD npm run start-duel + diff --git a/Dockerfile.nexus b/Dockerfile.nexus new file mode 100644 index 0000000..36c57f5 --- /dev/null +++ b/Dockerfile.nexus @@ -0,0 +1,11 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +EXPOSE 500 +#RUN chown -R node /usr/src/app +CMD npm run start-nexus diff --git a/Dockerfile.overseer b/Dockerfile.overseer new file mode 100644 index 0000000..ec531bf --- /dev/null +++ b/Dockerfile.overseer @@ -0,0 +1,12 @@ +FROM node:18.14.2 +ENV NODE_ENV=production +WORKDIR /usr/src/app +COPY ["package.json","./"] +COPY ["./dist/","./dist/"] +COPY ["./resource/public/admin_page/build","./resource/public/admin_page/build"] +#RUN apt isntall npm +RUN npm install +#RUN npm i -g pm2 +EXPOSE 555 +#RUN chown -R node /usr/src/app +CMD npm run start-overseer diff --git a/README.md b/README.md index 6806624..97c2b44 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,324 @@ -# RebuildMobileGameServer +# NexForgeServer + +NexForge 몚바음 게임 혞슀튞. 읞슝·로비 HTTP, Socket.IO 맀칭, PVP 런타임, 슀쌀쀄러, 정적 에셋, 욎영 윘솔을 **프로섞슀 닚위로 분늬**한닀. + +| | | +| --- | --- | +| 팚킀지 | `nexforge_server` `1.0.1` | +| 런타임 | Node.js 18, TypeScript (CommonJS → `dist/`) | +| 저장소 | [Wayhope/NexForgeServer](https://github.com/Wayhope/NexForgeServer) | +| 데읎터 | MySQL 8 (영속), Redis (섞션·맀칭·마슀터 캐시), MongoDB (튞레읎슀) | +| 띌읎선슀 | ISC | + +```mermaid +flowchart LR + Client[Mobile Client] + Core[core HTTPS] + Nexus[nexus Socket.IO] + Duel[duel Socket.IO] + Clock[clockwork] + Asset[asset HTTP] + Overseer[overseer] + MySQL[(MySQL)] + Redis[(Redis)] + Mongo[(MongoDB)] + + Client --> Core + Client --> Nexus + Client --> Duel + Client --> Asset + Overseer --> Redis + Overseer --> MySQL + Clock --> Redis + Clock --> MySQL + Core --> MySQL + Core --> Redis + Core --> Mongo + Nexus --> Redis + Duel --> Redis + Duel --> MySQL +``` + +- [섞션 흐멄](#섞션-흐멄) +- [프로섞슀](#프로섞슀) +- [빠륞 시작](#빠륞-시작) +- [HTTP API (core)](#http-api-core) +- [Socket.IO](#socketio) +- [저장소](#저장소) +- [환겜 변수](#환겜-변수) +- [Docker](#docker) +- [욎영 윘솔](#욎영-윘솔) +- [디렉터늬](#디렉터늬) +- [용얎](#용얎) +- [테슀튞](#테슀튞) + +## 섞션 흐멄 + +1. **가입 / 로귞읞** — `POST /app/gamebase/sign`, `POST /app/gamebase/login`. 응답의 occupancy ticket을 헀더 `SessionToken`에 넣는닀. 티쌓은 `occupancyScrambleKey`로 슀크랚랔한닀. +2. **로비** — `/app/lobby/*`는 occupancy 게읎튞륌 탄닀. 마슀터데읎터, 읞벀, 상점, 캠페읞, 크레읎튞 등읎 여Ʞ 있닀. 로비 요청은 occupancy 크레딧을 소몚한닀. +3. **맀칭** — nexus에 Socket.IO로 접속한닀. 연결 믞듀웚얎가 occupancy + 멀버 게읎튞륌 검사한닀. 캐죌얌/랭크 큐륌 1쎈 죌Ʞ로 슀캔한닀. +4. **대전** — 맀칭읎 되멎 `success-matching` → ë°© 생성 후 `success-generate-game`곌 duel ticket(혞슀튞 IP, room id)을 받는닀. 큎띌는 duel 러너에 `join-game`한닀. 혞슀튞는 occupancy가 가장 낮은 duel 러너륌 고륞닀. +5. **종료** — ì–‘ìž¡ `end-game` 늬포튞가 음치하멎 볎상·등꞉을 처늬하고 occupancy륌 정늬한닀. + +점검 몚드(`server status --inspection`)읎멎 `/app` 요청읎 혞슀튞 헬슀 게읎튞에서 거절된닀. + +## 프로섞슀 + +로컬은 `nodemon`윌로 `.ts`륌 직접 띄우고, 배포는 `npm run build` 후 `dist`륌 싀행한닀. `NODE_ENV=production`읎멎 `.env`, ê·ž 왞에는 `.env.dev` (`config/host-spec.ts`). + +| 혞슀튞 | 엔튞늬 | 개발 / 배포 | Ʞ볞 포튞 (compose) | 역할 | +| --- | --- | --- | --- | --- | +| **core** | `app/core-app/core-app.ts` | `dev-core` / `start-core` | 443 | 게임 HTTP. 프로덕션은 CPU당 워컀, 개발은 워컀 1개 | +| **nexus** | `app/nexus-app/nexus-app.ts` | `dev-nexus` / `start-nexus` | 500 | 맀칭 허뾌. 캐죌얌·랭크 큐 슀캔, occupancy 큮럭. 개발 Ʞ동 시 레읞 Redis `FLUSHDB` | +| **duel** | `app/duel-app/duel-app.ts` | `dev-duel` / `start-duel` | `duelRunnerPortBase`부터 | PVP 러너. 프로덕션은 CPU 수, 개발은 2개 | +| **clockwork** | `app/clockwork-app/clockwork-app.ts` | `dev-clockwork` / `start-clockwork` | — | 시슌·맵·IP 밎·우펞 만료 크론 + Redis 하읎드레읎션. 개발 Ʞ동 시 마슀터/멀버 Redis `FLUSHDB` | +| **asset** | `app/asset-app/asset-app.ts` | `dev-asset` / `start-asset` | 80 | `resource/public/web`, `game_resource` | +| **overseer** | `app/overseer-app/overseer-app.ts` | `dev-overseer` / `start-overseer` | 555 | 욎영 윘솔. Basic Auth `overseerUser`(Ʞ볞 `admin`) / `overseerPassword` | + +권장 Ʞ동 순서: MySQL · Mongo · Redis → **clockwork** → core · nexus · duel · asset · overseer. + +## 빠륞 시작 +**필요 환겜:** Node.js 18, npm, Docker (DB·Redis용). +```bash +npm install +cp .env.example .env.dev +``` + +PowerShell읎멎 `Copy-Item .env.example .env.dev`. -## Getting started +`.env.example`은 DB·Redis·시크늿 곚격만 있닀. 포튞, 토큰 TTL, 신규 회원 Ʞ볞값, 아읎템 윔드는 `config/runtime-spec.ts`, `config/play-baseline-spec.ts`, `config/host-spec.ts`, `config/duel-host-spec.ts`가 읜윌므로 `.env.dev`에 맞춰 채욎닀. -To make it easy for you to get started with GitLab, here's a list of recommended next steps. +HTTPS는 `resource/private/global_ssl/certificate.crt`, `private.key`가 있윌멎 로드한닀. Firebase / Google 킀는 `resource/private/` (gitignore). -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! +```bash +# DB만 로컬 컚테읎너 +docker compose -f docker_compose/db_server/docker-compose.yml up -d -## Add your files +npm run dev-clockwork +npm run dev-core +npm run dev-nexus +npm run dev-duel +npm run dev-asset +npm run dev-overseer +``` -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: +배포 바읎너늬: +```bash +npm run build +NODE_ENV=production npm run start-clockwork +NODE_ENV=production npm run start-core +# start-nexus / start-duel / start-asset / start-overseer ``` -cd existing_repo -git remote add origin https://wayhope.kro.kr/gitlab/wayhope/rebuildmobilegameserver.git -git branch -M main -git push -uf origin main + +욎영 UI는 React (`resource/public/admin_page`). 빌드 결곌륌 overseer가 `/admin`윌로 서빙한닀. + +```bash +cd resource/public/admin_page && npm install && npm run build ``` -## Integrate with your tools +## HTTP API (core) + +믞듀웚얎: `/app` → 헬슀 + 큎띌 띌읎선슀. `/app/gamebase` → IP occupancy. `/app/lobby` → `SessionToken` occupancy. `/server` → 서버 띌읎선슀. + +### `/app/gamebase` + +| Method | Path | | +| --- | --- | --- | +| POST | `/sign` | 가입 (Google / Facebook / Guest) | +| POST | `/login` | 로귞읞, occupancy ticket 발꞉ | +| GET | `/app-data` | 큎띌 팩 (앱 버전, OAuth 공개값) | +| GET | `/server-data` | 혞슀튞 상태 (`Normal` / `Inspection`) | + +### `/app/lobby` + +| Method | Path | | +| --- | --- | --- | +| GET | `/session-check` | occupancy 유횚 확읞 | +| GET | `/gamedata` | 마슀터데읎터 | +| GET | `/update-data` | 덾타 | +| GET | `/season-code` | 시슌 | +| GET | `/pvp-map-code` | 대전 ë§µ | +| POST | `/newuser` | 신규 섞팅 | +| POST | `/change-name` | 닉넀임 | +| POST | `/account-mapping` | 계정 연동 | +| DELETE | `/account-withdrawal` | 탈퇮 | +| POST | `/fcm-option` | 푞시 옵튞 | +| GET | `/tutorial-step`, `/tutorial-next-step` | 튜토늬얌 | +| POST | `/adventure`, `/adventure-clear` | 캠페읞 | +| POST | `/buy-product` | 읞게임 상점 | +| POST | `/google-purchase` | Play 결제 | +| POST | `/change-blue-coin` | 재화 교환 | +| POST | `/register-chest`, `/open-chest`, `/open-instant-chest` | 크레읎튞 | +| POST | `/open-gift` | 우펞 | +| POST | `/unit-upgrade`, `/unit-order-setting`, `/unit-parts-setting` | Ʞ첎 | +| POST | `/parts-upgrade`, `/decompose-parts`, `/increase-parts-slot` | 파잠 | +| POST | `/making-product` | 제작 | +| GET | `/roulette-reward` | 룰렛 | -- [ ] [Set up project integrations](http://183.102.165.194:501/gitlab/wayhope/rebuildmobilegameserver/-/settings/integrations) +### `/auth` -## Collaborate with your team +| Method | Path | | +| --- | --- | --- | +| POST | `/facebook/remove/callback` | Facebook 삭제 윜백 | +| GET | `/admob/reward` | ꎑ고 볎상 | -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) +## Socket.IO -## Test and Deploy +접속 시 occupancy 티쌓을 검사한닀. nexus는 Redis adapter(레읞)로 소쌓을 맞춘닀. -Use the built-in continuous integration in GitLab. +### nexus (맀칭) -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) +큎띌 → 서버 -*** +| event | | +| --- | --- | +| `start-free-matching` / `stop-free-matching` | 캐죌얌 큐 | +| `start-rank-matching` / `stop-rank-matching` | 랭크 큐 | -# Editing this README +서버 → 큎띌 -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. +| event | | +| --- | --- | +| `success-matching` | 상대 확정 | +| `success-generate-game` | 방·duel ticket (혞슀튞 IP, room id) | +| `fail-generate-game` | ë°© 생성 싀팚 | -## Suggestions for a good README +끊Ʞ멎 큐에서 빌고 occupancy륌 닫는닀. -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. +### duel (전투) -## Name -Choose a self-explaining name for your project. +큎띌 → 서버 -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. +| event | | +| --- | --- | +| `join-game` | ë°© 입장 | +| `exit-game` | 쀑도 퇎장 | +| `end-turn` | 페읎슈 종료 | +| `end-game` | 대전 종료 늬포튞 | +| `relay` | 전투 슀튞늌 쀑계 | -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. +서버 → 큎띌 -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. +| event | | +| --- | --- | +| `start-turn` | 페읎슈 시작 | +| `end-game` | 종료·볎상 | +| `stop-game` | 쀑닚 | +| `break-game` | 강제 종료 | +| `stream-data` | ꎀ전·쀑계 데읎터 | -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. +`end-game`은 ì–‘ìž¡ 승팚 늬포튞가 음치할 때만 볎상을 확정한닀. `relay` 페읎로드는 바읎튞 상한읎 있닀. -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. +## 저장소 -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. +| | 용도 | +| --- | --- | +| **MySQL** | 회원, 읞벀, 결제, 캠페읞 등 영속 데읎터 (`config/sql-link-spec.ts`) | +| **Redis Ꞁ로벌** | `redisGameDBIndex` 마슀터/슀테읎지/점검, `redisUserDBIndex` 멀버 occupancy·읞벀 캐시 | +| **Redis 레읞** | `redisLocalDBIndex` 맀칭 큐, Socket.IO adapter. 개발 Ʞ동 시 flush (`allowPocketFlushOnBoot`) | +| **MongoDB** | 혞슀튞/멀버 튞레읎슀 (`config/trace-store-spec.ts`) | -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. +clockwork는 개발에서 Ꞁ로벌 Redis륌 flush한 ë’€ 마슀터데읎터, 캠페읞, 강화값, IP ë°Ž, 혞슀튞 헬슀륌 닀시 넣는닀. 프로덕션(`NODE_ENV=production`)에서는 Ʞ볞윌로 flush하지 않는닀. 강제하렀멎 `allowPocketFlushOnBoot=true`. 같은 Redis륌 환겜끌늬 읞덱슀로 분늬하지 않윌멎 데읎터가 지워진닀. -## Contributing -State if you are open to contributions and what your requirements are for accepting them. +clockwork 룚틎: 시슌 슀탬프(`1,3,5,7,9,11`월 1음 0시), 대전 ë§µ, IP occupancy, 우펞 만료. -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. +## 환겜 변수 + +`.env` / `.env.dev`는 컀밋하지 않는닀. 연결·시크늿 킀는 `.env.example`을 볞닀. -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. +| 구분 | 변수 | +| --- | --- | +| Mongo | `mongodbURI` `mongoDBUserName` `mongoDBPassword` | +| MySQL | `mysqlHost` `mysqlPort` `mysqlUser` `mysqlPassword` `mysqlDBName` | +| Redis | `redisGlobalHostName` `redisLocalHostName` `redisPortNumber` `redisPassword` `redisGameDBIndex` `redisUserDBIndex` `redisLocalDBIndex` `redisFamilyNumber`(볎통 `4`) | +| 포튞 | `coreHostPort` `nexusHostPort` `adminServerPort` `resourceServerPort` `duelRunnerPortBase` | +| 시크늿 | `admitTicketSecret` `duelTicketSecret` `occupancyScrambleKey` `overseerPassword` | +| 띌읎선슀 | `appLicenseCode` `serverLicenseCode` | +| OAuth | `googleClientID` `googleClientSecret` `facebookAppID` `facebookAppSecret` `androidPackageName` | +| TTL | `expiredRefreshTokenTime` `expiredAccessTokenTime` `expiredPVPTokenTime` `occupancyLapseSpan` | -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. +`allowPocketFlushOnBoot`는 Ʞ동 시 Redis `FLUSHDB` 여부. 명시하지 않윌멎 프로덕션 `false`, ê·ž 왞 `true`. + +플레읎 Ʞ볞값·아읎템 윔드는 `config/play-baseline-spec.ts`가 같은 방식윌로 env륌 읜는닀. + +## Docker + +읎믞지는 `nexforgehost/...`. 빌드 전에 `npm run build`. 베읎슀는 `node:18.14.2`. + +| Dockerfile | compose 포튞 | +| --- | --- | +| `Dockerfile.core` | 443 | +| `Dockerfile.nexus` | 500 | +| `Dockerfile.duel` | 2000+ | +| `Dockerfile.clockwork` | — | +| `Dockerfile.asset` | 80 | +| `Dockerfile.overseer` | 555 | + +- `docker_compose/db_server` — Mongo, MySQL (`MONGO_INITDB_ROOT_*`, `MYSQL_ROOT_PASSWORD`) +- `docker_compose/mem_server` — Redis, overseer, clockwork +- `docker_compose/main_server` — Redis, core, duel +- `docker_compose/resource_server` — asset (`resource/public` 볌륚) +- `docker-compose-build.yml` — 읎믞지 빌드용 (core, nexus, duel, clockwork, overseer, asset) + +## 욎영 윘솔 + +`/admin` Basic Auth. `POST /admin/cmd`에 `{ "cmd": "..." }`. + +``` +help +server status --inspection | --normal +admin give gift -n -m -e -u|-p|-i [-a amount] +admin give unit|item|parts -n --all +``` + +## 디렉터늬 + +``` +app/core-app/ HTTP API +app/nexus-app/ 맀칭 허뾌 +app/duel-app/ 러너 팩토늬 +app/duel-runner/ 방·페읎슈·슀튞늌 +app/clockwork-app/ 크론·캐시 하읎드 +app/asset-app/ 정적 파음 +app/overseer-app/ 욎영 윘솔 +config/ 포튞, SQL/Redis, JWT, 플레읎 Ʞ볞값 +data/ DTO, 엔티티, 도메읞 몚덞 +handler/ 대전 종료 볎상, 등꞉/밮드 +manager/ Redis pocket, 로귞 슀토얎 +middleware/ 띌읎선슀, occupancy, 헬슀 +modules/ SQL/Redis/Mongo/OAuth/JWT +resource/public/ 웹, 게임 늬소슀, admin_page +resource/private/ SSL, Firebase, Google +docker_compose/ db / mem / main / resource +test/ node:test +``` + +## 용얎 + +낎부 윔드와 HTTP 겜로가 닀륎닀. 큎띌 혞환을 위핎 띌우튞는 Ʞ졎 읎늄을 유지한닀. + +| 윔드 | HTTP / 큎띌 | 의믞 | +| --- | --- | --- | +| occupancy | `SessionToken` | 로귞읞 섞션 | +| vessel | `/unit-upgrade` | Ʞ첎 | +| gadget | `/parts-upgrade` | 파잠 | +| crate | `/open-chest` | 크레읎튞 | +| parcel | `/open-gift` | 우펞 | +| relic | item | 재화·아읎템 | +| nexus | matching hub | 맀칭 | +| duel | PVP runner | 대전 | +| clockwork | scheduler | 크론 | +| overseer | `/admin` | 욎영 윘솔 | + +## 테슀튞 + +```bash +npm test +``` -## License -For open source projects, say how it is licensed. +Node.js 낎장 테슀튞 러너 (`test/deduction-fixes.test.ts`). 맀칭 승팚 교찚검슝, 캠페읞 큎늬얎 권위, IAP purchaseState, occupancy 크레딧, SQL amount 바읞딩, Redis flush 정책, duel 로드밞런서륌 검사한닀. -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +CI: `.github/workflows/test.yml` — push / PR에서 Node 18 + `npm test`. diff --git a/app/asset-app/asset-app.ts b/app/asset-app/asset-app.ts new file mode 100644 index 0000000..99ca14b --- /dev/null +++ b/app/asset-app/asset-app.ts @@ -0,0 +1,61 @@ +//Node Module +//Config +import * as config from "../../config/host-spec"; +import cluster from "cluster"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; +import ReadJsonFile from "../../modules/parse-unit/json-reader"; +import httpExpressAppLoader from "./source/loader/plain-express-boot"; +import { StampHostLabel } from "../../manager/trace-store-keeper/trace-store-spec"; +import { BindClusterRefork, BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; + +//Preset + +cluster.schedulingPolicy = cluster.SCHED_RR; + +StartServer(); +StampHostLabel(__filename); + + +async function StartServer() { + if (cluster.isMaster) { + await StartMasterWorker(); + } else { + await StartWorker(); + } +} + +async function StartMasterWorker() { + // os.cpus().forEach((cpu) => { + // let worker = cluster.fork(); + // stampLogPrinter.Log(__filename, config.workerForkMSG + worker.id); + // }); + + ServerIntro(); + + let worker = cluster.fork(); + stampLogPrinter.Log(__filename, config.workerForkMSG + worker.id); + + BindClusterRefork(); + BindFatalProcessGuards(); + + process.on('exit', function () { + stampLogPrinter.Log(__filename, config.exitServerMessage); + }); +} + +async function StartWorker() { + const httpApp = new config.App().application; + httpExpressAppLoader(httpApp); + + httpApp.listen(config.resourceServerPort, () => { + stampLogPrinter.Log(__filename, config.ServerStartHttpMSG(config.resourceServerPort)); + }); + + BindFatalProcessGuards(); +} + +function ServerIntro(): void { + let packageData = ReadJsonFile(config.packagePath); + console.log(config.serverLogo); + stampLogPrinter.Log(__filename, `${packageData["name"]} version: ${packageData["version"]}`); +} \ No newline at end of file diff --git a/app/asset-app/source/loader/plain-express-boot.ts b/app/asset-app/source/loader/plain-express-boot.ts new file mode 100644 index 0000000..a87ad43 --- /dev/null +++ b/app/asset-app/source/loader/plain-express-boot.ts @@ -0,0 +1,10 @@ +import express, { Application, NextFunction, Request, Response } from 'express'; +//import fileNameParser from '../../../../modules/parse-unit/path-leaf-reader'; +import redirectMiddleWare from '../../../../middleware/bounce-gate'; +import { swaggerUI, specs } from '../../../../modules/openapi-unit/openapi-unit' +import basicAuthModule from "../../../../modules/plain-auth-unit/plain-auth-unit"; + +export default (app: Application) => { + app.use('/', express.static("./resource/public/web")); + app.use('/game_resource', express.static("./resource/public/game_resource")); +} diff --git a/app/clockwork-app/clockwork-app.ts b/app/clockwork-app/clockwork-app.ts new file mode 100644 index 0000000..a87a485 --- /dev/null +++ b/app/clockwork-app/clockwork-app.ts @@ -0,0 +1,58 @@ +import * as config from "../../config/host-spec"; + +import * as duelStageClock from "./source/routine/duel-stage-clock"; +import * as ipOccupancyClock from "./source/routine/ip-occupancy-clock"; +import * as cycleClock from "./source/routine/cycle-clock"; +import * as parcelLapseClock from "./source/routine/parcel-lapse-clock"; + +import * as sqlUnit from "../../modules/sql-unit/sql-link"; +//import fileNameParser from "../../modules/parse-unit/path-leaf-reader"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; +import * as pocketLink from "../../modules/pocket-link/pocket-link"; +import * as inspectionManager from "../../manager/pocket-keeper/host-hold-keeper/host-hold-keeper"; +import { ServerStateType } from "../../data/dto/core-packet"; +import * as ipBanController from "../../manager/pocket-keeper/ip-block-keeper/ip-block-keeper"; +import * as gameUpgradeValueManager from "../../manager/pocket-keeper/refine-pocket-keeper/refine-pocket-keeper"; +import * as campaignPocketKeeper from "../../manager/pocket-keeper/campaign-pocket-keeper/campaign-pocket-keeper"; +import * as gameDataCacheManager from "../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import { BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; +import { FlushPocketIfAllowed } from "../../modules/pocket-link/pocket-boot-flush"; + + +console.log(config.serverLogo); + +StartServiceSysetm(); + +async function StartServiceSysetm() { + await FlushPocketIfAllowed(pocketLink.MasterPocketClient(), "master"); + await FlushPocketIfAllowed(pocketLink.MemberPocketClient(), "member"); + + sqlUnit.BootSqlPool(); + + await InitSystemData(); + await duelStageClock.StartRoutine(); + await ipOccupancyClock.StartRoutine(); + await cycleClock.StartRoutine(); + await parcelLapseClock.StartRoutine(); + BindFatalProcessGuards(); +} + +async function StopServiceSystem() { + await duelStageClock.StopRoutine(); + await ipOccupancyClock.StopRoutine(); + await cycleClock.StopRoutine(); + await parcelLapseClock.StopRoutine(); +} + +async function InitSystemData() { + await inspectionManager.StampHostHealth(ServerStateType.Normal); + await ipBanController.HydrateBlockPocket(); + await gameUpgradeValueManager.HydrateRefinePocket(); + await gameDataCacheManager.HydrateMasterPocket(); + await campaignPocketKeeper.HydrateCampaignPocket(); +} + +process.on('exit', async function () { + await StopServiceSystem(); + stampLogPrinter.Log(__filename, config.exitServerMessage); +}); \ No newline at end of file diff --git a/app/clockwork-app/source/routine/cycle-clock.ts b/app/clockwork-app/source/routine/cycle-clock.ts new file mode 100644 index 0000000..2851735 --- /dev/null +++ b/app/clockwork-app/source/routine/cycle-clock.ts @@ -0,0 +1,17 @@ +import * as cycleKeeper from "../../../../manager/pocket-keeper/cycle-keeper/cycle-keeper"; +import * as cron from "node-cron" +//'55 59 23 * * *' +const dayScheduleWorker = cron.schedule('0 0 0 1 1,3,5,7,9,11 *', ScheduleWorker, { scheduled: false }); + +export async function StartRoutine() { + await cycleKeeper.StampCycle(); + dayScheduleWorker.start(); +} + +export async function StopRoutine() { + dayScheduleWorker.stop(); +} + +async function ScheduleWorker(now: any) { + await cycleKeeper.StampCycle(); +} diff --git a/app/clockwork-app/source/routine/duel-stage-clock.ts b/app/clockwork-app/source/routine/duel-stage-clock.ts new file mode 100644 index 0000000..5e0c468 --- /dev/null +++ b/app/clockwork-app/source/routine/duel-stage-clock.ts @@ -0,0 +1,18 @@ +import * as duelStageKeeper from "../../../../manager/pocket-keeper/duel-stage-keeper/duel-stage-keeper"; +import * as cron from "node-cron" +//'55 59 23 * * *' +const dayScheduleWorker = cron.schedule('*/5 * * * * *', ScheduleWorker, { scheduled: false }); + +export async function StartRoutine() { + await duelStageKeeper.HydrateDuelStageCodes(); + await duelStageKeeper.RefreshDuelStageCodes(); + dayScheduleWorker.start(); +} + +export async function StopRoutine() { + dayScheduleWorker.stop(); +} + +async function ScheduleWorker(now: any) { + await duelStageKeeper.RefreshDuelStageCodes(); +} diff --git a/app/clockwork-app/source/routine/ip-occupancy-clock.ts b/app/clockwork-app/source/routine/ip-occupancy-clock.ts new file mode 100644 index 0000000..3aafa49 --- /dev/null +++ b/app/clockwork-app/source/routine/ip-occupancy-clock.ts @@ -0,0 +1,20 @@ +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as hubOccupancyKeeper from "../../../../manager/pocket-keeper/ip-occupancy-keeper/ip-occupancy-keeper"; +import * as cron from "node-cron" + + +const minuteScheduleWorker = cron.schedule('* * * * *', MinuteScheduleWorker, { scheduled: false }); + +export async function StartRoutine() { + minuteScheduleWorker.start(); +} + +export async function StopRoutine() { + minuteScheduleWorker.stop(); +} + +async function MinuteScheduleWorker(now: any) { + await hubOccupancyKeeper.SweepLapsedOccupancy(); + stampLogPrinter.Log(__filename, "Checked Expired Session"); +} \ No newline at end of file diff --git a/app/clockwork-app/source/routine/parcel-lapse-clock.ts b/app/clockwork-app/source/routine/parcel-lapse-clock.ts new file mode 100644 index 0000000..d437aac --- /dev/null +++ b/app/clockwork-app/source/routine/parcel-lapse-clock.ts @@ -0,0 +1,16 @@ +import * as parcelKeeper from "../../../../manager/pocket-keeper/parcel-keeper/parcel-keeper"; +import * as cron from "node-cron" +//'55 59 23 * * *' +const dayScheduleWorker = cron.schedule('0 0 0 * * *', ScheduleWorker, { scheduled: false }); + +export async function StartRoutine() { + dayScheduleWorker.start(); +} + +export async function StopRoutine() { + dayScheduleWorker.stop(); +} + +async function ScheduleWorker(now: any) { + await parcelKeeper.DropLapsedParcels(); +} diff --git a/app/clockwork-app/source/routine/push-dispatch-clock.ts b/app/clockwork-app/source/routine/push-dispatch-clock.ts new file mode 100644 index 0000000..622432d --- /dev/null +++ b/app/clockwork-app/source/routine/push-dispatch-clock.ts @@ -0,0 +1,14 @@ +// import * as fcmManager from "../../../../../manager/fcm-manager/fcm-manager"; + +// const fcmCheckingInterval = 10 * 1000; //10쎈 + +// let fcmWorker: NodeJS.Timer; + +// export function StartWorker() { +// fcmWorker = setInterval(fcmManager.CheckMessage2Send, fcmCheckingInterval); +// } + +// export function StopScheduleWorker() { +// clearInterval(fcmWorker); +// } + diff --git a/app/core-app/core-app.ts b/app/core-app/core-app.ts new file mode 100644 index 0000000..2621977 --- /dev/null +++ b/app/core-app/core-app.ts @@ -0,0 +1,81 @@ +//Config +import * as config from "../../config/host-spec"; + +//Node Module +import https from "https"; +import cluster from "cluster"; +import os from "os"; + +//Module +import ReadJsonFile from "../../modules/parse-unit/json-reader"; +import * as sqlUnit from "../../modules/sql-unit/sql-link"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; + +//Manager +import * as crateOddsLedger from "./source/manager/crate-odds-ledger"; + +//Loader +import MainExpressAppLoader from "./source/loader/tls-express-boot"; +import { StampHostLabel } from "../../manager/trace-store-keeper/trace-store-spec"; +import { BindClusterRefork, BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; + +//Preset + +cluster.schedulingPolicy = cluster.SCHED_RR; + +StartServer(); +StampHostLabel(__filename); + + +async function StartServer() { + if (cluster.isMaster) { + await StartMasterWorker(); + } else { + await StartWorker(); + } +} + +async function StartMasterWorker() { + function GenerateWorker() { + let worker = cluster.fork(); + stampLogPrinter.Log(__filename, config.workerForkMSG + worker.id); + } + + if (process.env.NODE_ENV == "production") { + os.cpus().forEach((cpu) => { + GenerateWorker(); + }); + } else { + GenerateWorker(); + } + + ServerIntro(); + BindClusterRefork(); + BindFatalProcessGuards(); + + process.on('exit', function () { + stampLogPrinter.Log(__filename, config.exitServerMessage); + }); + +} + +async function StartWorker() { + + sqlUnit.BootSqlPool(); + await crateOddsLedger.HydrateCrateOdds(); + + const expressApp = new config.App().application; + MainExpressAppLoader(expressApp); + //Create Server + https.createServer(config.httpsServerOption, expressApp).listen(config.coreHostPort, () => { + stampLogPrinter.Log(__filename, config.ServerStartHttpsMSG(config.coreHostPort)); + }); + + BindFatalProcessGuards(); +} + +function ServerIntro(): void { + let packageData = ReadJsonFile(config.packagePath); + console.log(config.serverLogo); + stampLogPrinter.Log(__filename, `${packageData["name"]} version: ${packageData["version"]}`); +} \ No newline at end of file diff --git a/app/core-app/source/handler/alias-shift-arranger.ts b/app/core-app/source/handler/alias-shift-arranger.ts new file mode 100644 index 0000000..dae4f59 --- /dev/null +++ b/app/core-app/source/handler/alias-shift-arranger.ts @@ -0,0 +1,53 @@ +import memberAliasStore, { searchKey } from "../../../../manager/sql-keeper/member-data-keeper/member-alias-store"; +import { NameChangeResultType } from "../../../../data/dto/hub-packet"; +import { MemberAliasRecord } from "../../../../data/entity/nexforge_db_record"; +import { memberAliasPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; + +export const maxNameLength = 10; +export const minNameLength = 2; +export const memberAliasColumn = 'user_profile_name'; + +export function ShiftAlias(memberKey: string, memberAliasData: MemberAliasRecord): Promise { + return new Promise(async (resolve, reject) => { + let nameChangeResultType: NameChangeResultType = await CheckName(memberAliasData.user_profile_name as string); + if (nameChangeResultType == NameChangeResultType.SuccessChanged) { + await memberAliasPocket.SetData(memberKey, memberAliasData) + await memberAliasStore.UpdateData(memberKey, memberAliasData); + } + resolve(nameChangeResultType); + }); +} + +async function CheckName(name: string): Promise { + return new Promise(async (resolve, reject) => { + if (await memberAliasStore.ExistSearchData(searchKey, name)) return resolve(NameChangeResultType.ExistName); //읎늄 졎재하멎 + if (CheckLength(name)) return resolve(NameChangeResultType.NameLengthIsOver); + if (CheckSpace(name)) return resolve(NameChangeResultType.NameHasSpace); + if (CheckSpecialChar(name)) return resolve(NameChangeResultType.NameHasSpecialText); + return resolve(NameChangeResultType.SuccessChanged); + }); +} + +function CheckLength(name: string): boolean { + if (minNameLength <= name.length && name.length <= maxNameLength) { + return false; + } else return true; +} + +function CheckSpace(name: string): boolean { + if (name.search(/\s/) != -1) { + return true; + } else { + return false; + } +} +// 특수 묞자가 있나 없나 첎크 +function CheckSpecialChar(name: string): boolean { + var special_pattern = /[`~!@#$%^&*|\\\'\";:\/?.]/gi; + + if (special_pattern.test(name) == true) { + return true; + } else { + return false; + } +} diff --git a/app/core-app/source/handler/grant-gadget-arranger.ts b/app/core-app/source/handler/grant-gadget-arranger.ts new file mode 100644 index 0000000..35dbb42 --- /dev/null +++ b/app/core-app/source/handler/grant-gadget-arranger.ts @@ -0,0 +1,40 @@ +import memberGadgetStore from "../../../../manager/sql-keeper/member-data-keeper/member-gadget-store"; +import { MintId } from "../../../../modules/id-mint-unit/id-mint"; +import { MemberGadgetShape } from "../../../../data/model/member-shape/member-data-shape"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { memberSidePocket, memberGadgetPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { SendParcel, SendParcelByAlias } from "../../../../manager/pocket-keeper/parcel-keeper/parcel-keeper"; +import { StockKind } from "../../../../data/entity/nexforge_db_record"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export async function GrantGadget(memberKey: string, partsCode: string): Promise { + return BoundMutationLane(async () => { + let partsUUID = MintId(); + + let memberGadgetData: MemberGadgetShape = { + _partsCode: Number(partsCode), + _partsLevel: 0, + _partsUUID: partsUUID, + } + + let memberCoreData = await memberSidePocket.GetData(memberKey); + if (memberCoreData.user_parts_max_count as number <= await memberGadgetPocket.GetLength(memberKey)) { + await SendParcel(memberKey, 1, 8, Number(partsCode), StockKind.parts, 1); + memberGadgetData._partsUUID = "gift"; + return memberGadgetData; + } + + await memberGadgetStore.InsertData({ + parts_code: Number(partsCode), + parts_level: 0, + parts_uuid: partsUUID, + user_uuid: memberKey + }); + await memberPocketKeeper.memberGadgetPocket.SetData(memberKey, partsUUID.toString(), { + parts_code: Number(partsCode), + parts_level: 0, + parts_uuid: partsUUID, + }) + return memberGadgetData; + }); +} \ No newline at end of file diff --git a/app/core-app/source/handler/grant-stock-arranger.ts b/app/core-app/source/handler/grant-stock-arranger.ts new file mode 100644 index 0000000..0bcf855 --- /dev/null +++ b/app/core-app/source/handler/grant-stock-arranger.ts @@ -0,0 +1,65 @@ +import { StockKind, BazaarOfferShape } from "../../../../data/model/play-shape/play-data-shape"; +import memberRelicStore from "../../../../manager/sql-keeper/member-data-keeper/member-relic-store"; +import * as grantGadgetArranger from "./grant-gadget-arranger"; +import memberVesselStore from "../../../../manager/sql-keeper/member-data-keeper/member-vessel-store"; +import { MemberRelicShape, MemberGadgetShape, MemberVesselShape } from "../../../../data/model/member-shape/member-data-shape"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export function GrantStock(memberKey: string, entityCode: number, entityType: StockKind, giveAmount: number): Promise { + return BoundMutationLane(async () => { + let entityCodeText = entityCode.toString(); + if (entityType == StockKind.item) { + let memberRelicData: MemberRelicShape = { + _itemCode: entityCode, + _itemAmount: giveAmount + } + + await memberRelicStore.IncreaseData(memberKey, entityCodeText, giveAmount); + + if (await memberPocketKeeper.memberRelicPocket.ExistData(memberKey, entityCodeText)) { + let memberRelicPocketData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, entityCodeText); + (memberRelicPocketData.item_amount as number) += giveAmount; + await memberPocketKeeper.memberRelicPocket.SetData(memberKey, entityCodeText, memberRelicPocketData); + } else { + await memberPocketKeeper.memberRelicPocket.SetData(memberKey, entityCodeText, { + item_amount: giveAmount, + item_code: entityCode, + }) + } + return memberRelicData; + } else if (entityType == StockKind.parts) { + return await grantGadgetArranger.GrantGadget(memberKey, entityCodeText); + } else if (entityType == StockKind.unit) { + let memberVesselData: MemberVesselShape + + if (!(await memberPocketKeeper.memberVesselPocket.ExistData(memberKey, entityCodeText))) { + await memberPocketKeeper.memberVesselGadgetPocket.InitData(memberKey, entityCodeText); + let untiGameData = await gameDataCachingManager.unitDataHashPocketStore.GetData(entityCodeText); + + memberVesselData = { + _unitCode: entityCode, + _unitLevel: 1, + _unitUsingPartsCodeList: (await memberPocketKeeper.memberVesselGadgetPocket.GetData(memberKey, entityCodeText)).vesselMountedGadgetList, + _unitClass: untiGameData._unitBandKind + } + + await memberPocketKeeper.memberVesselPocket.SetData(memberKey, entityCodeText, { + unit_code: entityCode, + unit_level: 1, + unit_class: memberVesselData._unitClass, + user_uuid: memberKey + }); + + await memberVesselStore.InsertData({ + unit_code: memberVesselData._unitCode, + unit_level: memberVesselData._unitLevel, + user_uuid: memberKey, + unit_class: memberVesselData._unitClass + }); + return memberVesselData; + } + } + }); +} \ No newline at end of file diff --git a/app/core-app/source/handler/member-admit-arranger.ts b/app/core-app/source/handler/member-admit-arranger.ts new file mode 100644 index 0000000..5b39b3d --- /dev/null +++ b/app/core-app/source/handler/member-admit-arranger.ts @@ -0,0 +1,243 @@ +import memberStore from "../../../../manager/sql-keeper/member-data-keeper/member-store"; +import memberCoreStore from "../../../../manager/sql-keeper/member-data-keeper/member-core-store"; +import memberAliasStore from "../../../../manager/sql-keeper/member-data-keeper/member-alias-store" +import memberVesselStore from "../../../../manager/sql-keeper/member-data-keeper/member-vessel-store"; +import memberRelicStore from "../../../../manager/sql-keeper/member-data-keeper/member-relic-store"; +import memberTraceStore from "../../../../manager/sql-keeper/member-data-keeper/member-trace-store"; +import memberFeatStore from "../../../../manager/sql-keeper/member-data-keeper/member-feat-store"; +import memberBazaarTraceStore from "../../../../manager/sql-keeper/member-data-keeper/member-bazaar-trace-store"; +import logCodeGameDAO from "../../../../manager/sql-keeper/master-data-keeper/trace-code-master-store"; +import memberTicketStore, { subPrimaryKey } from "../../../../manager/sql-keeper/member-data-keeper/member-ticket-store"; +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import * as jwtModule from "../../../../modules/admit-jwt-unit/admit-jwt-unit" +import * as uuidModule from "../../../../modules/id-mint-unit/id-mint"; +import { FeatRecord, BandKind, BazaarOfferRecord, MemberFeatRecord, MemberCampaignRecord, MemberRecord, MemberCoreRecord, MemberRelicRecord, MemberTraceRecord, MemberAliasRecord, MemberBracketRecord, MemberBazaarTraceRecord, MemberSideRecord, MemberTicketRecord, MemberVesselRecord } from "../../../../data/entity/nexforge_db_record"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import randToken from "rand-token"; +import Print from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as accessTokenIssuer from "../../../../modules/admit-ticket-unit/admit-ticket-unit"; +import * as env from "../../../../config/runtime-spec"; +import { achievementDataHashPocketStore, shopProudctDataHashPocketStore, unitDataHashPocketStore } from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import { FeatMasterShape, BazaarOfferShape } from "../../../../data/model/play-shape/play-data-shape"; + +import { resolve } from "path"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; +import memberSideStore from "../../../../manager/sql-keeper/member-data-keeper/member-side-store"; +import memberCampaignStore from "../../../../manager/sql-keeper/member-data-keeper/member-campaign-store"; +import memberBracketStore from "../../../../manager/sql-keeper/member-data-keeper/member-bracket-store"; + +const enrollCreatedMSG = "Sign up new user uniqueID: " +const enrollMSG = "Sign in user uniqueID: " + + +class MemberAdmitArranger { + public Sign(uniqueID: string, country: string | undefined = undefined, enrollKind: string | undefined, deviceUUID: string): Promise { + return BoundMutationLane(async () => { + if (await memberStore.SerachData("user_unique_id", uniqueID)) { + Print.Log(__filename, enrollMSG + uniqueID); + return await this.AdmitMember(uniqueID, deviceUUID); + } else { + Print.Log(__filename, enrollCreatedMSG + uniqueID); + return await this.EnrollMember(uniqueID, country, enrollKind, deviceUUID); + } + }); + } + + private AdmitMember(uniqueID: string, devcieUUID: string): Promise { //?ᅵ로???ᅵ섞???ᅵ큰 + return new Promise(async (resolve, reject) => { + let memberRecord: MemberRecord = await memberStore.SerachData("user_unique_id", uniqueID); + let tokenGroup = await jwtModule.IssueMemberKey((memberRecord.user_uuid as string).toString()); + let enrollAt = Math.trunc(Date.now() / 1000); + let refreshToken: string = randToken.generate(16); + + //Refresh Token Setting + await ParkRefreshTicket(memberRecord.user_uuid as string, refreshToken, enrollAt, devcieUUID); + + let EnrollPacket: hdgbDTO.RESGBEnrollPacket = { + accessToken: tokenGroup.accessToken, + refreshToken: refreshToken, + }; + + return resolve(EnrollPacket); + }); + } + + private EnrollMember(uniqueID: string, country: string | undefined = undefined, enrollKind: string | undefined, deviceUUID: string): Promise { + return new Promise(async (resolve, reject) => { + let newMemberKey: string = await MintFreshMemberKey(); + let enrollStamp = Math.trunc(Date.now() / 1000); + await SeedNewMemberRecords(newMemberKey, uniqueID, country, enrollKind, enrollStamp); + return resolve(await this.MintMemberTickets(newMemberKey, deviceUUID, enrollStamp)); + }); + } + + public MintMemberTickets(memberKey: string, deviceUUID: string, enrollStamp: number): Promise { + return BoundMutationLane(async () => { + let refreshToken: string = randToken.generate(16); + let accessToken: string = await accessTokenIssuer.IssueAccessToken(memberKey); + + await ParkRefreshTicket(memberKey, refreshToken, enrollStamp, deviceUUID); + + return { + accessToken: accessToken, + refreshToken: refreshToken, + }; + }); + } +} + +function MintFreshMemberKey(): Promise { + return new Promise(async (resolve, reject) => { + while (true) { + let newUUID = uuidModule.MintId(); + if (await memberStore.ExistData(newUUID)) { + continue; + } else { + resolve(newUUID); + break; + } + } + }) +} + +async function SeedNewMemberRecords(newMemberKey: string, uniqueID: string, country: string | undefined = undefined, enrollKind: string | undefined, enrollStamp: number) { + await BoundMutationLane(async () => { + //MemberRecord Setting + let memberRecord: MemberRecord = { + user_unique_id: uniqueID, + user_uuid: newMemberKey, + user_signup_time: enrollStamp, + user_signin_type: enrollKind, + user_redirect_id: undefined + }; + await memberStore.InsertData(memberRecord); + + //MemberAlias Setting + let memberAliasRecord: MemberAliasRecord = { + user_country: country, + user_profile_name: gameConfig.memberAnonInitial + newMemberKey, + user_uuid: newMemberKey, + } + + await memberAliasStore.InsertData(memberAliasRecord); + + //User Gold Setting + var memberRelicRecord: MemberRelicRecord = { + user_uuid: newMemberKey, + item_amount: gameConfig.memberDefaultCreditValue, + item_code: gameConfig.goldItemCode + }; + await memberRelicStore.InsertData(memberRelicRecord); + + //User Gem Setting + var memberRelicRecord: MemberRelicRecord = { + user_uuid: newMemberKey, + item_amount: gameConfig.memberDefaultPocketValue, + item_code: gameConfig.pocketRelicCode + }; + await memberRelicStore.InsertData(memberRelicRecord); + + //User Unit Setting + let memberVesselRecord: MemberVesselRecord = { + unit_code: gameConfig.memberDefaultVesselCode, + unit_level: 1, + user_uuid: newMemberKey, + unit_class: (await unitDataHashPocketStore.GetData(gameConfig.memberDefaultVesselCode.toString()))._unitBandKind as BandKind + } + await memberVesselStore.InsertData(memberVesselRecord); + + //MemberCoreShape Settting + let memberCoreRecord: MemberCoreRecord = { + user_uuid: newMemberKey, + user_level: gameConfig.memberDefaultGrade, + user_exp: gameConfig.memberDefaultGradeValue, + user_character_code: 0 + } + await memberCoreStore.InsertData(memberCoreRecord); + + let memberSideRecord: MemberSideRecord = { + user_parts_max_count: gameConfig.memberGadgetBayMax, + user_tutorial_step: gameConfig.memberDefaultPrimerStep, + user_uuid: newMemberKey + } + + await memberSideStore.InsertData(memberSideRecord); + + let memberCampaignRecord: MemberCampaignRecord = { + user_adventure_code: gameConfig.memberDefaultCampaignStep, + user_adventure_level: gameConfig.memberDefaultCampaignSubStep, + user_uuid: newMemberKey + } + + await memberCampaignStore.InsertData(memberCampaignRecord); + + let memberBracketRecord: MemberBracketRecord = { + user_tier_code: gameConfig.memberDefaultBandCode, + user_trophy: gameConfig.memberDefaultStanding, + user_uuid: newMemberKey, + user_winning_streak_count: 0 + } + await memberBracketStore.InsertData(memberBracketRecord); + + let achievementGameDataDictionary = await achievementDataHashPocketStore.GetAllData(); + let achievementGameDataKey = Object.keys(achievementGameDataDictionary); + let achievementEntityList: MemberFeatRecord[] = []; + for (let i = 0; i < achievementGameDataKey.length; i++) { + let element = JSON.parse(achievementGameDataDictionary[achievementGameDataKey[i]]) as FeatMasterShape; + achievementEntityList.push({ + achieved: false, + achievement_code: element._achievementCode, + log_code: element._logCode, + user_uuid: newMemberKey, + }); + } + await memberFeatStore.InsertListData(achievementEntityList); + + let log_code_list = await logCodeGameDAO.GetAllData(); + let logEntityList: MemberTraceRecord[] = []; + for (let i = 0; i < log_code_list.length; i++) { + let element = log_code_list[i]; + logEntityList.push({ + log_code: element.log_code, + log_value: 0, + user_uuid: newMemberKey + }); + } + + await memberTraceStore.InsertListData(logEntityList); + + let _shopProductDataDictionary = await shopProudctDataHashPocketStore.GetAllData(); + let _shopProductDataDictionaryKeyList = Object.keys(_shopProductDataDictionary); + let _shopProductLogEntityList: MemberBazaarTraceRecord[] = []; + for (let index = 0; index < _shopProductDataDictionaryKeyList.length; index++) { + const element = JSON.parse(_shopProductDataDictionary[_shopProductDataDictionaryKeyList[index]]) as BazaarOfferShape; + if (element._buyRestrictionCount != 0 && element._shopLineCode != 0) { + _shopProductLogEntityList.push({ + buy_count: 0, + shop_product_code: element._productCode, + user_uuid: newMemberKey + }); + } + } + await memberBazaarTraceStore.InsertListData(_shopProductLogEntityList); + }); +} + +async function ParkRefreshTicket(memberKey: string, refreshToken: string, time: number, deviceUUID: string) { + await BoundMutationLane(async () => { + if (await memberTicketStore.ExistSelectData(subPrimaryKey, memberKey)) { + await memberTicketStore.DeleteAllSelectData(subPrimaryKey, memberKey); + } + let tokenEntity: MemberTicketRecord = { + user_uuid: memberKey, + user_refresh_token: refreshToken, + refresh_token_expiry_time: time + env.expiredRefreshTokenTime, + access_token_expiry_time: time + env.expiredAccessTokenTime, + device_uuid: deviceUUID + } + await memberTicketStore.InsertData(tokenEntity); + }); +} + +export default new MemberAdmitArranger(); \ No newline at end of file diff --git a/app/core-app/source/handler/open-crate-arranger.ts b/app/core-app/source/handler/open-crate-arranger.ts new file mode 100644 index 0000000..f8ba2ca --- /dev/null +++ b/app/core-app/source/handler/open-crate-arranger.ts @@ -0,0 +1,72 @@ +import { CrateGroupOddsRecord, CrateOddsRecord } from "../../../../data/entity/nexforge_db_record"; +import { rollInt } from "../../../../modules/kit-unit/chance-unit"; +import { BandKind, StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import * as crateOddsLedger from "../manager/crate-odds-ledger"; +import * as giveEntityHandler from "./grant-stock-arranger"; +import { REQRelicPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; + +export default async (usreUUID: string, chestCode: number): Promise => { + return new Promise(async (resolve, reject) => { + let resultData = GetRandomEntity(GetRandomCategory(chestCode) as CrateGroupOddsRecord) as CrateOddsRecord; + // drop crate then insert payload + let entityData = await giveEntityHandler.GrantStock(usreUUID, resultData.code as number, resultData.code_type as StockKind, resultData.getting_amount as number); + + if (resultData.code_type == StockKind.item) { + resolve({ + _itemData: entityData, + _entityType: resultData.code_type + }) + } else if (resultData.code_type == StockKind.parts) { + resolve({ + _partsData: entityData, + _entityType: resultData.code_type, + }) + } else if (resultData.code_type == StockKind.unit) { + resolve({ + _unitData: entityData, + _entityType: resultData.code_type + }) + } + }) +} + +function GetRandomCategory(chestCode: number): CrateGroupOddsRecord | undefined { + let beforeScore: number = 0; + let randomValue = rollInt(0, crateOddsLedger.CrateGroupOddsScoreHash[chestCode] - 1); + + var categoryKeyList = Object.keys(crateOddsLedger.CrateGroupOddsHash[chestCode]); + for (let index = 0; index < categoryKeyList.length; index++) { + var categoryKey = categoryKeyList[index]; + var classKeyKeyList = Object.keys(crateOddsLedger.CrateGroupOddsHash[chestCode][categoryKey]); + for (let index = 0; index < classKeyKeyList.length; index++) { + var classKey = classKeyKeyList[index]; + let chestCategoryProbabilityData = crateOddsLedger.CrateGroupOddsHash[chestCode][categoryKey][classKey]; + if (beforeScore <= randomValue && randomValue < beforeScore + (chestCategoryProbabilityData.select_probability_value as number)) { + return chestCategoryProbabilityData; + } else { + beforeScore += chestCategoryProbabilityData.select_probability_value as number; + } + } + } + return undefined; +} + +function GetRandomEntity(chestCategoryProbabilityData: CrateGroupOddsRecord): CrateOddsRecord | undefined { + let randomValue = rollInt(0, crateOddsLedger.CrateOddsTotalHash[chestCategoryProbabilityData.item_code as number][chestCategoryProbabilityData.category_type as StockKind][chestCategoryProbabilityData.class_code as BandKind] - 1); + var entityKeyList = Object.keys(crateOddsLedger.CrateOddsHash[chestCategoryProbabilityData.item_code as number][chestCategoryProbabilityData.category_type as StockKind][chestCategoryProbabilityData.class_code as BandKind]); + let beforeScore: number = 0; + for (let index = 0; index < entityKeyList.length; index++) { + const entityKey: number = Number(entityKeyList[index]); + var standingGainKeyList = Object.keys(crateOddsLedger.CrateOddsHash[chestCategoryProbabilityData.item_code as number][chestCategoryProbabilityData.category_type as StockKind][chestCategoryProbabilityData.class_code as BandKind][entityKey]); + for (let index = 0; index < standingGainKeyList.length; index++) { + const standingGainKey: number = Number(standingGainKeyList[index]); + let crateOddsData = crateOddsLedger.CrateOddsHash[chestCategoryProbabilityData.item_code as number][chestCategoryProbabilityData.category_type as StockKind][chestCategoryProbabilityData.class_code as BandKind][entityKey][standingGainKey]; + if (beforeScore <= randomValue && randomValue < beforeScore + (crateOddsData.getting_score as number)) { + return crateOddsData; + } else { + beforeScore += crateOddsData.getting_score as number; + } + } + } + return undefined; +} \ No newline at end of file diff --git a/app/core-app/source/loader/tls-express-boot-spec.ts b/app/core-app/source/loader/tls-express-boot-spec.ts new file mode 100644 index 0000000..13988c8 --- /dev/null +++ b/app/core-app/source/loader/tls-express-boot-spec.ts @@ -0,0 +1,223 @@ +export const startListenMessage: string = "Start Listen Server"; +export const successLoadMessage: string = "Success loaded"; +export const failLoadMessage: string = "Fail loaded"; + +import { RouteData } from "../../../../config/gateway-spec"; +//Router +import readMasterdataGateway from "../router/read-masterdata-gateway"; +import bazaarCheckoutGateway from "../router/bazaar-checkout-gateway"; +import newMemberSetupGateway from "../router/new-member-setup-gateway"; +import openInstantCrateGateway from "../router/open-instant-crate-gateway"; +import parkCrateGateway from "../router/park-crate-gateway"; +import openCrateGateway from "../router/open-crate-gateway" +import vesselRefineGateway from "../router/vessel-refine-gateway"; +import gadgetRefineGateway from "../router/gadget-refine-gateway"; +import vesselGadgetLoadoutGateway from "../router/vessel-gadget-loadout-gateway"; +import vesselLineGateway from "../router/vessel-line-gateway"; +import craftStockGateway from "../router/craft-stock-gateway"; +import shatterGadgetGateway from "../router/shatter-gadget-gateway"; +import campaignGateway from "../router/campaign-gateway"; +import enrollGateway from "../router/enroll-gateway"; +import clientPackGateway from "../router/client-pack-gateway"; +import readHostHealthGateway from "../router/read-host-health-gateway"; +import enrollCallbackGateway from "../router/enroll-callback-gateway"; +import admitGateway from "../router/admit-gateway"; +import accountLinkGateway from "../router/account-link-gateway"; +import accountRetireGateway from "../router/account-retire-gateway"; +import pushOptGateway from "../router/push-opt-gateway"; +import readDuelStageGateway from "../router/read-duel-stage-gateway"; +import readCycleGateway from "../router/read-cycle-gateway"; +import aliasShiftGateway from "../router/alias-shift-gateway"; +import expandGadgetBayGateway from "../router/expand-gadget-bay-gateway"; +import swapAzureTokenGateway from "../router/swap-azure-token-gateway"; +import occupancyCheckGateway from "../router/occupancy-check-gateway"; +import readDeltaGateway from "../router/read-delta-gateway"; +import openParcelGateway from "../router/open-parcel-gateway" +import primerGateway from "../router/primer-gateway"; +import campaignClearGateway from "../router/campaign-clear-gateway"; +import adBountyGateway from "../router/ad-bounty-gateway"; +import claimSpinnerBountyGateway from "../router/claim-spinner-bounty-gateway"; +import storefrontCheckoutGateway from "../router/storefront-checkout-gateway"; + +let routeList: Array = new Array(); + +routeList.push({ + mainRoute: "/app/gamebase", + subRoute: enrollGateway, + subRouteName: "enrollGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/gamebase", + subRoute: clientPackGateway, + subRouteName: "clientPackGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/gamebase", + subRoute: readHostHealthGateway, + subRouteName: "readHostHealthGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/gamebase", + subRoute: admitGateway, + subRouteName: "admitGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: accountLinkGateway, + subRouteName: "accountLinkGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: accountRetireGateway, + subRouteName: "accountRetireGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: pushOptGateway, + subRouteName: "pushOptGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: readMasterdataGateway, + subRouteName: "readMasterdataGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: bazaarCheckoutGateway, + subRouteName: "bazaarCheckoutGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: newMemberSetupGateway, + subRouteName: "newMemberSetupGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: openInstantCrateGateway, + subRouteName: "openInstantCrateGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: parkCrateGateway, + subRouteName: "parkCrateGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: openCrateGateway, + subRouteName: "openCrateGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: vesselRefineGateway, + subRouteName: "vesselRefineGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: gadgetRefineGateway, + subRouteName: "gadgetRefineGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: vesselLineGateway, + subRouteName: "vesselLineGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: vesselGadgetLoadoutGateway, + subRouteName: "vesselGadgetLoadoutGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: shatterGadgetGateway, + subRouteName: "ShatterGadgetGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: craftStockGateway, + subRouteName: "CraftStockGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: campaignGateway, + subRouteName: "CampaignGateway" +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: readDuelStageGateway, + subRouteName: "readDuelStageGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: readCycleGateway, + subRouteName: "readCycleGateway", +} as RouteData); +routeList.push({ + mainRoute: "/app/lobby", + subRoute: aliasShiftGateway, + subRouteName: "aliasShiftGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: swapAzureTokenGateway, + subRouteName: "swapAzureTokenGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: expandGadgetBayGateway, + subRouteName: "expandGadgetBayGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: readDeltaGateway, + subRouteName: "readDeltaGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: openParcelGateway, + subRouteName: "openParcelGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: occupancyCheckGateway, + subRouteName: "occupancyCheckGateway" +} as RouteData) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: primerGateway, + subRouteName: "primerGateway" +}) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: campaignClearGateway, + subRouteName: "campaignClearGateway" +}) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: storefrontCheckoutGateway, + subRouteName: "storefrontCheckoutGateway" +}) +routeList.push({ + mainRoute: "/app/lobby", + subRoute: claimSpinnerBountyGateway, + subRouteName: "claimSpinnerBountyGateway" +}) +routeList.push({ + mainRoute: "/auth", + subRoute: enrollCallbackGateway, + subRouteName: "enrollCallbackGateway", +} as RouteData); +routeList.push({ + mainRoute: "/auth", + subRoute: adBountyGateway, + subRouteName: "adBountyGateway", +} as RouteData); + + +function GetRouteLoadMSG(routeData: RouteData): string { + return "Set Route " + + routeData.mainRoute + + " | Setted Router: " + + routeData.subRouteName; +} + +export { routeList, RouteData, GetRouteLoadMSG }; diff --git a/app/core-app/source/loader/tls-express-boot.ts b/app/core-app/source/loader/tls-express-boot.ts new file mode 100644 index 0000000..f2104bf --- /dev/null +++ b/app/core-app/source/loader/tls-express-boot.ts @@ -0,0 +1,58 @@ +import express, { Application, NextFunction, Request, Response } from "express"; +import * as config from "./tls-express-boot-spec"; +import Print from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import licenseMiddleWare from "../../../../middleware/client-license-gate"; +import occupancyGate from "../../../../middleware/hub-occupancy-gate"; +import bodyParser from "body-parser"; +import { ErrorType } from "../../../../modules/trace-unit/trace-unit-spec/fault-dispatch-spec"; +import serverLicenseMiddleWare from "../../../../middleware/host-license-gate"; +import ipOccupancyGate from "../../../../middleware/ip-occupancy-gate"; +import serverStatusMiddleware from "../../../../middleware/host-health-gate"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + + + +export default (app: Application) => { + try { + //Middle Ware + app.use(bodyParser.json()); + + // app.use("/auth", ipOccupancyGate); Facebook + + app.use("/app", serverStatusMiddleware); + app.use("/app", licenseMiddleWare); + app.use("/app/gamebase", ipOccupancyGate); + + app.use("/app/lobby", occupancyGate); + + app.use("/server", serverLicenseMiddleWare); + + //Route + LoadGameBaseRouter(app); + + //Handler + app.use(GlobalErrorHandler); + + Print.Log(__filename, config.successLoadMessage); + } catch (err: NodeJS.ErrnoException | any) { + Print.Log(__filename, config.failLoadMessage); + } +}; + +function LoadGameBaseRouter(app: Application) { + config.routeList.forEach((routeData: config.RouteData) => { + app.use(routeData.mainRoute, routeData.subRoute); + Print.Log(__filename, config.GetRouteLoadMSG(routeData)); + }); +} + +function GlobalErrorHandler(err: any, req: Request, res: Response, __: NextFunction) { + if (!err.status) { + console.error(err.stack); + } + Print.Error(__filename, ErrorType.InternalServiceError, err.message); + if (!res.headersSent) { + faultDispatch(res, ErrorType.InternalServiceError, err.status || 500); + } +} \ No newline at end of file diff --git a/app/core-app/source/manager/crate-odds-ledger.ts b/app/core-app/source/manager/crate-odds-ledger.ts new file mode 100644 index 0000000..c2488af --- /dev/null +++ b/app/core-app/source/manager/crate-odds-ledger.ts @@ -0,0 +1,15 @@ +import { CrateGroupOddsRecord, CrateOddsRecord } from "../../../../data/entity/nexforge_db_record"; +import { BandKind, StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; + +export let CrateOddsHash: { [itemIndxe: number]: { [categoryTypeIndex: StockKind | string]: { [classTypeIndex: BandKind | string]: { [entityCode: number]: { [entityCount: number]: CrateOddsRecord } } } } } = {}; +export let CrateGroupOddsHash: { [itemIndxe: number]: { [categoryTypeIndex: StockKind | string]: { [classTypeIndex: BandKind | string]: CrateGroupOddsRecord } } } = {}; +export let CrateOddsTotalHash: { [itemIndxe: number]: { [categoryTypeIndex: StockKind | string]: { [classTypeIndex: BandKind | string]: number } } } = {}; +export let CrateGroupOddsScoreHash: { [index: number]: number } = {}; + +export async function HydrateCrateOdds() { + CrateOddsHash = await gameDataCachingManager.crateOddsPocket.GetData(); + CrateGroupOddsHash = await gameDataCachingManager.crateGroupOddsPocket.GetData(); + CrateOddsTotalHash = await gameDataCachingManager.crateOddsTotalPocket.GetData(); + CrateGroupOddsScoreHash = await gameDataCachingManager.crateGroupOddsTotalPocket.GetData(); +} \ No newline at end of file diff --git a/app/core-app/source/router/account-link-gateway.ts b/app/core-app/source/router/account-link-gateway.ts new file mode 100644 index 0000000..5af2e02 --- /dev/null +++ b/app/core-app/source/router/account-link-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import accountLinkUsecase from "../service/account-link-usecase"; + +const route = Router(); +route.post("/account-mapping", async (req, res) => { + try { + await accountLinkUsecase(req.occupancy.memberKey, req.body); + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/account-retire-gateway.ts b/app/core-app/source/router/account-retire-gateway.ts new file mode 100644 index 0000000..00f6e08 --- /dev/null +++ b/app/core-app/source/router/account-retire-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import accountRetireUsecase from "../service/account-retire-usecase"; + +const route = Router(); +route.delete("/account-withdrawal", async (req, res) => { + try { + await accountRetireUsecase(req.occupancy.memberKey); + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/ad-bounty-gateway.ts b/app/core-app/source/router/ad-bounty-gateway.ts new file mode 100644 index 0000000..87a17bc --- /dev/null +++ b/app/core-app/source/router/ad-bounty-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import adBountyUsecase from "../service/ad-bounty-usecase"; + +const route = Router(); + +route.get("/admob/reward", async (req, res) => { + try { + await adBountyUsecase(req.query['user_id'] as string, req.query['reward_item'] as string); + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + res.sendStatus(500); + } +}); +export default route; diff --git a/app/core-app/source/router/admit-gateway.ts b/app/core-app/source/router/admit-gateway.ts new file mode 100644 index 0000000..4ec4d32 --- /dev/null +++ b/app/core-app/source/router/admit-gateway.ts @@ -0,0 +1,29 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; + +import AdmitUsecase from "../service/admit-usecase"; +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import { ErrorType } from "../../../../modules/trace-unit/trace-unit-spec/fault-dispatch-spec"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import { ResolveClientIp } from "../../../../modules/kit-unit/request-ip"; + +const route = Router(); +route.post("/login", async (req, res) => { + try { + const ip: string = ResolveClientIp(req); + const result: any | ErrorType = await AdmitUsecase(ip, req.body); + if (result in ErrorType) { + faultDispatch(res, result as ErrorType, 403); + } else { + res.json(result as hdgbDTO.RESGBAdmitPacket); + } + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/alias-shift-gateway.ts b/app/core-app/source/router/alias-shift-gateway.ts new file mode 100644 index 0000000..18ca0f9 --- /dev/null +++ b/app/core-app/source/router/alias-shift-gateway.ts @@ -0,0 +1,19 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import aliasShiftUsecase from "../service/alias-shift-usecase"; + +const route = Router(); +route.post("/change-name", async (req, res) => { + try { + res.json(await aliasShiftUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/bazaar-checkout-gateway.ts b/app/core-app/source/router/bazaar-checkout-gateway.ts new file mode 100644 index 0000000..0c23d69 --- /dev/null +++ b/app/core-app/source/router/bazaar-checkout-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import bazaarCheckoutUsecase from "../service/bazaar-checkout-usecase"; + +const route = Router(); +route.post("/buy-product", async (req, res) => { + try { + res.json(await bazaarCheckoutUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/campaign-clear-gateway.ts b/app/core-app/source/router/campaign-clear-gateway.ts new file mode 100644 index 0000000..a6633c3 --- /dev/null +++ b/app/core-app/source/router/campaign-clear-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import campaignClearUsecase from "../service/campaign-clear-usecase"; + +const route = Router(); +route.post("/adventure-clear", async (req, res) => { + try { + res.json(await campaignClearUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/campaign-gateway.ts b/app/core-app/source/router/campaign-gateway.ts new file mode 100644 index 0000000..921be89 --- /dev/null +++ b/app/core-app/source/router/campaign-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import campaignUsecase from "../service/campaign-usecase"; + +const route = Router(); +route.post("/adventure", async (req, res) => { + try { + res.json(await campaignUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/claim-spinner-bounty-gateway.ts b/app/core-app/source/router/claim-spinner-bounty-gateway.ts new file mode 100644 index 0000000..0502d11 --- /dev/null +++ b/app/core-app/source/router/claim-spinner-bounty-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import claimSpinnerBountyUsecase from "../service/claim-spinner-bounty-usecase"; + +const route = Router(); +route.get("/roulette-reward", async (req, res) => { + try { + res.json(await claimSpinnerBountyUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/client-pack-gateway.ts b/app/core-app/source/router/client-pack-gateway.ts new file mode 100644 index 0000000..381b42b --- /dev/null +++ b/app/core-app/source/router/client-pack-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import clientPackUsecase from "../service/client-pack-usecase" + +const route = Router(); +route.get("/app-data", async (req, res) => { + try { + res.json(await clientPackUsecase()); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/craft-stock-gateway.ts b/app/core-app/source/router/craft-stock-gateway.ts new file mode 100644 index 0000000..519982a --- /dev/null +++ b/app/core-app/source/router/craft-stock-gateway.ts @@ -0,0 +1,19 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import craftStockUsecase from "../service/craft-stock-usecase"; + +const route = Router(); +route.post("/making-product", async (req, res) => { + try { + res.json(await craftStockUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/enroll-callback-gateway.ts b/app/core-app/source/router/enroll-callback-gateway.ts new file mode 100644 index 0000000..781ea23 --- /dev/null +++ b/app/core-app/source/router/enroll-callback-gateway.ts @@ -0,0 +1,19 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; + + +const route = Router(); + +route.post("/facebook/remove/callback", async (req, res) => { + try { + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + res.sendStatus(500) + } +}); +export default route; diff --git a/app/core-app/source/router/enroll-gateway.ts b/app/core-app/source/router/enroll-gateway.ts new file mode 100644 index 0000000..f32d6ba --- /dev/null +++ b/app/core-app/source/router/enroll-gateway.ts @@ -0,0 +1,23 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import enrollUsecase from "../service/enroll-usecase"; + +const route = Router(); + + +route.post("/sign", async (req, res) => { + try { + let signData = await enrollUsecase(req.body); + res.json(signData); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; diff --git a/app/core-app/source/router/expand-gadget-bay-gateway.ts b/app/core-app/source/router/expand-gadget-bay-gateway.ts new file mode 100644 index 0000000..1db92da --- /dev/null +++ b/app/core-app/source/router/expand-gadget-bay-gateway.ts @@ -0,0 +1,19 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import expandGadgetBayUsecase from "../service/expand-gadget-bay-usecase"; + +const route = Router(); +route.post("/increase-parts-slot", async (req, res) => { + try { + res.json(await expandGadgetBayUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/gadget-refine-gateway.ts b/app/core-app/source/router/gadget-refine-gateway.ts new file mode 100644 index 0000000..96d2280 --- /dev/null +++ b/app/core-app/source/router/gadget-refine-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import gadgetRefineUsecase from "../service/gadget-refine-usecase"; + +const route = Router(); +route.post("/parts-upgrade", async (req, res) => { + try { + res.json(await gadgetRefineUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/new-member-setup-gateway.ts b/app/core-app/source/router/new-member-setup-gateway.ts new file mode 100644 index 0000000..e0d3124 --- /dev/null +++ b/app/core-app/source/router/new-member-setup-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import newMemberSetupUsecase from "../service/new-member-setup-usecase"; + +const route = Router(); +route.post("/newuser", async (req, res) => { + try { + res.json(await newMemberSetupUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/occupancy-check-gateway.ts b/app/core-app/source/router/occupancy-check-gateway.ts new file mode 100644 index 0000000..3529c0a --- /dev/null +++ b/app/core-app/source/router/occupancy-check-gateway.ts @@ -0,0 +1,18 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +const route = Router(); +route.get("/session-check", async (req, res) => { + try { + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/open-crate-gateway.ts b/app/core-app/source/router/open-crate-gateway.ts new file mode 100644 index 0000000..5844695 --- /dev/null +++ b/app/core-app/source/router/open-crate-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import openCrateUsecase from "../service/open-crate-usecase"; + +const route = Router(); +route.post("/open-chest", async (req, res) => { + try { + res.json(await openCrateUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/open-instant-crate-gateway.ts b/app/core-app/source/router/open-instant-crate-gateway.ts new file mode 100644 index 0000000..293a4c6 --- /dev/null +++ b/app/core-app/source/router/open-instant-crate-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import openCrateInstantUsecase from "../service/open-crate-instant-usecase"; + +const route = Router(); +route.post("/open-instant-chest", async (req, res) => { + try { + res.json(await openCrateInstantUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/open-parcel-gateway.ts b/app/core-app/source/router/open-parcel-gateway.ts new file mode 100644 index 0000000..15da0d9 --- /dev/null +++ b/app/core-app/source/router/open-parcel-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import openParcelUsecase from "../service/open-parcel-usecase"; + +const route = Router(); +route.post("/open-gift", async (req, res) => { + try { + res.json(await openParcelUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/park-crate-gateway.ts b/app/core-app/source/router/park-crate-gateway.ts new file mode 100644 index 0000000..704b2f1 --- /dev/null +++ b/app/core-app/source/router/park-crate-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import parkCrateUsecase from "../service/park-crate-usecase"; + +const route = Router(); +route.post("/register-chest", async (req, res) => { + try { + res.json(await parkCrateUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/primer-gateway.ts b/app/core-app/source/router/primer-gateway.ts new file mode 100644 index 0000000..ab24141 --- /dev/null +++ b/app/core-app/source/router/primer-gateway.ts @@ -0,0 +1,31 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import * as primerUsecase from "../service/primer-usecase"; +import { RESPrimerStepPacket } from "../../../../data/dto/hub-packet"; + +const route = Router(); +route.get("/tutorial-next-step", async (req, res) => { + try { + res.send(new RESPrimerStepPacket(await primerUsecase.post(req.occupancy.memberKey)) + ); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); + +route.get("/tutorial-step", async (req, res) => { + try { + res.send((await primerUsecase.get(req.occupancy.memberKey)).toString()); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } + +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/push-opt-gateway.ts b/app/core-app/source/router/push-opt-gateway.ts new file mode 100644 index 0000000..afcdf97 --- /dev/null +++ b/app/core-app/source/router/push-opt-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import pushOptUsecase from "../service/push-opt-usecase"; + +const route = Router(); +route.post("/fcm-option", async (req, res) => { + try { + await pushOptUsecase(req.occupancy.memberKey, req.body); + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/read-cycle-gateway.ts b/app/core-app/source/router/read-cycle-gateway.ts new file mode 100644 index 0000000..4bd8c79 --- /dev/null +++ b/app/core-app/source/router/read-cycle-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import readCycleUsecase from "../service/read-cycle-usecase"; + +const route = Router(); +route.get("/season-code", async (req, res) => { + try { + res.json(await readCycleUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/read-delta-gateway.ts b/app/core-app/source/router/read-delta-gateway.ts new file mode 100644 index 0000000..b8b17c3 --- /dev/null +++ b/app/core-app/source/router/read-delta-gateway.ts @@ -0,0 +1,19 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import readDeltaUsecase from "../service/read-delta-usecase"; + +const route = Router(); +route.get("/update-data", async (req, res) => { + try { + res.json(await readDeltaUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/read-duel-stage-gateway.ts b/app/core-app/source/router/read-duel-stage-gateway.ts new file mode 100644 index 0000000..ef92227 --- /dev/null +++ b/app/core-app/source/router/read-duel-stage-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import readDuelStageUsecase from "../service/read-duel-stage-usecase"; + +const route = Router(); +route.get("/pvp-map-code", async (req, res) => { + try { + res.json(await readDuelStageUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/read-host-health-gateway.ts b/app/core-app/source/router/read-host-health-gateway.ts new file mode 100644 index 0000000..a9c7d81 --- /dev/null +++ b/app/core-app/source/router/read-host-health-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import readHostHealthUsecase from "../service/read-host-health-usecase" + +const route = Router(); +route.get("/server-data", async (req, res) => { + try { + res.json(await readHostHealthUsecase()); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/read-masterdata-gateway.ts b/app/core-app/source/router/read-masterdata-gateway.ts new file mode 100644 index 0000000..c66ea4a --- /dev/null +++ b/app/core-app/source/router/read-masterdata-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import readMasterdataUsecase from "../service/read-masterdata-usecase"; + +const route = Router(); +route.get("/gamedata", async (req, res) => { + try { + res.json(await readMasterdataUsecase(req.occupancy.memberKey)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/shatter-gadget-gateway.ts b/app/core-app/source/router/shatter-gadget-gateway.ts new file mode 100644 index 0000000..c7eda26 --- /dev/null +++ b/app/core-app/source/router/shatter-gadget-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import shatterGadgetUsecase from "../service/shatter-gadget-usecase"; + +const route = Router(); +route.post("/decompose-parts", async (req, res) => { + try { + res.json(await shatterGadgetUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/storefront-checkout-gateway.ts b/app/core-app/source/router/storefront-checkout-gateway.ts new file mode 100644 index 0000000..46ebda5 --- /dev/null +++ b/app/core-app/source/router/storefront-checkout-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import storefrontCheckoutUsecase from "../service/storefront-checkout-usecase"; + +const route = Router(); +route.post("/google-purchase", async (req, res) => { + try { + res.json(await storefrontCheckoutUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/swap-azure-token-gateway.ts b/app/core-app/source/router/swap-azure-token-gateway.ts new file mode 100644 index 0000000..657c5f1 --- /dev/null +++ b/app/core-app/source/router/swap-azure-token-gateway.ts @@ -0,0 +1,25 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; +import swapAzureTokenUsecase from "../service/swap-azure-token-usecase"; +import { RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import { MemberRelicShape } from "../../../../data/model/member-shape/member-data-shape"; + +const route = Router(); +route.post("/change-blue-coin", async (req, res) => { + try { + let entity = new RESGrantStockDTO(); + entity._entityType = StockKind.item; + entity._itemData = (await swapAzureTokenUsecase(req.occupancy.memberKey, req.body)) as MemberRelicShape; + res.json(entity); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/vessel-gadget-loadout-gateway.ts b/app/core-app/source/router/vessel-gadget-loadout-gateway.ts new file mode 100644 index 0000000..f2b913a --- /dev/null +++ b/app/core-app/source/router/vessel-gadget-loadout-gateway.ts @@ -0,0 +1,21 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import vesselGadgetLoadoutUsecase from "../service/vessel-gadget-loadout-usecase"; + +const route = Router(); +route.post("/unit-parts-setting", async (req, res) => { + try { + await vesselGadgetLoadoutUsecase(req.occupancy.memberKey, req.body) + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/vessel-line-gateway.ts b/app/core-app/source/router/vessel-line-gateway.ts new file mode 100644 index 0000000..9787385 --- /dev/null +++ b/app/core-app/source/router/vessel-line-gateway.ts @@ -0,0 +1,21 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import vesselLineUsecase from "../service/vessel-line-usecase"; + +const route = Router(); +route.post("/unit-order-setting", async (req, res) => { + try { + await vesselLineUsecase(req.occupancy.memberKey, req.body) + res.sendStatus(200); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/router/vessel-refine-gateway.ts b/app/core-app/source/router/vessel-refine-gateway.ts new file mode 100644 index 0000000..a13ae50 --- /dev/null +++ b/app/core-app/source/router/vessel-refine-gateway.ts @@ -0,0 +1,20 @@ +import express, { + Application, + NextFunction, + Request, + Response, + Router, +} from "express"; +import faultDispatch from "../../../../modules/trace-unit/fault-dispatch"; + +import vesselRefineUsecase from "../service/vessel-refine-usecase"; + +const route = Router(); +route.post("/unit-upgrade", async (req, res) => { + try { + res.json(await vesselRefineUsecase(req.occupancy.memberKey, req.body)); + } catch (error: NodeJS.ErrnoException | any) { + faultDispatch(res, error.message, 403); + } +}); +export default route; \ No newline at end of file diff --git a/app/core-app/source/service/account-link-usecase.ts b/app/core-app/source/service/account-link-usecase.ts new file mode 100644 index 0000000..e83cd32 --- /dev/null +++ b/app/core-app/source/service/account-link-usecase.ts @@ -0,0 +1,84 @@ +import { REQMappingAccountDTO } from "../../../../data/dto/hub-packet" +import { EnrollKind } from "../../../../data/dto/core-packet"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +import { AuthParams } from "../../../../config/enroll-unit-spec/enroll-spec"; +import * as env from '../../../../config/runtime-spec' +//import { KeyWeave } from "../module/security_module/key-weave-unit"; +import * as aesMoudle from "../../../../modules/cipher-unit/scramble-unit"; +import * as signConfig from "../../../../config/enroll-unit-spec/enroll-spec"; +import * as enrollUnit from "../handler/member-admit-arranger"; +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import * as memberPocketLedger from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import memberStore from "../../../../manager/sql-keeper/member-data-keeper/member-store"; +import * as nameChangeHandler from "../handler/alias-shift-arranger"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import * as hubPacket from "../../../../data/dto/hub-packet"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + + +const accountLinkFaultMSG = "Not Guest Account"; + +export default async (memberKey: string, reqDTO: REQMappingAccountDTO): Promise => { + return new Promise(async (resolve, reject) => { + let userData = await memberPocketLedger.memberRecordPocket.GetData(memberKey); + let memberAliasData = await memberPocketLedger.memberAliasPocket.GetData(memberKey); + if (userData.user_signin_type as EnrollKind == EnrollKind.Guest) { + let aliasShiftData: hubPacket.NameChangeResultType; + if (memberAliasData.user_profile_name == gameConfig.memberAnonInitial + memberKey) { + memberAliasData.user_profile_name = reqDTO._newUserName; + aliasShiftData = await nameChangeHandler.ShiftAlias(memberKey, memberAliasData); + if (aliasShiftData != hubPacket.NameChangeResultType.SuccessChanged) { + resolve({ + enrollPayload: new hdgbDTO.RESGBEnrollPacket(), + changNameData: { + _changedUserName: memberAliasData.user_profile_name, + _reqResult: aliasShiftData + } + }); + return; + } + } else { + reject(new Error(accountLinkFaultMSG)); + return; + } + + //Social Login 처늬 + let authModule: (authParams: AuthParams) => void = + signConfig.authModuleMap.get(reqDTO._signInType) as ( + authParams: AuthParams + ) => void; //Deligate + + let enrollPacket: hdgbDTO.RESGBEnrollPacket = await new Promise(async (resolve, reject) => { + authModule({ + authToken: reqDTO._authCode as string, + memberKey: reqDTO._userID as string, + successAction: async ( + memberKey: string + ) => { + let enrollStamp = Math.trunc(Date.now() / 1000); + userData.user_unique_id = memberKey; + userData.user_signin_type = reqDTO._signInType; + userData.user_uuid = memberKey; + resolve(await BoundMutationLane(async () => { + await memberStore.UpdateData(memberKey, userData); + return await enrollUnit.default.MintMemberTickets(memberKey, reqDTO._deviceCode as string, enrollStamp); + })); + return; + }, + }); + }); + resolve({ + enrollPayload: enrollPacket, + changNameData: { + _changedUserName: memberAliasData.user_profile_name, + _reqResult: aliasShiftData + } + }); + return; + } else { + reject(new Error(accountLinkFaultMSG)); + return; + } + }); +} diff --git a/app/core-app/source/service/account-retire-usecase.ts b/app/core-app/source/service/account-retire-usecase.ts new file mode 100644 index 0000000..ccded98 --- /dev/null +++ b/app/core-app/source/service/account-retire-usecase.ts @@ -0,0 +1,11 @@ +import { REQMappingAccountDTO } from "../../../../data/dto/hub-packet" +import memberStore from "../../../../manager/sql-keeper/member-data-keeper/member-store"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as OccupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; + + + +export default async (memberKey: string) => { + await memberStore.DeleteData(memberKey); + await OccupancyKeeper.CloseOccupancy(memberKey); +} diff --git a/app/core-app/source/service/ad-bounty-usecase.ts b/app/core-app/source/service/ad-bounty-usecase.ts new file mode 100644 index 0000000..e6e049e --- /dev/null +++ b/app/core-app/source/service/ad-bounty-usecase.ts @@ -0,0 +1,16 @@ +import * as aesModule from "../../../../modules/cipher-unit/scramble-unit"; +import * as env from "../../../../config/runtime-spec"; +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberAdBountyStore } from "../../../../manager/pocket-keeper/ad-bounty-keeper/ad-bounty-keeper"; + +export default async (occupancyTicket: string, rewardType: string) => { + let memberKey: string = aesModule.decrypt(occupancyTicket, env.occupancyScrambleKey).data as string; + + if (await occupancyKeeper.OccupancyMatchesTicket(memberKey, occupancyTicket)) { + if (rewardType != "roulette") return; + + await MemberAdBountyStore.SetData(memberKey, rewardType, { + rewardStatus: true + }); + } +} \ No newline at end of file diff --git a/app/core-app/source/service/admit-usecase.ts b/app/core-app/source/service/admit-usecase.ts new file mode 100644 index 0000000..b259283 --- /dev/null +++ b/app/core-app/source/service/admit-usecase.ts @@ -0,0 +1,123 @@ +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import { ErrorType } from "../../../../modules/trace-unit/trace-unit-spec/fault-dispatch-spec"; +import * as jwtModule from "../../../../modules/admit-jwt-unit/admit-jwt-unit"; +import { TOKEN_ERROR } from "../../../../modules/admit-jwt-unit/admit-jwt-unit"; +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import memberTicketStore from "../../../../manager/sql-keeper/member-data-keeper/member-ticket-store"; +import { MemberTicketRecord } from "../../../../data/entity/nexforge_db_record"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as accessTokenIssuer from "../../../../modules/admit-ticket-unit/admit-ticket-unit"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +import * as gameConfig from "../../../../config/play-baseline-spec"; + + + +export default function AdmitUsecase(memberIp: string, admitPayload: hdgbDTO.REQGBAdmitPacket | undefined): Promise { + return new Promise(async (resolve, reject) => { + if (admitPayload == undefined) return resolve(ErrorType.InvalidAccessToken); + + let result: any = jwtModule.Verify(admitPayload.accessToken as string); + if (result in TOKEN_ERROR) { + let errorType: TOKEN_ERROR = result as TOKEN_ERROR; + if (errorType == TOKEN_ERROR.ACCESS_TOKEN_EXPIRED) { + //늬프레시 토큰 확읞후 늬프레시 토큰 유횚하멎 새로욎 엑섞슀 토큰 발꞉ 만앜 늬프레시 토큰읎 유횚하지 않닀멎 재로귞읞 요청 + let payload: jwtModule.JWTPayload | undefined = jwtModule.Decode(admitPayload.accessToken as string); + if (payload == undefined) { + return resolve(ErrorType.JWTUnkownError); + } else { + let memberTicketData = await memberTicketStore.GetData(admitPayload.refreshToken as string, payload.memberKey as string); + if (memberTicketData == undefined) { + return resolve(ErrorType.InvalidAccessToken); + } + if (memberTicketData.access_token_expiry_time as number > Math.trunc(Date.now() / 1000) || memberTicketData.device_uuid != admitPayload.deviceUUID) {//디바읎슀 UUID 확읞 + // member ticket compromised + stampLogPrinter.Log(__filename, "Expired access token"); + await memberTicketStore.DeleteAllData(admitPayload.refreshToken as string); + return resolve(ErrorType.InvalidAccessToken); + } + + let result: string | TOKEN_ERROR = await GenerateNewAccessToken(admitPayload, payload); + if (result in TOKEN_ERROR) { + let resAdmitPacket: hdgbDTO.RESGBAdmitPacket = { + admitOutcome: hdgbDTO.AdmitOutcomeKind.ExpiredRefreshToken + } + return resolve(resAdmitPacket); + } else { + //AccessToken 발꞉후 닀시 Login + admitPayload.accessToken = result as string; + let memberKey = payload.memberKey as string; + if (await memberTicketStore.ExistData(admitPayload.refreshToken as string, payload.memberKey as string)) { + return resolve(await HandleAdmit(memberKey, admitPayload, hdgbDTO.AdmitOutcomeKind.ExpiredAccessToken, memberIp)); + } else { + stampLogPrinter.Log(__filename, "Can't find refresh token"); + return resolve(ErrorType.InvalidAccessToken); + } + } + } + } else if (errorType == TOKEN_ERROR.ACCESS_TOKEN_INVALID) { + stampLogPrinter.Log(__filename, "Invalid access token"); + return resolve(ErrorType.InvalidAccessToken); + } + } else { + let memberKey: string = (result as jwtModule.JWTPayload).memberKey as string; + if (await memberTicketStore.ExistData(admitPayload.refreshToken as string, memberKey)) { + return resolve(await HandleAdmit(memberKey, admitPayload, hdgbDTO.AdmitOutcomeKind.SuccessAccess, memberIp)); + } else { + stampLogPrinter.Log(__filename, "Can't find refresh token"); + return resolve(ErrorType.InvalidAccessToken); + } + } + }) +} + +function HandleAdmit(memberKey: string, admitPayload: hdgbDTO.REQGBAdmitPacket, admitKind: hdgbDTO.AdmitOutcomeKind, memberIp: string): Promise { + return new Promise(async (resolve, reject) => { + // open occupancy + let occupancyTicket: string = await occupancyKeeper.OpenOccupancy(memberKey, memberIp); + + //User FCM Register + let memberPushData = await memberPocketKeeper.memberPushPocket.GetData(memberKey); + memberPushData._fcmToken = admitPayload.fcmToken; + await memberPocketKeeper.memberPushPocket.SetData(memberKey, memberPushData); + + // load member record + admit kind + let userData = await memberPocketKeeper.memberRecordPocket.GetData(memberKey); + let memberSideData = await memberPocketKeeper.memberSidePocket.GetData(memberKey); + let isNewMember = memberSideData.user_tutorial_step == gameConfig.memberDefaultPrimerStep; + + //DTO 생성 + let resAdmitPacket: hdgbDTO.RESGBAdmitPacket = { + accessToken: admitPayload.accessToken, + admitOutcome: admitKind, + occupancyTicket: occupancyTicket, + isNewMember: isNewMember, + signType: userData.user_signin_type as hdgbDTO.EnrollKind + } + + return resolve(resAdmitPacket); + }); +} + +function GenerateNewAccessToken(admitPayload: hdgbDTO.REQGBAdmitPacket, payload: jwtModule.JWTPayload): Promise { + return new Promise(async (resolve, reject) => { + if (!await memberTicketStore.ExistData(admitPayload.refreshToken as string, payload.memberKey as string)) return resolve(TOKEN_ERROR.REFRESH_TOKEN_INVALID); + + let memberTicketData: MemberTicketRecord = await memberTicketStore.GetData(admitPayload.refreshToken as string, payload.memberKey as string); + + let nowTime: number = Math.trunc(Date.now() / 1000); + if (memberTicketData.refresh_token_expiry_time as number <= nowTime) { + //만료된 늬프레시 토큰 + return resolve(TOKEN_ERROR.REFRESH_TOKEN_EXPIRED); + } else { + //만료안된 늬프레시 토큰 + if (memberTicketData.user_refresh_token == admitPayload.refreshToken) { + //새롭게 ACCESSTOKEN 발꞉ + let accessToken: string = await accessTokenIssuer.IssueAccessToken(payload.memberKey as string); + return resolve(accessToken); + } else { + return resolve(TOKEN_ERROR.REFRESH_TOKEN_INVALID); + } + } + }) +} \ No newline at end of file diff --git a/app/core-app/source/service/alias-shift-usecase.ts b/app/core-app/source/service/alias-shift-usecase.ts new file mode 100644 index 0000000..bf2f067 --- /dev/null +++ b/app/core-app/source/service/alias-shift-usecase.ts @@ -0,0 +1,24 @@ +import { memberRelicPocket, memberAliasPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { NameChangeResultType, REQAliasShiftPacket as REQAliasShiftPacket, RESAliasShiftPacket } from "../../../../data/dto/hub-packet"; +import * as nameChangeHandler from "../handler/alias-shift-arranger"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default async (memberKey: string, reqDTO: REQAliasShiftPacket): Promise => { + return BoundMutationLane(async () => { + let memberAliasData = await memberAliasPocket.GetData(memberKey) + let memberAliasShiftRelic = await memberRelicPocket.GetData(memberKey, gameConfig.aliasShiftRelicCode.toString()); + if (memberAliasShiftRelic.item_amount as number > 0) { + memberAliasData.user_profile_name = reqDTO._name as string; + let nameChangeResultType = await nameChangeHandler.ShiftAlias(memberKey, memberAliasData); + if (nameChangeResultType == NameChangeResultType.SuccessChanged) { + await memberRelicPocket.TrimMemberRelicCount(memberKey, memberAliasShiftRelic, 1); + } + return { + _changeNameResult: nameChangeResultType + }; + } else { + throw new Error("Haven't nameChange item") + } + }); +} diff --git a/app/core-app/source/service/bazaar-checkout-usecase.ts b/app/core-app/source/service/bazaar-checkout-usecase.ts new file mode 100644 index 0000000..987c291 --- /dev/null +++ b/app/core-app/source/service/bazaar-checkout-usecase.ts @@ -0,0 +1,66 @@ +import { REQBazaarCheckoutPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { StockKind, BazaarOfferShape } from "../../../../data/model/play-shape/play-data-shape"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { MemberRelicRecord } from "../../../../data/entity/nexforge_db_record"; +import * as giveEntityHandler from "../handler/grant-stock-arranger" +import memberBazaarTraceStore from "../../../../manager/sql-keeper/member-data-keeper/member-bazaar-trace-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQBazaarCheckoutPacket): Promise => { + return BoundMutationLane(async () => { + let prouductData: BazaarOfferShape = await gameDataCachingManager.shopProudctDataHashPocketStore.GetData((reqDTO._productCode as number).toString()); + + if (prouductData._payItemCode == 0) { + throw new Error("Unable to process itemCode. Please use the IAP service."); + } + + if (prouductData._codeType as StockKind == StockKind.unit) { + if (await memberPocketKeeper.memberVesselPocket.ExistData(memberKey, (prouductData._elasticCode as number).toString())) { + throw new Error("Exist Unit Buy Error"); + } + } + + let memberRelicRecord: MemberRelicRecord = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, (prouductData._payItemCode as number).toString()); + + if (memberRelicRecord == null) { + throw new Error("Can't buy item"); + } + + if ((memberRelicRecord.item_amount as number) >= (prouductData._productPrice as number) * (reqDTO._productAmount as number)) { + if (prouductData._buyRestrictionCount != 0) { + let productBuyData = await memberPocketKeeper.memberBazaarTracePocket.GetData(memberKey, (prouductData._productCode as number).toString()); + if ((prouductData._buyRestrictionCount as number) <= (productBuyData.buy_count as number)) { + throw new Error("This Product is Sold Out"); + } else { + (productBuyData.buy_count as number) += 1; + await memberBazaarTraceStore.UpdateData(memberKey, prouductData._productCode as number, productBuyData); + await memberPocketKeeper.memberBazaarTracePocket.SetData(memberKey, (productBuyData.shop_product_code as number).toString(), productBuyData); + } + } + + let entityData = await giveEntityHandler.GrantStock(memberKey, (prouductData._elasticCode as number), prouductData._codeType as StockKind, prouductData._productAmount as number * (reqDTO._productAmount as number)); + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, memberRelicRecord, prouductData._productPrice as number * (reqDTO._productAmount as number)); + + if (prouductData._codeType == StockKind.item) { + return { + _itemData: entityData, + _entityType: prouductData._codeType + } + } else if (prouductData._codeType == StockKind.parts) { + return { + _partsData: entityData, + _entityType: prouductData._codeType + } + } else if (prouductData._codeType == StockKind.unit) { + return { + _unitData: entityData, + _entityType: prouductData._codeType + } + } + throw new Error("Can't buy item"); + } else { + throw new Error("Can't buy item"); + } + }) +} diff --git a/app/core-app/source/service/campaign-clear-usecase.ts b/app/core-app/source/service/campaign-clear-usecase.ts new file mode 100644 index 0000000..abd1291 --- /dev/null +++ b/app/core-app/source/service/campaign-clear-usecase.ts @@ -0,0 +1,121 @@ +import { REQCampaignClearPacket, REQCampaignPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { StockKind, CombatantSlot } from "../../../../data/model/play-shape/play-data-shape"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { BoutCloseKind, RESCloseBoutDTO } from "../../../../data/dto/duel-packet"; +import { VerifyGameToken } from "../../../../modules/admit-jwt-unit/admit-jwt-unit"; +import { GrantGrade } from "../../../../handler/grade-shift-arranger"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import { GetPairKey } from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-spec"; +import memberCampaignStore from "../../../../manager/sql-keeper/member-data-keeper/member-campaign-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; +import { ResolveCampaignClearAuthority } from "../../../../handler/campaign-clear-policy"; +import { TakeCampaignBout } from "../../../../manager/pocket-keeper/campaign-bout-keeper/campaign-bout-keeper"; +import * as env from "../../../../config/runtime-spec"; + +export default async (memberKey: string, reqDTO: REQCampaignClearPacket): Promise => { + const tokenData = VerifyGameToken(reqDTO._gameToken as string); + if (typeof tokenData === "number") { + throw new Error("Invalid campaign token"); + } + const decodedData: REQCampaignPacket = JSON.parse(tokenData.data as string) as REQCampaignPacket; + const bout = await TakeCampaignBout(memberKey, tokenData.boutId as string); + const memberCampaignData = await memberPocketKeeper.memberCampaignPocket.GetData(memberKey, (decodedData._adventureCode as number).toString()); + const verdict = ResolveCampaignClearAuthority({ + occupancyMemberKey: memberKey, + tokenMemberKey: tokenData.memberKey, + tokenBoutId: tokenData.boutId, + tokenAdventureCode: decodedData._adventureCode, + tokenAdventureSubCode: decodedData._adventureSubCode, + bout, + macSecret: env.admitTicketSecret, + claimedVictor: reqDTO._winPlayer, + winnerAliveUnitCount: reqDTO._winnerAliveUnitCount, + loserAliveUnitCount: reqDTO._loserAliveUnitCount, + memberProgressLevel: memberCampaignData.user_adventure_level as number + }); + if (!verdict.ok) { + throw new Error("Invalid campaign clear: " + verdict.reason); + } + + const adventureCode = GetPairKey(decodedData._adventureCode as number, decodedData._adventureSubCode as number); + if (!await gameDataCachingManager.campaignMasterPocket.ExistData(adventureCode)) { + throw new Error("Can not Entrance Adventure"); + } + + if (!verdict.grantReward) { + const memberCoreData = await memberPocketKeeper.memberCorePocket.GetData(memberKey); + return { + gameEndType: BoutCloseKind.Default, + giveEntityList: [], + giveEXP: 0, + grantStanding: 0, + nowExp: memberCoreData.user_exp as number, + isLeaveBout: false, + nowLevel: memberCoreData.user_level as number, + victorCombatant: verdict.victorCombatant, + nowTierCode: undefined, + nowScore: undefined, + }; + } + + const campaignData = await gameDataCachingManager.campaignMasterPocket.GetData(adventureCode); + return BoundMutationLane(async () => { + let giveEntityList: RESGrantStockDTO[] = []; + (memberCampaignData.user_adventure_level as number) += 1; + + let nextAdventureCode = GetPairKey(memberCampaignData.user_adventure_code as number, memberCampaignData.user_adventure_level as number); + + await memberPocketKeeper.memberCampaignPocket.SetData(memberKey, (memberCampaignData.user_adventure_code as number).toString(), memberCampaignData); + await memberCampaignStore.UpdateData(memberKey, memberCampaignData.user_adventure_code, memberCampaignData); + + if (!await gameDataCachingManager.campaignMasterPocket.ExistData(nextAdventureCode)) { + (memberCampaignData.user_adventure_code as number) += 1; + memberCampaignData.user_adventure_level = 1; + await memberPocketKeeper.memberCampaignPocket.SetData(memberKey, (memberCampaignData.user_adventure_code as number).toString(), memberCampaignData); + await memberCampaignStore.UpdateData(memberKey, memberCampaignData.user_adventure_code, memberCampaignData); + } + + let levelData = await GrantGrade(memberKey, campaignData._rewardExp as number); + + for (let i = 0; i < (campaignData._rewardAmount as number); i++) { + let entityData = await GrantStock(memberKey, campaignData._rewardCode as number, campaignData._rewardStockKind as StockKind, campaignData._rewardAmount as number); + let entity: RESGrantStockDTO = new RESGrantStockDTO(); + + if (campaignData._rewardStockKind == StockKind.item) { + entity = { + _entityType: StockKind.item, + _itemData: entityData + } + giveEntityList.push(entity); + break; + } else if (campaignData._rewardStockKind == StockKind.parts) { + entity = { + _entityType: StockKind.parts, + _partsData: entityData + } + giveEntityList.push(entity); + } else if (campaignData._rewardStockKind == StockKind.unit) { + entity = { + _entityType: StockKind.unit, + _unitData: entityData + } + giveEntityList.push(entity); + break; + } + } + + return { + gameEndType: BoutCloseKind.Default, + giveEntityList: giveEntityList, + giveEXP: campaignData._rewardExp as number, + victorCombatant: CombatantSlot.CombatantA, + nowExp: levelData.expAmount as number, + nowLevel: levelData.level as number, + grantStanding: undefined, + isLeaveBout: undefined, + nowScore: undefined, + nowTierCode: undefined, + } as RESCloseBoutDTO + }); +} diff --git a/app/core-app/source/service/campaign-usecase.ts b/app/core-app/source/service/campaign-usecase.ts new file mode 100644 index 0000000..dc73d4f --- /dev/null +++ b/app/core-app/source/service/campaign-usecase.ts @@ -0,0 +1,40 @@ +import { REQCampaignPacket, RESCampaignPacket } from "../../../../data/dto/hub-packet"; +import { PlayKind } from "../../../../data/model/play-shape/play-data-shape"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { campaignBotCombatantPocket, campaignBattlePocket } from "../../../../manager/pocket-keeper/campaign-pocket-keeper/campaign-pocket-keeper"; +import * as jwtModule from "../../../../modules/admit-jwt-unit/admit-jwt-unit"; +import { GetPairKey } from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-spec"; +import { MintId } from "../../../../modules/id-mint-unit/id-mint"; +import { CountLineVessels } from "../../../../handler/campaign-clear-policy"; +import { ParkCampaignBout } from "../../../../manager/pocket-keeper/campaign-bout-keeper/campaign-bout-keeper"; + +export default async (memberKey: string, reqDTO: REQCampaignPacket): Promise => { + let memberCampaignData = await memberPocketKeeper.memberCampaignPocket.GetData(memberKey, (reqDTO._adventureCode as number).toString()); + if (memberCampaignData == undefined) { + throw new Error("Can not Entrance Adventure"); + } + let adventureCode = GetPairKey(reqDTO._adventureCode as number, reqDTO._adventureSubCode as number); + if ((memberCampaignData.user_adventure_level as number) >= (reqDTO._adventureSubCode as number)) { + const boutId = MintId(); + const lineMap = await memberPocketKeeper.memberVesselLinePocket.GetAllData(memberKey); + await ParkCampaignBout({ + boutId, + memberKey, + adventureCode: reqDTO._adventureCode as number, + adventureSubCode: reqDTO._adventureSubCode as number, + vesselCount: CountLineVessels(lineMap) + }); + let tokenGroup = await jwtModule.IssueGameToken(memberKey.toString(), JSON.stringify(reqDTO), boutId); + let ingameData = await campaignBattlePocket.GetData(adventureCode); + ingameData._adventureCode = reqDTO._adventureCode as number; + ingameData._adventureSubCode = reqDTO._adventureSubCode as number; + let data: RESCampaignPacket = { + _aiCombatantShape: await campaignBotCombatantPocket.GetData(adventureCode), + _inGameData: ingameData, + _gameToken: tokenGroup.accessToken as string + }; + (data._inGameData as typeof ingameData)._gameType = PlayKind.CampaignPlay; + return data; + } + throw new Error("Can not Entrance Adventure"); +} diff --git a/app/core-app/source/service/claim-spinner-bounty-usecase.ts b/app/core-app/source/service/claim-spinner-bounty-usecase.ts new file mode 100644 index 0000000..356551f --- /dev/null +++ b/app/core-app/source/service/claim-spinner-bounty-usecase.ts @@ -0,0 +1,51 @@ +import { MemberAdBountyStore } from "../../../../manager/pocket-keeper/ad-bounty-keeper/ad-bounty-keeper"; +import { memberHubPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { RESSpinnerPacket } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import { rollInt } from "../../../../modules/kit-unit/chance-unit"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default async (memberKey: string): Promise => { + return BoundMutationLane(async () => { + let hubData = await memberHubPocket.GetData(memberKey); + if (hubData._userRouletteTime as number > Date.now() / 1000) { + throw new Error("roulette is not ready"); + } + if (await MemberAdBountyStore.ExistData(memberKey, "roulette")) { + let randomCode = rollInt(1, 6); + let rewardAmount = 0; + let rewardCode = 0; + + if (randomCode == 1) { + rewardAmount = 1000; + rewardCode = 1; + } else if (randomCode == 2) { + rewardAmount = 400; + rewardCode = 1; + } else if (randomCode == 3) { + rewardAmount = 5; + rewardCode = 2; + } else if (randomCode == 4) { + rewardAmount = 600; + rewardCode = 1; + } else if (randomCode == 5) { + rewardAmount = 800; + rewardCode = 1; + } else if (randomCode == 6) { + rewardAmount = 5; + rewardCode = 2; + } + hubData._userRouletteTime = Math.floor(Date.now() / 1000) + 60 * 30; + await memberHubPocket.SetData(memberKey, hubData); + await GrantStock(memberKey, rewardCode, StockKind.item, rewardAmount); + return { + _rouletteCode: randomCode + }; + } else { + return { + _rouletteCode: 0 + }; + } + }); +} diff --git a/app/core-app/source/service/client-pack-usecase.ts b/app/core-app/source/service/client-pack-usecase.ts new file mode 100644 index 0000000..d951bd6 --- /dev/null +++ b/app/core-app/source/service/client-pack-usecase.ts @@ -0,0 +1,23 @@ +import { + appID, +} from "../../../../config/enroll-unit-spec/facebook-auth-spec"; +import { googleClientID } from "../../../../config/enroll-unit-spec/google-auth-spec"; +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import * as gameCacheController from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper" + +//#endregion + +export default () => { + return new Promise(async (resolve, reject) => { + let defaultData: hdgbDTO.RESGBDefaultDTO; + let googleData: hdgbDTO.GoogleAuthData = { googleClientID: googleClientID }; + let facebookData: hdgbDTO.FacebookAuthData = { + facebookAppID: appID, + facebookAppSecretCode: "", + }; + let appData: hdgbDTO.ApplicationDefaultData = { appVersion: (await gameCacheController.gameDefaultCacheDAO.GetData()).game_version }; + + defaultData = { applicationDefaultData: appData, googleAuthData: googleData, facebookAuthData: facebookData }; + return resolve(defaultData); + }) +} diff --git a/app/core-app/source/service/craft-stock-usecase.ts b/app/core-app/source/service/craft-stock-usecase.ts new file mode 100644 index 0000000..566c718 --- /dev/null +++ b/app/core-app/source/service/craft-stock-usecase.ts @@ -0,0 +1,59 @@ +import { REQCraftStockPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQCraftStockPacket): Promise => { + return BoundMutationLane(async () => { + let craftRecipeData = await gameDataCachingManager.craftRecipePocket.GetData((reqDTO._productCode as number).toString()); + if (craftRecipeData._needMaterialList != null) { + if (craftRecipeData._codeType as StockKind == StockKind.unit) { + if (await memberPocketKeeper.memberVesselPocket.ExistData(memberKey, (craftRecipeData._elasticCode as number).toString())) { + throw new Error("Exist Unit Error"); + } + } + for (let i = 0; i < craftRecipeData._needMaterialList.length; i++) { + let materialData = craftRecipeData._needMaterialList[i]; + let memberMaterialData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, (materialData._materialCode as number).toString()); + if (memberMaterialData == null) { + throw new Error("Material Amount is not Enough"); + } + if ((memberMaterialData.item_amount as number) < (materialData._materialNeedAmount as number)) { + throw new Error("Material Amount is not Enough"); + } + } + } else { + throw new Error("Making Product Service Error"); + } + + let entityData = await GrantStock(memberKey, craftRecipeData._elasticCode as number, craftRecipeData._codeType as StockKind, craftRecipeData._productAmount as number); + + if (craftRecipeData._needMaterialList != null) { + for (let i = 0; i < craftRecipeData._needMaterialList.length; i++) { + let materialData = craftRecipeData._needMaterialList[i]; + let memberMaterialData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, (materialData._materialCode as number).toString()); + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, memberMaterialData, materialData._materialNeedAmount as number); + } + } + + if (craftRecipeData._codeType == StockKind.item) { + return { + _itemData: entityData, + _entityType: craftRecipeData._codeType, + } + } else if (craftRecipeData._codeType == StockKind.parts) { + return { + _partsData: entityData, + _entityType: craftRecipeData._codeType, + } + } else if (craftRecipeData._codeType == StockKind.unit) { + return { + _unitData: entityData, + _entityType: craftRecipeData._codeType, + } + } + throw new Error("Making Product Service Error"); + }) +} diff --git a/app/core-app/source/service/enroll-usecase.ts b/app/core-app/source/service/enroll-usecase.ts new file mode 100644 index 0000000..93558da --- /dev/null +++ b/app/core-app/source/service/enroll-usecase.ts @@ -0,0 +1,39 @@ +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import { AuthParams } from "../../../../config/enroll-unit-spec/enroll-spec"; +import * as env from '../../../../config/runtime-spec' +//import { KeyWeave } from "../module/security_module/key-weave-unit"; +import * as aesMoudle from "../../../../modules/cipher-unit/scramble-unit"; +import * as signConfig from "../../../../config/enroll-unit-spec/enroll-spec"; +import * as enrollUnit from "../handler/member-admit-arranger"; + +export default (enrollPayload: hdgbDTO.REQGBEnrollPacket): Promise => { + return new Promise(async (resolve, reject) => { + if (enrollPayload.enrollKind?.valueOf() == hdgbDTO.EnrollKind.Guest.valueOf()) { + resolve(await enrollUnit.default.Sign(enrollPayload.deviceUUID as string, enrollPayload.countryCode, enrollPayload.enrollKind.toString(), enrollPayload.deviceUUID as string)); + } else { + //Social Login 처늬 + let authModule: (authParams: AuthParams) => void = + signConfig.authModuleMap.get(enrollPayload.enrollKind) as ( + authParams: AuthParams + ) => void; //Deligate + + let enrollPacket: hdgbDTO.RESGBEnrollPacket = await new Promise(async (resolve, reject) => { + authModule({ + authToken: enrollPayload.authCode as string, + memberKey: enrollPayload.memberKey as string, + successAction: async ( + memberKey: string + ) => { + resolve(await enrollUnit.default.Sign(memberKey, enrollPayload.countryCode, enrollPayload.enrollKind?.toString(), enrollPayload.deviceUUID as string)); + }, + }); + }); + resolve(enrollPacket); + } + + //let dhModule: KeyWeave = new KeyWeave(3, 1559, 100, 30, "."); + // let decryptDataString: string = aesMoudle.decrypt(JSON.stringify(resSignData), appSecretKey); + // let decryptData: dtco.RESGBEnrollPayload = JSON.parse(decryptDataString); + // //console.log(aesMoudle.decrypt(decryptData.aesSecretKey as string, secretKey)); + }) +} diff --git a/app/core-app/source/service/expand-gadget-bay-usecase.ts b/app/core-app/source/service/expand-gadget-bay-usecase.ts new file mode 100644 index 0000000..5b07fb2 --- /dev/null +++ b/app/core-app/source/service/expand-gadget-bay-usecase.ts @@ -0,0 +1,22 @@ +import { memberSidePocket, memberRelicPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper" +import * as gameConfig from "../../../../config/play-baseline-spec"; +import memberSideStore from "../../../../manager/sql-keeper/member-data-keeper/member-side-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default async (memberKey: string) => { + return BoundMutationLane(async () => { + let gadgetBayCoupon = await memberRelicPocket.GetData(memberKey, gameConfig.gadgetBayExpandRelicCode.toString()); + if (gadgetBayCoupon.item_amount as number > 0) { + let memberSideData = await memberSidePocket.GetData(memberKey); + if (memberSideData.user_parts_max_count as number >= gameConfig.expandGadgetBayMaxValue) { + throw new Error("Max parts slot"); + } + await memberRelicPocket.TrimMemberRelicCount(memberKey, gadgetBayCoupon, 1); + (memberSideData.user_parts_max_count as number) += gameConfig.expandGadgetBayValue; + await memberSideStore.UpdateData(memberKey, memberSideData); + await memberSidePocket.SetData(memberKey, memberSideData); + } else { + throw new Error("Haven't parts slot coupon"); + } + }); +} diff --git a/app/core-app/source/service/gadget-refine-usecase.ts b/app/core-app/source/service/gadget-refine-usecase.ts new file mode 100644 index 0000000..9888495 --- /dev/null +++ b/app/core-app/source/service/gadget-refine-usecase.ts @@ -0,0 +1,41 @@ +import { REQGadgetRefinePacket, RESResultDTO } from "../../../../data/dto/hub-packet" +import * as memberPocketLedger from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameCacheDataManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import { BandKind } from "../../../../data/model/play-shape/play-data-shape"; +import { rollInt } from "../../../../modules/kit-unit/chance-unit"; +import * as gameDefaultValueConfig from "../../../../config/play-baseline-spec"; +import memberGadgetStore from "../../../../manager/sql-keeper/member-data-keeper/member-gadget-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQGadgetRefinePacket): Promise => { + return BoundMutationLane(async () => { + if (await memberPocketLedger.memberGadgetPocket.ExistData(memberKey, reqDTO._targetPartsUUID as string) && + await memberPocketLedger.memberGadgetPocket.ExistData(memberKey, reqDTO._sacrificePartsUUID as string)) { + + let targetGadgetData = await memberPocketLedger.memberGadgetPocket.GetData(memberKey, reqDTO._targetPartsUUID as string); + if (targetGadgetData.parts_level as number < gameDefaultValueConfig.gadgetMaxGrade) { + let tributeGadgetData = await memberPocketLedger.memberGadgetPocket.GetData(memberKey, reqDTO._sacrificePartsUUID as string); + let targetGadgetMaster = await gameCacheDataManager.gadgetMasterPocket.GetData((targetGadgetData.parts_code as number).toString()); + let tributeGadgetMaster = await gameCacheDataManager.gadgetMasterPocket.GetData((tributeGadgetData.parts_code as number).toString()); + let targetGadgetStanding = ((await gameCacheDataManager.gadgetBandRefinePocket.GetData(targetGadgetMaster._partsBandKind as BandKind))._upgradeAbleScoreValueList as number[])[targetGadgetData.parts_level as number]; + let tributeGadgetStanding = (await gameCacheDataManager.gadgetBandRefinePocket.GetData(tributeGadgetMaster._partsBandKind as BandKind))._upgradeScore as number; + let randomValue = rollInt(0, targetGadgetStanding - 1); + + let isSuccess = false; + if (randomValue < tributeGadgetStanding) { + let memberGadgetData = await memberPocketLedger.memberGadgetPocket.GetData(memberKey, reqDTO._targetPartsUUID as string); + (memberGadgetData.parts_level as number) += 1; + await memberPocketLedger.memberGadgetPocket.SetData(memberKey, reqDTO._targetPartsUUID as string, memberGadgetData); + await memberGadgetStore.UpdateData(memberKey, reqDTO._targetPartsUUID as string, memberGadgetData); + isSuccess = true; + } + await memberPocketLedger.memberVesselGadgetPocket.RemoveData(memberKey, reqDTO._sacrificePartsUUID as string); + await memberGadgetStore.DeleteData(memberKey, reqDTO._sacrificePartsUUID as string); + return { + _isSuccess: isSuccess + } + } + } + throw new Error("It cannot be processed."); + }) +} diff --git a/app/core-app/source/service/new-member-setup-usecase.ts b/app/core-app/source/service/new-member-setup-usecase.ts new file mode 100644 index 0000000..2e659b8 --- /dev/null +++ b/app/core-app/source/service/new-member-setup-usecase.ts @@ -0,0 +1,64 @@ +import * as hubPacket from "../../../../data/dto/hub-packet"; +import * as nameChangeHandler from "../handler/alias-shift-arranger"; +import { MemberAvatarRecord, MemberRecord, MemberCoreRecord, MemberAliasRecord, MemberSideRecord } from "../../../../data/entity/nexforge_db_record"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import memberConsentStore from "../../../../manager/sql-keeper/member-data-keeper/member-consent-store"; +import memberStore from "../../../../manager/sql-keeper/member-data-keeper/member-store"; +import { memberAliasPocket, memberPushPocket, memberRecordPocket, memberSidePocket, memberCorePocket, memberAvatarPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { EnrollKind } from "../../../../data/dto/core-packet"; +import memberSideStore from "../../../../manager/sql-keeper/member-data-keeper/member-side-store"; +import memberAvatarStore from "../../../../manager/sql-keeper/member-data-keeper/member-avatar-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default async (memberKey: string, newMemberSetupPacket: hubPacket.REQNewMemberSetupPacket): Promise => { + return BoundMutationLane(async () => { + let memberAliasRecord: MemberAliasRecord = await memberAliasPocket.GetData(memberKey); + let memberRecord: MemberRecord = await memberRecordPocket.GetData(memberKey); + let memberSideRecord: MemberSideRecord = await memberSidePocket.GetData(memberKey); + let memberCoreRecord: MemberCoreRecord = await memberCorePocket.GetData(memberKey); + let nameChangeResultType: hubPacket.NameChangeResultType = hubPacket.NameChangeResultType.SuccessChanged; + + if (memberSideRecord.user_tutorial_step != gameConfig.memberDefaultPrimerStep) { + throw new Error("Not a new user"); + } + if (memberAliasRecord.user_profile_name == gameConfig.memberAnonInitial + memberKey) { + if (memberRecord.user_signin_type as EnrollKind != EnrollKind.Guest) { + memberAliasRecord.user_profile_name = newMemberSetupPacket._userName; + nameChangeResultType = await nameChangeHandler.ShiftAlias(memberKey, memberAliasRecord); + } + } + await memberConsentStore.InsertData({ + user_uuid: memberKey as string, + consent_game_terms: (newMemberSetupPacket._userAgreementList as boolean[])[0], + consent_personal_info_terms: (newMemberSetupPacket._userAgreementList as boolean[])[1], + }) + + let memberPushData = await memberPushPocket.GetData(memberKey); + memberPushData._consentFcmReceive = (newMemberSetupPacket._userAgreementList as boolean[])[2]; + await memberPushPocket.SetData(memberKey, memberPushData); + + let resData: hubPacket.RESNewMemberSetupPacket = + { + _changedUserName: memberAliasRecord.user_profile_name, + _reqResult: nameChangeResultType + } + + if (nameChangeResultType == hubPacket.NameChangeResultType.SuccessChanged) { + memberSideRecord.user_tutorial_step = gameConfig.memberPrimerFinishStep; + memberCoreRecord.user_character_code = newMemberSetupPacket._userSelectedCharacter as number; + let memberAvatarRecord: MemberAvatarRecord = { + character_code: newMemberSetupPacket._userSelectedCharacter as number, + user_uuid: memberKey + } + await memberAvatarPocket.SetData(memberKey, (newMemberSetupPacket._userSelectedCharacter as number).toString(), memberAvatarRecord); + await memberSidePocket.SetData(memberKey, memberSideRecord); + await memberCorePocket.SetData(memberKey, memberCoreRecord); + await memberAvatarStore.InsertData(memberAvatarRecord); + await memberSideStore.UpdateData(memberKey, memberSideRecord); + } + + await memberRecordPocket.SetData(memberKey, memberRecord); + await memberStore.UpdateData(memberKey, memberRecord); + return resData; + }); +} diff --git a/app/core-app/source/service/open-crate-instant-usecase.ts b/app/core-app/source/service/open-crate-instant-usecase.ts new file mode 100644 index 0000000..23b4193 --- /dev/null +++ b/app/core-app/source/service/open-crate-instant-usecase.ts @@ -0,0 +1,33 @@ +import { REQRelicPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import openCrateArranger from "../handler/open-crate-arranger"; +import { BandKind } from "../../../../data/model/play-shape/play-data-shape"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQRelicPacket): Promise => { + return BoundMutationLane(async () => { + let memberTokenRelicData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, gameConfig.pocketRelicCode.toString()); + let memberCrateRelicData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, (reqDTO._itemCode as number).toString()); + let itemGameData = await gameDataCachingManager.relicMasterPocket.GetData((reqDTO._itemCode as number).toString()); + let crateBandData = await gameDataCachingManager.crateBandPocket.GetData(itemGameData._itemBandKind as BandKind); + + if (itemGameData == null || memberTokenRelicData == null || memberCrateRelicData == null || crateBandData == null) { + throw new Error("Open Instant Cheest Error"); + } + + if (memberTokenRelicData == undefined || memberCrateRelicData == undefined) { + throw new Error("Not enough items"); + } + + if ((crateBandData._needCache as number) <= (memberTokenRelicData.item_amount as number) || 1 <= (memberCrateRelicData.item_amount as number)) { + let result = await openCrateArranger(memberKey, reqDTO._itemCode as number); + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, memberCrateRelicData, 1); + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, memberTokenRelicData, crateBandData._needCache as number); + return result; + } else { + throw new Error("Open Instant Cheest Error"); + } + }) +} diff --git a/app/core-app/source/service/open-crate-usecase.ts b/app/core-app/source/service/open-crate-usecase.ts new file mode 100644 index 0000000..178cff3 --- /dev/null +++ b/app/core-app/source/service/open-crate-usecase.ts @@ -0,0 +1,22 @@ +import { REQCratePacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import openCrateArranger from "../handler/open-crate-arranger"; +import memberCrateStore from "../../../../manager/sql-keeper/member-data-keeper/member-crate-store"; +import { memberCratePocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQCratePacket): Promise => { + return BoundMutationLane(async () => { + let memberCrateData = await memberCratePocket.GetData(memberKey, reqDTO._chestUUID as string); + if (memberCrateData == null) { + throw new Error("It cannot be processed."); + } + if (memberCrateData.chest_open_time as number <= Math.trunc(Date.now() / 1000)) { + let result = await openCrateArranger(memberKey, memberCrateData.item_code as number); + await memberCratePocket.RemoveData(memberKey, memberCrateData.chest_uuid as string); + await memberCrateStore.DeleteAllData(memberCrateData.chest_uuid as string); + return result; + } else { + throw new Error("It cannot be processed."); + } + }) +} diff --git a/app/core-app/source/service/open-parcel-usecase.ts b/app/core-app/source/service/open-parcel-usecase.ts new file mode 100644 index 0000000..aec5def --- /dev/null +++ b/app/core-app/source/service/open-parcel-usecase.ts @@ -0,0 +1,52 @@ +import { memberParcelPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { REQOpenParcelPacket, RESGrantStockDTO, RESOpenParcelPacket } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/entity/nexforge_db_record"; +import * as giveEntityHandler from "../handler/grant-stock-arranger" +import memberParcelStore from "../../../../manager/sql-keeper/member-data-keeper/member-parcel-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQOpenParcelPacket): Promise => { + return BoundMutationLane(async () => { + let memberParcelData = await memberParcelPocket.GetData(memberKey, reqDTO._giftUUID as string); + let date = new Date(); + date.setHours(0, 0, 0, 0); + if (memberParcelData.expired_time as number >= ((date.getTime() / 1000))) { + let giveData = await giveEntityHandler.GrantStock(memberKey, memberParcelData.gift_code as number, memberParcelData.gift_code_type as StockKind, memberParcelData.gift_amount as number); + await memberParcelPocket.RemoveData(memberKey, memberParcelData.gift_uuid as string); + await memberParcelStore.DeleteData(memberKey, memberParcelData.gift_uuid as string); + + if (memberParcelData.gift_code_type == StockKind.item) { + return { + _entityData: { + _itemData: giveData, + _entityType: memberParcelData.gift_code_type + } + , _message: "success" + } + } else if (memberParcelData.gift_code_type == StockKind.parts) { + return { + _entityData: { + _partsData: giveData, + _entityType: memberParcelData.gift_code_type + } + , _message: "success" + } + } else if (memberParcelData.gift_code_type == StockKind.unit) { + return { + _entityData: { + _unitData: giveData, + _entityType: memberParcelData.gift_code_type + } + , _message: "success" + } + } + return { + _message: "success" + } + } else { + return { + _message: "expiryed gift" + } + } + }); +} diff --git a/app/core-app/source/service/park-crate-usecase.ts b/app/core-app/source/service/park-crate-usecase.ts new file mode 100644 index 0000000..327a708 --- /dev/null +++ b/app/core-app/source/service/park-crate-usecase.ts @@ -0,0 +1,42 @@ +import { REQRelicPacket, RESParkedCratePacket } from "../../../../data/dto/hub-packet"; +import memberCrateStore from "../../../../manager/sql-keeper/member-data-keeper/member-crate-store"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import { BandKind } from "../../../../data/model/play-shape/play-data-shape"; +import { memberCratePocket, memberRelicPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { MintId } from "../../../../modules/id-mint-unit/id-mint"; +import { MemberCrateRecord } from "../../../../data/entity/nexforge_db_record"; +import * as config from "../../../../config/play-baseline-spec" +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQRelicPacket): Promise => { + return BoundMutationLane(async () => { + let memberCrateRelicData = await memberRelicPocket.GetData(memberKey, (reqDTO._itemCode as number).toString()); + let itemGameData = await gameDataCachingManager.relicMasterPocket.GetData((reqDTO._itemCode as number).toString()); + let crateBandData = await gameDataCachingManager.crateBandPocket.GetData(itemGameData._itemBandKind as BandKind); + + if (memberCrateRelicData.item_amount as number > 0 && Object.keys((await memberCratePocket.GetAllData(memberKey))).length < config.crateParkCount) { + let crateOpenAt = Math.trunc(Date.now() / 1000 + (crateBandData._needTime as number)); + let crateRelicCode = memberCrateRelicData.item_code; + let chestUUID = MintId(); + + let crateRecord: MemberCrateRecord = { + user_uuid: memberKey, + chest_open_time: crateOpenAt, + item_code: crateRelicCode, + chest_uuid: chestUUID + } + + await memberRelicPocket.TrimMemberRelicCount(memberKey, memberCrateRelicData, 1); + await memberCratePocket.SetData(memberKey, chestUUID, crateRecord) + await memberCrateStore.InsertData(crateRecord) + return { + _userChestData: { + _chestOpenTime: crateOpenAt, + _itemCode: crateRelicCode, + _chestUUID: chestUUID, + } + } + } + throw new Error("It cannot be processed."); + }) +} diff --git a/app/core-app/source/service/primer-usecase.ts b/app/core-app/source/service/primer-usecase.ts new file mode 100644 index 0000000..fca4440 --- /dev/null +++ b/app/core-app/source/service/primer-usecase.ts @@ -0,0 +1,31 @@ +import { memberSidePocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper" +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import { IncreaseAchivementLogData } from "../../../../manager/pocket-keeper/feat-trace-keeper/feat-trace-keeper"; +import memberSideStore from "../../../../manager/sql-keeper/member-data-keeper/member-side-store"; + +export const post = async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + let memberSideData = await memberSidePocket.GetData(memberKey); + if (memberSideData.user_tutorial_step == gameConfig.memberDefaultPrimerStep) { + reject(new Error("pls process start user setting")) + } + if (memberSideData.user_tutorial_step as number < gameConfig.memberMaxPrimerStep) { + (memberSideData.user_tutorial_step as number) += 1; + if (memberSideData.user_tutorial_step == gameConfig.memberMaxPrimerStep) { + await IncreaseAchivementLogData(memberKey, 1, 1); + } + await memberSidePocket.SetData(memberKey, memberSideData); + resolve(memberSideData.user_tutorial_step as number); + await memberSideStore.UpdateData(memberKey, memberSideData) + } else { + reject(new Error("max tutorial step")); + } + }); +} + +export const get = (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + resolve((await memberSidePocket.GetData(memberKey)).user_tutorial_step as number); + }); +} \ No newline at end of file diff --git a/app/core-app/source/service/push-opt-usecase.ts b/app/core-app/source/service/push-opt-usecase.ts new file mode 100644 index 0000000..658ceac --- /dev/null +++ b/app/core-app/source/service/push-opt-usecase.ts @@ -0,0 +1,8 @@ +import { REQPushOptPacket } from "../../../../data/dto/hub-packet" +import * as memberPocketLedger from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; + +export default async (memberKey: string, reqDTO: REQPushOptPacket) => { + let memberPushData = await memberPocketLedger.memberPushPocket.GetData(memberKey); + memberPushData._consentFcmReceive = reqDTO._fcmSendPermit as boolean; + await memberPocketLedger.memberPushPocket.SetData(memberKey, memberPushData); +} diff --git a/app/core-app/source/service/read-cycle-usecase.ts b/app/core-app/source/service/read-cycle-usecase.ts new file mode 100644 index 0000000..e43c07b --- /dev/null +++ b/app/core-app/source/service/read-cycle-usecase.ts @@ -0,0 +1,10 @@ +import * as hubPacket from "../../../../data/dto/hub-packet"; +import * as cycleKeeper from "../../../../manager/pocket-keeper/cycle-keeper/cycle-keeper" + +export default async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + resolve({ + _seasonCode: await cycleKeeper.ReadCurrentCycle() + }) + }) +} diff --git a/app/core-app/source/service/read-delta-usecase.ts b/app/core-app/source/service/read-delta-usecase.ts new file mode 100644 index 0000000..f5d3301 --- /dev/null +++ b/app/core-app/source/service/read-delta-usecase.ts @@ -0,0 +1,30 @@ + +import { GetAchivementLogData, MemberFeatFlagStore } from "../../../../manager/pocket-keeper/feat-trace-keeper/feat-trace-keeper"; +import { ReadParcelPocket, MemberParcelFlagPocket } from "../../../../manager/pocket-keeper/parcel-keeper/parcel-keeper"; +import * as hubPacket from "../../../../data/dto/hub-packet"; + +export default async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + let memberParcelFlag = await MemberParcelFlagPocket.GetData(memberKey); + let memberTraceFlag = await MemberFeatFlagStore.GetData(memberKey); + let memberDeltaData: hubPacket.RESUpdateDataDTO = new hubPacket.RESUpdateDataDTO(); + if (memberParcelFlag != null) { + if (memberParcelFlag.isChangeGift) { + memberParcelFlag.isChangeGift = false; + await MemberParcelFlagPocket.SetData(memberKey, memberParcelFlag); + memberDeltaData._giftData = await ReadParcelPocket(memberKey); + memberDeltaData._giftStatus = true; + + } + } + if (memberTraceFlag != null) { + if (memberTraceFlag.isChangedAchivement) { + memberTraceFlag.isChangedAchivement = false; + MemberFeatFlagStore.SetData(memberKey, memberTraceFlag); + memberDeltaData._logStatus = true; + memberDeltaData._logData = await GetAchivementLogData(memberKey); + } + } + resolve(memberDeltaData) + }) +} diff --git a/app/core-app/source/service/read-duel-stage-usecase.ts b/app/core-app/source/service/read-duel-stage-usecase.ts new file mode 100644 index 0000000..1ffec0b --- /dev/null +++ b/app/core-app/source/service/read-duel-stage-usecase.ts @@ -0,0 +1,10 @@ +import * as hubPacket from "../../../../data/dto/hub-packet"; +import * as duelStageKeeper from "../../../../manager/pocket-keeper/duel-stage-keeper/duel-stage-keeper" + +export default async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + resolve({ + _mapCode: await duelStageKeeper.PickDuelStageCode() + }) + }) +} diff --git a/app/core-app/source/service/read-host-health-usecase.ts b/app/core-app/source/service/read-host-health-usecase.ts new file mode 100644 index 0000000..650c0ef --- /dev/null +++ b/app/core-app/source/service/read-host-health-usecase.ts @@ -0,0 +1,11 @@ +import * as hdgbDTO from "../../../../data/dto/core-packet"; +import * as inspectionManager from "../../../../manager/pocket-keeper/host-hold-keeper/host-hold-keeper"; + +export default () => { + return new Promise(async (resolve, reject) => { + let serverData: hdgbDTO.RESGBServerDTO = { + serverStatus: await inspectionManager.GetServerStatus() + }; + return resolve(serverData); + }) +} diff --git a/app/core-app/source/service/read-masterdata-usecase.ts b/app/core-app/source/service/read-masterdata-usecase.ts new file mode 100644 index 0000000..18fbc64 --- /dev/null +++ b/app/core-app/source/service/read-masterdata-usecase.ts @@ -0,0 +1,25 @@ +import { GameRankDataModel } from "../../../../data/model/play-shape/play-data-shape"; +import PlayShape from "../../../../data/model/play-shape/PlayShape"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper" +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import MemberShape from "../../../../data/model/member-shape/MemberShape"; +import * as hubPacket from "../../../../data/dto/hub-packet"; + +export default async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + let gameModel: PlayShape = await gameDataCachingManager.gameBlobPocketStore.GetData(); + let gameRankData = await gameDataCachingManager.rankingCacheDAO.GetData(); + + if (gameRankData == null) { + gameRankData = {}; + } + + gameModel._rankingDataDictionary = gameRankData; + + let memberShape: MemberShape = await memberPocketKeeper.GetMemberShape(memberKey); + return resolve({ + _gameData: gameModel, + _userData: memberShape + }); + }) +} diff --git a/app/core-app/source/service/shatter-gadget-usecase.ts b/app/core-app/source/service/shatter-gadget-usecase.ts new file mode 100644 index 0000000..dfe1815 --- /dev/null +++ b/app/core-app/source/service/shatter-gadget-usecase.ts @@ -0,0 +1,28 @@ +import { REQShatterGadgetPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { BandKind, StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { rollInt } from "../../../../modules/kit-unit/chance-unit"; +import memberGadgetStore from "../../../../manager/sql-keeper/member-data-keeper/member-gadget-store"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQShatterGadgetPacket): Promise => { + return BoundMutationLane(async () => { + if (await memberPocketKeeper.memberGadgetPocket.ExistData(memberKey, reqDTO._targetPartsUUID as string)) { + let gadgetMemberData = await memberPocketKeeper.memberGadgetPocket.GetData(memberKey, reqDTO._targetPartsUUID as string); + let gadgetMasterData = await gameDataCachingManager.gadgetMasterPocket.GetData((gadgetMemberData.parts_code as number).toString()) + let gadgetShatterData = await gameDataCachingManager.gadgetShatterPocket.GetData(gadgetMasterData._partsBandKind as BandKind); + let gadgetShardAmount = rollInt(gadgetShatterData._minAmount as number, gadgetShatterData._maxAmount as number); + let entityData = await GrantStock(memberKey, gameConfig.gadgetShardRelicCode, StockKind.item, gadgetShardAmount); + await memberPocketKeeper.memberGadgetPocket.RemoveData(memberKey, reqDTO._targetPartsUUID as string); + await memberGadgetStore.DeleteData(memberKey, reqDTO._targetPartsUUID as string); + return { + _entityType: StockKind.item, + _itemData: entityData + } + } + throw new Error("Can't decompose parts"); + }) +} diff --git a/app/core-app/source/service/storefront-checkout-usecase.ts b/app/core-app/source/service/storefront-checkout-usecase.ts new file mode 100644 index 0000000..c40c77f --- /dev/null +++ b/app/core-app/source/service/storefront-checkout-usecase.ts @@ -0,0 +1,45 @@ +import MemberCheckoutStore from "../../../../manager/sql-keeper/member-data-keeper/member-checkout-store"; +import { REQPurchaseProductDTO, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/entity/nexforge_db_record"; +import { AcknowledgePurchase, GetReceiptData } from "../../../../modules/storefront-unit/storefront-api"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; +import { GetProductEntityCode } from "../../../../modules/storefront-unit/storefront-product-map"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; + +export default async (memberKey: string, purchaseRecipt: REQPurchaseProductDTO): Promise => { + let reciptData = JSON.parse(purchaseRecipt._receipt as string); + let payloadData = JSON.parse(reciptData["Payload"] as string); + let jsonData = JSON.parse(payloadData["json"] as string); + let purchaseToken = jsonData["purchaseToken"]; + let productId = jsonData["productId"]; + let realReceiptData = await GetReceiptData(productId, purchaseToken); + let orderId = realReceiptData.orderId as string; + let storeType = reciptData["Store"]; + + const granted = await BoundMutationLane(async () => { + if (await MemberCheckoutStore.ExistData(orderId)) { + throw new Error("Already Exist Order UUID"); + } + await MemberCheckoutStore.InsertData({ + iap_type: storeType, + purchase_order_uuid: orderId, + purchase_time: parseInt(String(realReceiptData.purchaseTime)), + product_id: productId, + user_uuid: memberKey + }); + let entityCode = GetProductEntityCode(productId); + if (entityCode == -1) throw new Error("Not Exist Product Id"); + let entity = await GrantStock(memberKey, entityCode, StockKind.item, 1); + return { + _entityType: StockKind.item, + _itemData: entity + } + }); + try { + await AcknowledgePurchase(productId, purchaseToken, realReceiptData); + } catch (err: NodeJS.ErrnoException | any) { + stampLogPrinter.Log(__filename, "Play acknowledge failed: " + err.message); + } + return granted; +} diff --git a/app/core-app/source/service/swap-azure-token-usecase.ts b/app/core-app/source/service/swap-azure-token-usecase.ts new file mode 100644 index 0000000..8105226 --- /dev/null +++ b/app/core-app/source/service/swap-azure-token-usecase.ts @@ -0,0 +1,33 @@ +import * as gameConfig from "../../../../config/play-baseline-spec"; +import { memberRelicPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { REQRelicPacket } from "../../../../data/dto/hub-packet"; +import { StockKind } from "../../../../data/model/play-shape/play-data-shape"; +import { MemberRelicShape } from "../../../../data/model/member-shape/member-data-shape"; +import { GrantStock } from "../handler/grant-stock-arranger"; +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default async (memberKey: string, reqDTO: REQRelicPacket): Promise => { + return BoundMutationLane(async () => { + let userItem = await memberRelicPocket.GetData(memberKey, (reqDTO._itemCode as number).toString()); + let itemData: MemberRelicShape | null = null; + + if (userItem.item_amount as number > 0) { + await memberRelicPocket.TrimMemberRelicCount(memberKey, userItem, 1); + + if (userItem.item_code == gameConfig.blueCoin100Code) { + itemData = await GrantStock(memberKey, 2, StockKind.item, 100); + } else if (userItem.item_code == gameConfig.blueCoin200Code) { + itemData = await GrantStock(memberKey, 2, StockKind.item, 200); + } else if (userItem.item_code == gameConfig.blueCoin700Code) { + itemData = await GrantStock(memberKey, 2, StockKind.item, 700); + } else if (userItem.item_code == gameConfig.blueCoin1500Code) { + itemData = await GrantStock(memberKey, 2, StockKind.item, 1500); + } + + if (itemData == null) throw new Error("give item null"); + return itemData as MemberRelicShape; + } else { + throw new Error("Not enough item"); + } + }); +} diff --git a/app/core-app/source/service/vessel-gadget-loadout-usecase.ts b/app/core-app/source/service/vessel-gadget-loadout-usecase.ts new file mode 100644 index 0000000..e5e4155 --- /dev/null +++ b/app/core-app/source/service/vessel-gadget-loadout-usecase.ts @@ -0,0 +1,23 @@ +import { REQBazaarCheckoutPacket, REQVesselGadgetLoadoutPacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { MemberVesselLinePocketShape } from "../../../../data/model/pocket-shape/member-pocket-shape"; +import { memberVesselGadgetPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import * as _userDataCachingManager from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; + +export default async (memberKey: string, reqDTO: REQVesselGadgetLoadoutPacket) => { + if (reqDTO._unitUsingPartsList.length <= gameConfig.vesselGadgetBayCount) { + for (let index = 0; index < reqDTO._unitUsingPartsList.length; index++) { + let element = reqDTO._unitUsingPartsList[index]; + if (element == "0") continue; + if (!await _userDataCachingManager.memberGadgetPocket.ExistData(memberKey, element)) { + throw new Error("Not Exist Parts"); + } + } + await memberVesselGadgetPocket.SetData(memberKey, (reqDTO._unitCode as number).toString(), { + unitCode: reqDTO._unitCode, + vesselMountedGadgetList: reqDTO._unitUsingPartsList + }) + } else { + throw new Error("Unit Parts Setting Service Error"); + } +} \ No newline at end of file diff --git a/app/core-app/source/service/vessel-line-usecase.ts b/app/core-app/source/service/vessel-line-usecase.ts new file mode 100644 index 0000000..cea7ce5 --- /dev/null +++ b/app/core-app/source/service/vessel-line-usecase.ts @@ -0,0 +1,17 @@ +import { REQBazaarCheckoutPacket, REQVesselLinePacket, RESGrantStockDTO } from "../../../../data/dto/hub-packet"; +import { MemberVesselLinePocketShape } from "../../../../data/model/pocket-shape/member-pocket-shape"; +import { memberVesselLinePocket, memberVesselPocket } from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameConifg from "../../../../config/play-baseline-spec"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; + +export default async (memberKey: string, reqDTO: REQVesselLinePacket) => { + if (reqDTO._unitCode as number != 0) { + if (!await memberPocketKeeper.memberVesselPocket.ExistData(memberKey, (reqDTO._unitCode as number).toString())) { + throw new Error("Unit Order Setting Service Error"); + } + } + await memberVesselLinePocket.SetData(memberKey, (reqDTO._orderCode as number).toString(), { + _orderCode: reqDTO._orderCode as number, + _unitCode: reqDTO._unitCode as number + }); +} diff --git a/app/core-app/source/service/vessel-refine-usecase.ts b/app/core-app/source/service/vessel-refine-usecase.ts new file mode 100644 index 0000000..604b146 --- /dev/null +++ b/app/core-app/source/service/vessel-refine-usecase.ts @@ -0,0 +1,41 @@ +import { REQVesselRefinePacket, RESResultDTO } from "../../../../data/dto/hub-packet" +import memberVesselStore from "../../../../manager/sql-keeper/member-data-keeper/member-vessel-store"; +import * as gameDefaultValue from "../../../../config/play-baseline-spec"; +import * as gameUpgradeValueManager from "../../../../manager/pocket-keeper/refine-pocket-keeper/refine-pocket-keeper"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper" +import { BoundMutationLane } from "../../../../modules/sql-unit/sql-link"; + +export default (memberKey: string, reqDTO: REQVesselRefinePacket): Promise => { + return BoundMutationLane(async () => { + let targetUnitData = await memberPocketKeeper.memberVesselPocket.GetData(memberKey, (reqDTO._targetUnitCode as number).toString()); + let targetUnitGameData = await gameDataCachingManager.unitDataHashPocketStore.GetData((targetUnitData.unit_code as number).toString()); + let targetUnitMaterialData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, (targetUnitGameData._unitMaterialCode as number).toString()); + + if (targetUnitMaterialData == null || targetUnitData == null || targetUnitGameData == null) { + throw new Error("Unit Upgrade Error"); + } + + if (targetUnitData.unit_level as number < gameDefaultValue.unitMaxLevel) { + let needUnitAmount = await (await gameUpgradeValueManager.vesselRefineShardCostPocket.GetData((targetUnitData.unit_level as number).toString())).amount as number; + if (needUnitAmount <= (targetUnitMaterialData.item_amount as number)) { + let memberCreditData = await memberPocketKeeper.memberRelicPocket.GetData(memberKey, gameDefaultValue.goldItemCode.toString()); + let needGoldAmount = await (await gameUpgradeValueManager.vesselRefineCreditCostPocket.GetData((targetUnitData.unit_level as number).toString())).amount as number; + if (needGoldAmount <= (memberCreditData.item_amount as number)) { + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, memberCreditData, needGoldAmount); + await memberPocketKeeper.memberRelicPocket.TrimMemberRelicCount(memberKey, targetUnitMaterialData, needUnitAmount); + + (targetUnitData.unit_level as number) += 1; + + await memberPocketKeeper.memberVesselPocket.SetData(memberKey, (targetUnitData.unit_code as number).toString(), targetUnitData); + await memberVesselStore.UpdateData(memberKey, targetUnitData.unit_code as number, targetUnitData); + + return { + _isSuccess: true + } + } + } + } + throw new Error("Unit Upgrade Error"); + }) +} diff --git a/app/duel-app/duel-app.ts b/app/duel-app/duel-app.ts new file mode 100644 index 0000000..0c65e69 --- /dev/null +++ b/app/duel-app/duel-app.ts @@ -0,0 +1,42 @@ +//node module +import os from "os"; + +//Config +import * as config from "../../config/host-spec"; +import { StampHostLabel } from "../../manager/trace-store-keeper/trace-store-spec"; + +//Module +import ReadJsonFile from "../../modules/parse-unit/json-reader"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; +import { BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; +import * as duelRunnerFactory from "./source/module/duel-runner-factory"; + + +process.env['NODE_NO_WARNINGS'] = '1'; + +process.removeAllListeners('warning'); + + + +StampHostLabel(__filename); +StartServer(); +async function StartServer() { + ServerIntro(); + StartPrimaryWorker(); +} + +async function StartPrimaryWorker() { + //os.cpus().length + if (process.env.NODE_ENV == "production") { + duelRunnerFactory.BootDuelRunners(os.cpus().length); + } else { + duelRunnerFactory.BootDuelRunners(2); //coutn n is important + } + + BindFatalProcessGuards(); +} +function ServerIntro(): void { + let packageData = ReadJsonFile(config.packagePath); + console.log(config.serverLogo); + stampLogPrinter.Log(__filename, `${packageData["name"]} version: ${packageData["version"]}`); +} \ No newline at end of file diff --git a/app/duel-app/source/module/duel-runner-factory.ts b/app/duel-app/source/module/duel-runner-factory.ts new file mode 100644 index 0000000..b0d2c2a --- /dev/null +++ b/app/duel-app/source/module/duel-runner-factory.ts @@ -0,0 +1,83 @@ +import * as serverConfig from "../../../../config/duel-host-spec" +import stampLogPrinter, { LogState } from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { Worker, WorkerOptions } from 'worker_threads'; +import { REQDuelRunnerSpawnPacket } from "../../../../data/dto/server2server-dto"; +import { ShouldReforkDuelRunner } from "./duel-runner-refork"; + +const duelRunnerReforkDelayMs = 1000; + + +export function BootDuelRunners(count: number) { + for (let i = 1; i <= count; i++) { + SpawnDuelRunner({ + workerPort: serverConfig.duelRunnerPortBase + i, + workerCode: i + }) + } +} + +function SpawnDuelRunner(duelRunnerSpawnData: REQDuelRunnerSpawnPacket) { + let workerPath: string; + let worker: Worker; + + if (__filename.includes(".ts")) { + workerPath = './app/duel-runner/duel-runner.ts'; + worker = SpawnTsRunner(workerPath, { workerData: duelRunnerSpawnData }); + } else { + workerPath = './dist/app/duel-runner/duel-runner.js'; + worker = SpawnJsRunner(workerPath, { workerData: duelRunnerSpawnData }); + } + + worker.on('message', (data) => { + + }) + worker.on('error', (err) => { + stampLogPrinter.Log(__filename, err.message, LogState.Error); + }) + worker.on('exit', (code) => { + if (!ShouldReforkDuelRunner(code)) { + return; + } + stampLogPrinter.Log(__filename, `Worker stopped with exit code ${code}`, LogState.Error); + setTimeout(() => { + SpawnDuelRunner(duelRunnerSpawnData); + }, duelRunnerReforkDelayMs); + }); +} + + +function SpawnTsRunner(filename: string, workerOptions: WorkerOptions) { + workerOptions.eval = true; + if (!workerOptions.workerData) { + workerOptions.workerData = {}; + } + workerOptions.workerData.__filename = filename; + return new Worker( + ` + const wk = require('worker_threads'); + require('ts-node').register(); + let file = wk.workerData.__filename; + require(file); + `, + workerOptions, + ); +} + + +function SpawnJsRunner(filename: string, workerOptions: WorkerOptions) { + workerOptions.eval = true; + if (!workerOptions.workerData) { + workerOptions.workerData = {}; + } + workerOptions.workerData.__filename = filename; + + return new Worker( + ` + const wk = require('worker_threads'); + let file = wk.workerData.__filename; + require(file); + `, + workerOptions, + ); +} \ No newline at end of file diff --git a/app/duel-app/source/module/duel-runner-refork.ts b/app/duel-app/source/module/duel-runner-refork.ts new file mode 100644 index 0000000..32f1601 --- /dev/null +++ b/app/duel-app/source/module/duel-runner-refork.ts @@ -0,0 +1,3 @@ +export function ShouldReforkDuelRunner(exitCode: number): boolean { + return exitCode !== 0; +} diff --git a/app/duel-runner/duel-runner.ts b/app/duel-runner/duel-runner.ts new file mode 100644 index 0000000..026c11f --- /dev/null +++ b/app/duel-runner/duel-runner.ts @@ -0,0 +1,44 @@ +import * as config from "../../config/host-spec"; +import { workerData, parentPort } from 'worker_threads'; +//import fileNameParser from "../../modules/parse-unit/path-leaf-reader"; +import { REQDuelRunnerSpawnPacket } from '../../data/dto/server2server-dto'; +import duelRunnerBoot from './source/loader/duel-runner-boot'; +// import { InitializeParentPort } from './source/module/parent-pipe-unit/parent-pipe-keeper'; +import stampLogPrinter from '../../modules/trace-unit/stamp-log-printer'; +import { BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; +import * as duelLoadBalancer from "../../modules/duel-load-balancer/duel-load-balancer"; +import * as battlePocketLedger from "./source/manager/battle-pocket-ledger"; +import * as ip from "ip"; +import * as mysql from "../../modules/sql-unit/sql-link"; +import * as http from 'http'; +import * as https from 'https' + +let duelRunnerSpawnData: REQDuelRunnerSpawnPacket = workerData as REQDuelRunnerSpawnPacket; + +export let serverIP: string; + +StartWorker(); +// InitializeParentPort(parentPort); + +export function GetServerIP() { + return serverIP + ":" + duelRunnerSpawnData.workerPort; +} + +async function StartWorker() { + http.get({ 'host': 'api64.ipify.org', 'port': 80, 'path': '/' }, function (resp) { + resp.on('data', async function (ip) { + serverIP = ip; + await duelLoadBalancer.ParkDuelHost(GetServerIP()); + await BootDuelHost(); + }); + }); +} + +async function BootDuelHost() { + mysql.BootSqlPool(); + await battlePocketLedger.HydrateBountyPocket(); + if (serverIP.length == 0) serverIP = ip.address(); + const httpsServer = https.createServer(config.httpsServerOption).listen(duelRunnerSpawnData.workerPort as number, () => { }); + duelRunnerBoot(httpsServer); + BindFatalProcessGuards(); +} \ No newline at end of file diff --git a/app/duel-runner/source/loader/duel-runner-boot.ts b/app/duel-runner/source/loader/duel-runner-boot.ts new file mode 100644 index 0000000..bf77918 --- /dev/null +++ b/app/duel-runner/source/loader/duel-runner-boot.ts @@ -0,0 +1,17 @@ +import * as duelSocketIo from "../module/duel-socket-io-module/duel-socket-io"; +import relayDataRouter from "../router/forward-stream-gateway"; +import endTurnRouter from "../router/close-phase-gateway"; +import endGameRouter from "../router/close-bout-gateway"; +import startShootRouter from "../router/leave-bout-gateway"; +import joinGameRouter from "../router/enter-bout-gateway"; + +import * as http from "http" + +export default (httpServer: http.Server) => { + duelSocketIo.AddRoute(relayDataRouter); + duelSocketIo.AddRoute(endTurnRouter); + duelSocketIo.AddRoute(endGameRouter); + duelSocketIo.AddRoute(startShootRouter); + duelSocketIo.AddRoute(joinGameRouter); + duelSocketIo.LoadSocketIOServer(httpServer); +}; \ No newline at end of file diff --git a/app/duel-runner/source/manager/battle-pocket-ledger.ts b/app/duel-runner/source/manager/battle-pocket-ledger.ts new file mode 100644 index 0000000..0876e8e --- /dev/null +++ b/app/duel-runner/source/manager/battle-pocket-ledger.ts @@ -0,0 +1,44 @@ +import { DuelDefaultBountyRecord, DuelLoseBountyRecord, DuelOutcomeKind, DuelWinBountyRecord, TierEntity, BandBountyRecord } from "../../../../data/entity/nexforge_db_record"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; + +export let duelWinBountyList: DuelWinBountyRecord[]; +export let duelLossBountyList: DuelLoseBountyRecord[]; +export let duelDefaultBountyMap: { [resultType: DuelOutcomeKind | string]: DuelDefaultBountyRecord } = {}; +export let duelBandBountyHash: { [tierCode: number]: BandBountyRecord[] } = {}; +export let duelBandHash: { [tierCode: number]: TierEntity } = {}; + +export let duelWinBountyMaxStanding: number = 0; +export let duelLossBountyMaxStanding: number = 0; + +export async function HydrateBountyPocket() { + duelWinBountyList = await gameDataCachingManager.duelWinBountyListPocket.GetData(); + duelLossBountyList = await gameDataCachingManager.duelLoseBountyListPocket.GetData(); + duelDefaultBountyMap[DuelOutcomeKind.win] = await gameDataCachingManager.duelDefaultBountyPocket.GetData(DuelOutcomeKind.win.toString()); + duelDefaultBountyMap[DuelOutcomeKind.lose] = await gameDataCachingManager.duelDefaultBountyPocket.GetData(DuelOutcomeKind.lose.toString()); + + for (let index = 0; index < duelWinBountyList.length; index++) { + const element = duelWinBountyList[index]; + duelWinBountyMaxStanding += element.getting_score as number; + } + + for (let index = 0; index < duelLossBountyList.length; index++) { + const element = duelLossBountyList[index]; + duelLossBountyMaxStanding += element.getting_score as number; + } + + let duelBandBountyMap = await gameDataCachingManager.tierRewradHashPocketStore.GetAllData(); + let duelBandBountyKeyList = Object.keys(duelBandBountyMap); + for (let index = 0; index < duelBandBountyKeyList.length; index++) { + let key = duelBandBountyKeyList[index]; + let element = JSON.parse(duelBandBountyMap[key]) as BandBountyRecord[]; + duelBandBountyHash[Number(key)] = element; + } + + let duelBandMap = await gameDataCachingManager.tierHashPocketStore.GetAllData(); + let duelBandKeyList = Object.keys(duelBandMap); + for (let index = 0; index < duelBandKeyList.length; index++) { + let key = duelBandKeyList[index]; + let element = JSON.parse(duelBandMap[key]) as TierEntity; + duelBandHash[Number(key)] = element; + } +} \ No newline at end of file diff --git a/app/duel-runner/source/module/duel-arena-unit/bout-state-kind.ts b/app/duel-runner/source/module/duel-arena-unit/bout-state-kind.ts new file mode 100644 index 0000000..5286dbc --- /dev/null +++ b/app/duel-runner/source/module/duel-arena-unit/bout-state-kind.ts @@ -0,0 +1,3 @@ +export enum GameStateType { + StartGame, StartTurn, StartShoot, ClosePhase, CloseBout +} \ No newline at end of file diff --git a/app/duel-runner/source/module/duel-arena-unit/close-bout-verdict.ts b/app/duel-runner/source/module/duel-arena-unit/close-bout-verdict.ts new file mode 100644 index 0000000..d0c828d --- /dev/null +++ b/app/duel-runner/source/module/duel-arena-unit/close-bout-verdict.ts @@ -0,0 +1,70 @@ +import { CombatantSlot } from "../../../../../data/model/play-shape/play-data-shape"; +import { REQCloseBoutData } from "../../../../../data/dto/duel-packet"; + +export type CloseBoutVerdict = + | { + agreed: true; + victorCombatant: CombatantSlot; + lossCombatant: CombatantSlot; + winnerAliveUnitCount: number | undefined; + loserAliveUnitCount: number | undefined; + } + | { + agreed: false; + reason: "missing" | "invalid-slot" | "mismatch"; + }; + +function IsCombatantSlot(value: unknown): value is CombatantSlot { + return value === CombatantSlot.CombatantA || value === CombatantSlot.CombatantB; +} + +function OppositeSlot(slot: CombatantSlot): CombatantSlot { + return slot === CombatantSlot.CombatantA ? CombatantSlot.CombatantB : CombatantSlot.CombatantA; +} + +export function AgreeCloseBoutReports( + reportA: REQCloseBoutData | undefined, + reportB: REQCloseBoutData | undefined +): CloseBoutVerdict { + if (!reportA || !reportB) { + return { agreed: false, reason: "missing" }; + } + if ( + !IsCombatantSlot(reportA.victorCombatant) || + !IsCombatantSlot(reportA.lossCombatant) || + !IsCombatantSlot(reportB.victorCombatant) || + !IsCombatantSlot(reportB.lossCombatant) + ) { + return { agreed: false, reason: "invalid-slot" }; + } + if (reportA.victorCombatant === reportA.lossCombatant || reportB.victorCombatant === reportB.lossCombatant) { + return { agreed: false, reason: "invalid-slot" }; + } + if (reportA.lossCombatant !== OppositeSlot(reportA.victorCombatant) || reportB.lossCombatant !== OppositeSlot(reportB.victorCombatant)) { + return { agreed: false, reason: "invalid-slot" }; + } + if (reportA.victorCombatant !== reportB.victorCombatant || reportA.lossCombatant !== reportB.lossCombatant) { + return { agreed: false, reason: "mismatch" }; + } + if ( + reportA.winnerAliveUnitCount != null && + reportB.winnerAliveUnitCount != null && + reportA.winnerAliveUnitCount !== reportB.winnerAliveUnitCount + ) { + return { agreed: false, reason: "mismatch" }; + } + if ( + reportA.loserAliveUnitCount != null && + reportB.loserAliveUnitCount != null && + reportA.loserAliveUnitCount !== reportB.loserAliveUnitCount + ) { + return { agreed: false, reason: "mismatch" }; + } + return { + agreed: true, + victorCombatant: reportA.victorCombatant, + lossCombatant: reportA.lossCombatant, + winnerAliveUnitCount: reportA.winnerAliveUnitCount ?? reportB.winnerAliveUnitCount, + loserAliveUnitCount: reportA.loserAliveUnitCount ?? reportB.loserAliveUnitCount + }; +} diff --git a/app/duel-runner/source/module/duel-arena-unit/duel-arena.ts b/app/duel-runner/source/module/duel-arena-unit/duel-arena.ts new file mode 100644 index 0000000..6bf04b5 --- /dev/null +++ b/app/duel-runner/source/module/duel-arena-unit/duel-arena.ts @@ -0,0 +1,382 @@ +import { Socket } from 'socket.io' +import { CombatantSlot } from '../../../../../data/model/play-shape/play-data-shape'; +import { DuelArenaShape, DuelKind } from '../../../../../data/model/duel-shape/duel-arena-shape'; +import * as config from "./duel-spec"; +import * as duelPacket from "../../../../../data/dto/duel-packet"; +import stampLogPrinter from '../../../../../modules/trace-unit/stamp-log-printer'; +import * as occupancyKeeper from "../../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from '../../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec'; +import { Timer, SetTimeout } from "../halt-clock/halt-clock"; +import * as duelArenaKeeper from "../../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; +import * as closeBoutBountyArranger from "../../../../../handler/close-bout-grant-bounty-arranger"; +import * as memberPocketKeeper from "../../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { AgreeCloseBoutReports } from "./close-bout-verdict"; +import { CanForwardDuelStream } from "./duel-stream-gate"; + +export default class DuelArena { + private _roomModel: DuelArenaShape | undefined; + private _combatantSocketMap: { [combatantSlot: CombatantSlot | string]: Socket } = {}; + private _enterMemberMap: { [combatantSlot: CombatantSlot | string]: undefined } = {}; + + private _isInitialize: boolean = false; + private _boutSettled: boolean = false; + private _boutAborted: boolean = false; + + private _broadcasterSlot: CombatantSlot = CombatantSlot.CombatantA; + private _observerSlot: CombatantSlot = CombatantSlot.CombatantB; + private _phaseCount: number = 0; + private _closePhaseMap: { [index: CombatantSlot | string]: boolean } = {}; + private _closeBoutMap: { [index: CombatantSlot | string]: duelPacket.REQCloseBoutData } = {}; + + private _breakGameReadyTimer: Timer | undefined = undefined; + private _breakGameJoinTimer: Timer | undefined = undefined; + private _streamerTimeoutDictionary: { [player: CombatantSlot | string]: Timer | undefined } = {} + + + public InitializeRoom(roomModel: DuelArenaShape) { + this._roomModel = roomModel; + this._isInitialize = true; + this._breakGameReadyTimer = SetTimeout(async () => { + await this.AbortBout(); + }, config.gameBreakTiem) + } + + public GetRoomData(): DuelArenaShape { + return this._roomModel as DuelArenaShape; + } + + public MarkCombatantReady(socket: Socket) { + if (Object.keys(this._combatantSocketMap).length == config.maxUserCount || !this._isInitialize) { + return; + } + let memberCombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + if (memberCombatantSlot in this._combatantSocketMap) { + return; + } + this.ParkCombatantSocket(memberCombatantSlot, socket); + if (Object.keys(this._combatantSocketMap).length == config.maxUserCount) { + if (this._breakGameReadyTimer != undefined) { + this._breakGameReadyTimer.pause(); + } + this._breakGameJoinTimer = SetTimeout(async () => { + await this.AbortBout(); + }, config.gameJoinTime) + } + } + + public MarkCombatantEntered(socket: Socket) { + let memberCombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + this._enterMemberMap[memberCombatantSlot] = undefined; + if (Object.keys(this._enterMemberMap).length == config.maxUserCount) { + if (this._breakGameJoinTimer != undefined) { + this._breakGameJoinTimer.pause(); + this.EmitOpenPhase(); + } + } + } + + + public ForwardStream(socket: Socket, data: unknown) { + const senderSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string]; + const observerSocket = this._combatantSocketMap[this._observerSlot]; + if (!CanForwardDuelStream({ + senderIsBroadcaster: senderSlot == this._broadcasterSlot, + observerReady: observerSocket != undefined && !observerSocket.disconnected, + payload: data + })) { + return; + } + observerSocket.emit("stream-data", data); + } + + public ClosePhase(socket: Socket) { + let combatantSlot: CombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + this._closePhaseMap[combatantSlot] = true; + if (this._streamerTimeoutDictionary[combatantSlot] != undefined) (this._streamerTimeoutDictionary[combatantSlot] as Timer).pause(); + if (Object.keys(this._closePhaseMap).length == 2) { + this._closePhaseMap = {}; + this.EmitOpenPhase(); + stampLogPrinter.Log(__filename, "ClosePhase!"); + } + } + + private markSettled(): boolean { + if (this._boutSettled) { + return false; + } + this._boutSettled = true; + return true; + } + + private async AbortBout() { + if (this._boutAborted) { + return; + } + this._boutAborted = true; + this._boutSettled = true; + await this.DropAllCombatantSockets(); + await duelArenaKeeper.DropDuelArena(this._roomModel?.duelArenaId as string); + } + + private EmitOpenPhase() { + this._phaseCount++; + this._broadcasterSlot = (this._phaseCount % 2 == 1) ? CombatantSlot.CombatantA : CombatantSlot.CombatantB; + this._observerSlot = (this._phaseCount % 2 == 0) ? CombatantSlot.CombatantA : CombatantSlot.CombatantB + + let startTurnData: duelPacket.RESStartTurnDTO = { + _playerType: this._broadcasterSlot + } + let startTurnPacket = JSON.stringify(startTurnData); + + if (this._streamerTimeoutDictionary[CombatantSlot.CombatantA] != undefined) { + (this._streamerTimeoutDictionary[CombatantSlot.CombatantA] as Timer).pause(); + } + if (this._streamerTimeoutDictionary[CombatantSlot.CombatantB] != undefined) { + (this._streamerTimeoutDictionary[CombatantSlot.CombatantB] as Timer).pause(); + } + this._streamerTimeoutDictionary = {}; + this._streamerTimeoutDictionary[this._broadcasterSlot] = SetTimeout(() => { + this.CheckPhaseTimeout(this._broadcasterSlot); + }, config.gameBreakTimeForTurn); + + this.EmitToAllCombatants(config.openPhaseEventMSG, startTurnPacket); + } + + public async LeaveBout(socket: Socket) { + if (!this.markSettled()) { + return; + } + let loseCombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + let winCombatantSlot = loseCombatantSlot == CombatantSlot.CombatantA ? CombatantSlot.CombatantB : CombatantSlot.CombatantA; + let victorCombatantKey = this._combatantSocketMap[winCombatantSlot].data?.memberKey as string; + let closeBoutVictorCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutVictorCombatantBounty.giveEntityList = []; + closeBoutVictorCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutVictorCombatantBounty.gameEndType = duelPacket.BoutCloseKind.ExitGame; + closeBoutVictorCombatantBounty.isLeaveBout = true; + if (this._roomModel?.duelKind == DuelKind.FREE) { + let memberBracketData = await memberPocketKeeper.memberBracketPocket.GetData(victorCombatantKey); + closeBoutVictorCombatantBounty.nowTierCode = memberBracketData.user_tier_code as number; + closeBoutVictorCombatantBounty.nowScore = memberBracketData.user_trophy as number; + closeBoutVictorCombatantBounty.grantStanding = 0; + } else if (this._roomModel?.duelKind == DuelKind.RANK) { + const lossCombatantKey = this._combatantSocketMap[loseCombatantSlot]?.data?.memberKey as string | undefined; + const closeBoutLossCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutLossCombatantBounty.giveEntityList = []; + closeBoutLossCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutLossCombatantBounty.gameEndType = duelPacket.BoutCloseKind.ExitGame; + closeBoutLossCombatantBounty.isLeaveBout = true; + const rankedBounty = await closeBoutBountyArranger.GrantRankedCloseBounties( + victorCombatantKey, + lossCombatantKey, + undefined, + this._phaseCount, + closeBoutVictorCombatantBounty, + closeBoutLossCombatantBounty + ); + closeBoutVictorCombatantBounty = rankedBounty.victor; + } + if (this._roomModel?.duelKind != DuelKind.RANK) { + closeBoutVictorCombatantBounty = await closeBoutBountyArranger.GrantVictorBounty(victorCombatantKey, undefined, this._phaseCount, closeBoutVictorCombatantBounty); + } + this.EmitToCombatant(winCombatantSlot, config.closeBoutEventMSG, JSON.stringify(closeBoutVictorCombatantBounty)); + await this.AbortBout(); + } + + public async CloseBoutOnBackground(socket: Socket) { + let loseCombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + await this.CloseBoutOnFault(loseCombatantSlot, duelPacket.BoutCloseKind.BackgroundOut); + } + + private async CloseBoutOnFault(combatantSlot: CombatantSlot, exceptionType: duelPacket.BoutCloseKind) { + if (!this.markSettled()) { + return; + } + let loseCombatantSlot = combatantSlot; + let winCombatantSlot = loseCombatantSlot == CombatantSlot.CombatantA ? CombatantSlot.CombatantB : CombatantSlot.CombatantA; + let victorCombatantKey = this._combatantSocketMap[winCombatantSlot].data?.memberKey as string; + let lossCombatantKey = this._combatantSocketMap[loseCombatantSlot].data?.memberKey as string; + let closeBoutVictorCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutVictorCombatantBounty.giveEntityList = []; + closeBoutVictorCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutVictorCombatantBounty.gameEndType = exceptionType; + + let closeBoutLossCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutLossCombatantBounty.giveEntityList = []; + closeBoutLossCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutLossCombatantBounty.gameEndType = exceptionType; + + let lossMemberBracketData = await memberPocketKeeper.memberBracketPocket.GetData(lossCombatantKey); + let lossMemberCoreData = await memberPocketKeeper.memberCorePocket.GetData(lossCombatantKey); + closeBoutLossCombatantBounty.nowTierCode = lossMemberBracketData.user_tier_code as number; + closeBoutLossCombatantBounty.nowScore = lossMemberBracketData.user_trophy as number; + closeBoutLossCombatantBounty.grantStanding = 0; + closeBoutLossCombatantBounty.nowExp = lossMemberCoreData.user_exp as number; + closeBoutLossCombatantBounty.giveEXP = 0; + closeBoutLossCombatantBounty.nowLevel = lossMemberCoreData.user_level as number; + closeBoutVictorCombatantBounty.isLeaveBout = true; + + if (this._roomModel?.duelKind == DuelKind.FREE) { + let victorMemberBracketData = await memberPocketKeeper.memberBracketPocket.GetData(victorCombatantKey); + closeBoutVictorCombatantBounty.nowTierCode = victorMemberBracketData.user_tier_code as number; + closeBoutVictorCombatantBounty.nowScore = victorMemberBracketData.user_trophy as number; + closeBoutVictorCombatantBounty.grantStanding = 0; + } else if (this._roomModel?.duelKind == DuelKind.RANK) { + const rankedBounty = await closeBoutBountyArranger.GrantRankedCloseBounties( + victorCombatantKey, + lossCombatantKey, + undefined, + this._phaseCount, + closeBoutVictorCombatantBounty, + closeBoutLossCombatantBounty + ); + closeBoutVictorCombatantBounty = rankedBounty.victor; + closeBoutLossCombatantBounty = rankedBounty.loss; + } + + if (this._roomModel?.duelKind != DuelKind.RANK) { + closeBoutVictorCombatantBounty = await closeBoutBountyArranger.GrantVictorBounty(victorCombatantKey, undefined, this._phaseCount, closeBoutVictorCombatantBounty); + } + this.EmitToCombatant(winCombatantSlot, config.closeBoutEventMSG, JSON.stringify(closeBoutVictorCombatantBounty)); + this.EmitToCombatant(loseCombatantSlot, config.closeBoutEventMSG, JSON.stringify(closeBoutLossCombatantBounty)); + await this.AbortBout(); + } + + + public async CloseBout(socket: Socket, endGameData: duelPacket.REQCloseBoutData) { + if (this._boutSettled) { + return; + } + let combatantSlot: CombatantSlot = this._roomModel?.memberSlotMap[socket.data?.memberKey as string] as CombatantSlot; + stampLogPrinter.Log(__filename, combatantSlot + "CloseBout!"); + this._closeBoutMap[combatantSlot] = endGameData; + if (this._streamerTimeoutDictionary[combatantSlot] != undefined) (this._streamerTimeoutDictionary[combatantSlot] as Timer).pause(); + if (Object.keys(this._closeBoutMap).length != 2) { + return; + } + if (!this.markSettled()) { + return; + } + + const verdict = AgreeCloseBoutReports( + this._closeBoutMap[CombatantSlot.CombatantA], + this._closeBoutMap[CombatantSlot.CombatantB] + ); + if (!verdict.agreed) { + stampLogPrinter.Log(__filename, "CloseBout reports did not agree: " + verdict.reason); + await this.VoidBout(duelPacket.BoutCloseKind.ResultMismatch); + return; + } + + let loseCombatantSlot = verdict.lossCombatant; + let winCombatantSlot = verdict.victorCombatant; + let victorCombatantKey = this._combatantSocketMap[winCombatantSlot].data?.memberKey as string; + let lossCombatantKey = this._combatantSocketMap[loseCombatantSlot].data?.memberKey as string; + const settledReport = new duelPacket.REQCloseBoutData(); + settledReport.victorCombatant = verdict.victorCombatant; + settledReport.lossCombatant = verdict.lossCombatant; + settledReport.winnerAliveUnitCount = verdict.winnerAliveUnitCount; + settledReport.loserAliveUnitCount = verdict.loserAliveUnitCount; + let closeBoutVictorCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutVictorCombatantBounty.giveEntityList = []; + closeBoutVictorCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutVictorCombatantBounty.gameEndType = duelPacket.BoutCloseKind.Default; + + let closeBoutLossCombatantBounty: duelPacket.RESCloseBoutDTO = new duelPacket.RESCloseBoutDTO(); + closeBoutLossCombatantBounty.giveEntityList = []; + closeBoutLossCombatantBounty.victorCombatant = winCombatantSlot; + closeBoutLossCombatantBounty.gameEndType = duelPacket.BoutCloseKind.Default; + + if (this._roomModel?.duelKind == DuelKind.FREE) { + let victorMemberCoreData = await memberPocketKeeper.memberBracketPocket.GetData(victorCombatantKey); + closeBoutVictorCombatantBounty.nowTierCode = victorMemberCoreData.user_tier_code as number; + closeBoutVictorCombatantBounty.nowScore = victorMemberCoreData.user_trophy as number; + closeBoutVictorCombatantBounty.grantStanding = 0; + + let lossMemberCoreData = await memberPocketKeeper.memberBracketPocket.GetData(lossCombatantKey); + closeBoutLossCombatantBounty.nowTierCode = lossMemberCoreData.user_tier_code as number; + closeBoutLossCombatantBounty.nowScore = lossMemberCoreData.user_trophy as number; + closeBoutLossCombatantBounty.grantStanding = 0; + } else if (this._roomModel?.duelKind == DuelKind.RANK) { + const rankedBounty = await closeBoutBountyArranger.GrantRankedCloseBounties( + victorCombatantKey, + lossCombatantKey, + settledReport, + this._phaseCount, + closeBoutVictorCombatantBounty, + closeBoutLossCombatantBounty + ); + closeBoutVictorCombatantBounty = rankedBounty.victor; + closeBoutLossCombatantBounty = rankedBounty.loss; + } + if (this._roomModel?.duelKind != DuelKind.RANK) { + closeBoutVictorCombatantBounty = await closeBoutBountyArranger.GrantVictorBounty(victorCombatantKey, settledReport, this._phaseCount, closeBoutVictorCombatantBounty); + closeBoutLossCombatantBounty = await closeBoutBountyArranger.GrantLossBounty(lossCombatantKey, settledReport, this._phaseCount, closeBoutLossCombatantBounty); + } + this.EmitToCombatant(winCombatantSlot, config.closeBoutEventMSG, JSON.stringify(closeBoutVictorCombatantBounty)); + this.EmitToCombatant(loseCombatantSlot, config.closeBoutEventMSG, JSON.stringify(closeBoutLossCombatantBounty)); + await this.AbortBout(); + } + + private async VoidBout(closeKind: duelPacket.BoutCloseKind) { + const slots: CombatantSlot[] = [CombatantSlot.CombatantA, CombatantSlot.CombatantB]; + for (let index = 0; index < slots.length; index++) { + const slot = slots[index]; + const payload = new duelPacket.RESCloseBoutDTO(); + payload.gameEndType = closeKind; + payload.giveEntityList = []; + payload.grantStanding = 0; + payload.giveEXP = 0; + const memberKey = this._combatantSocketMap[slot]?.data?.memberKey as string | undefined; + if (memberKey) { + try { + const bracket = await memberPocketKeeper.memberBracketPocket.GetData(memberKey); + const core = await memberPocketKeeper.memberCorePocket.GetData(memberKey); + payload.nowTierCode = bracket.user_tier_code as number; + payload.nowScore = bracket.user_trophy as number; + payload.nowExp = core.user_exp as number; + payload.nowLevel = core.user_level as number; + } catch { + } + } + this.EmitToCombatant(slot, config.closeBoutEventMSG, JSON.stringify(payload)); + } + await this.AbortBout(); + } + + + private CheckPhaseTimeout(combatantSlot: CombatantSlot) { + this.CloseBoutOnFault(combatantSlot, duelPacket.BoutCloseKind.TurnTimeOut); + } + + private EmitToCombatant(playeType: CombatantSlot, eventMSG: string, data: string) { + try { + this._combatantSocketMap[playeType].emit(eventMSG, data); + } catch { + } + } + + private EmitToAllCombatants(eventMSG: string, data: string) { + this._combatantSocketMap[CombatantSlot.CombatantA].emit(eventMSG, data); + this._combatantSocketMap[CombatantSlot.CombatantB].emit(eventMSG, data); + } + + private ParkCombatantSocket(combatantSlot: CombatantSlot, socket: Socket) { + this._combatantSocketMap[combatantSlot] = socket; + } + + private async DropAllCombatantSockets() { + let combatantList = Object.keys(this._combatantSocketMap); + for (let index = 0; index < combatantList.length; index++) { + let combatantSlot: CombatantSlot = combatantList[index] as CombatantSlot; + if (await occupancyKeeper.HasOccupancy(this._combatantSocketMap[combatantSlot].data?.memberKey as string)) { + await occupancyKeeper.WriteMemberPresence(this._combatantSocketMap[combatantSlot].data?.memberKey as string, MemberPresence.Lobby); + } + } + let combatantSocketList = Object.keys(this._combatantSocketMap); + for (let index = 0; index < combatantSocketList.length; index++) { + let element = this._combatantSocketMap[combatantSocketList[index]]; + if (!element.disconnected) element.disconnect(); + } + } +} diff --git a/app/duel-runner/source/module/duel-arena-unit/duel-spec.ts b/app/duel-runner/source/module/duel-arena-unit/duel-spec.ts new file mode 100644 index 0000000..eec03a3 --- /dev/null +++ b/app/duel-runner/source/module/duel-arena-unit/duel-spec.ts @@ -0,0 +1,10 @@ +export let maxUserCount = 2; + +export const openPhaseEventMSG = "start-turn"; +export const closeBoutEventMSG = "end-game"; +export const stopGameEventMSG = "stop-game"; +export const breakGameEventMSG = "break-game"; + +export const gameBreakTiem: number = 20000; +export const gameJoinTime: number = 20000; +export const gameBreakTimeForTurn: number = 60000; \ No newline at end of file diff --git a/app/duel-runner/source/module/duel-arena-unit/duel-stream-gate.ts b/app/duel-runner/source/module/duel-arena-unit/duel-stream-gate.ts new file mode 100644 index 0000000..febf03a --- /dev/null +++ b/app/duel-runner/source/module/duel-arena-unit/duel-stream-gate.ts @@ -0,0 +1,29 @@ +export const maxDuelStreamBytes = 8192; + +export function MeasureStreamBytes(data: unknown): number { + if (data == null) { + return 0; + } + if (typeof data === "string") { + return Buffer.byteLength(data); + } + if (Buffer.isBuffer(data)) { + return data.length; + } + try { + return Buffer.byteLength(JSON.stringify(data)); + } catch { + return Number.MAX_SAFE_INTEGER; + } +} + +export function CanForwardDuelStream(args: { + senderIsBroadcaster: boolean; + observerReady: boolean; + payload: unknown; +}): boolean { + if (!args.senderIsBroadcaster || !args.observerReady) { + return false; + } + return MeasureStreamBytes(args.payload) <= maxDuelStreamBytes; +} diff --git a/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io-spec.ts b/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io-spec.ts new file mode 100644 index 0000000..0ec88dd --- /dev/null +++ b/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io-spec.ts @@ -0,0 +1,5 @@ +import { Socket } from "socket.io" + +export function SuccessLoadSocketIOMsg(port: number): string { return `Start Socket IO Server ` }; +export function SuccessRegisterRouteMsg(route: string): string { return 'Register Route :' + route }; +export function memberSocketLinkedMSG(socket: Socket): string { return `Success Connect Server MemberKey:${socket.data?.memberKey}` }; \ No newline at end of file diff --git a/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io.ts b/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io.ts new file mode 100644 index 0000000..dde64f5 --- /dev/null +++ b/app/duel-runner/source/module/duel-socket-io-module/duel-socket-io.ts @@ -0,0 +1,99 @@ +import { Server, Socket } from 'socket.io' +import http from "http"; +import stampLogPrinter, { LogState } from '../../../../../modules/trace-unit/stamp-log-printer'; +import * as config from "./duel-socket-io-spec"; +import * as env from "../../../../../config/runtime-spec"; +import occupancyTicketAuthMD from "../../../../../middleware/socket-occupancy-gate"; +//import fileNameParser from '../../../../../modules/parse-unit/path-leaf-reader'; +import { GetDuelArena, GenerateDuelArena, ExistDuelArena } from '../../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper'; +import * as duelJwtUnit from "../../../../../modules/duel-jwt-unit/duel-jwt-unit"; +import { DuelArenaShape } from '../../../../../data/model/duel-shape/duel-arena-shape'; +import * as occupancyKeeper from "../../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; + + + +export class Route { + public route: string = ""; + public listener: (socket: Socket, data: string) => void | void = () => { }; + public constructor(route: string, listener: (socket: Socket, data: string) => void) { + this.route = route; + this.listener = listener; + } +} + +const routeList: Array = new Array(); + +const io = new Server({ + maxHttpBufferSize: 1e5, + pingTimeout: env.duelPingTimeout, + pingInterval: env.duelPingInterval, +}) + +async function HandleSocketConnect(socket: Socket) { + stampLogPrinter.Log(__filename, config.memberSocketLinkedMSG(socket)); + let result: any = duelJwtUnit.Verify(socket.handshake.auth.duelTicket as string); + + if (result in duelJwtUnit.TOKEN_ERROR) { + stampLogPrinter.Log(__filename, "jwt error"); + socket.disconnect(); + return; + } + let payload: duelJwtUnit.JWTPayload = result as duelJwtUnit.JWTPayload; + let memberPresenceData = await occupancyKeeper.ReadMemberPresence(socket.data?.memberKey as string); + if (memberPresenceData.roomUUID != payload.roomUUID) { + stampLogPrinter.Log(__filename, "room id error"); + socket.disconnect(); + return; + } + GenerateDuelArena(payload.roomUUID as string, payload.roomData as DuelArenaShape); + + if (ExistDuelArena(payload.roomUUID as string)) { + let duelArenaData; + duelArenaData = GetDuelArena(payload.roomUUID as string); + duelArenaData.MarkCombatantReady(socket); + } else { + stampLogPrinter.Log(__filename, "room id error"); + socket.disconnect(); + return; + } + + socket.on("disconnect", async (reason) => { + //방나감 + let memberPresenceData = await occupancyKeeper.ReadMemberPresence(socket.data?.memberKey as string); + if (memberPresenceData.roomUUID != payload.roomUUID) { + return; + } + if (ExistDuelArena(payload.roomUUID as string)) { + let duelArenaData = GetDuelArena(payload.roomUUID as string); + await duelArenaData.LeaveBout(socket); + } + }); + + + for (let index = 0; index < routeList.length; index++) { + const element = routeList[index]; + socket.on(element.route, (data: string) => { + element.listener(socket, data); + }); + } +} + +export function AddRoute(route: Route) { + stampLogPrinter.Log(__filename, config.SuccessRegisterRouteMsg(route.route)); + routeList.push(route); +} + +export function BroadCast(ev: string, data: string) { + io.emit(ev, data); +} + +export function EmitBySocketID(socketID: string, ev: string, data: string) { + io.to(socketID).emit(ev, data); +} + +export function LoadSocketIOServer(httpsServer: http.Server) { + io.on('error', err => stampLogPrinter.Log(__filename, err, LogState.Error)); + io.on('connection', HandleSocketConnect) + io.use(occupancyTicketAuthMD); + io.listen(httpsServer); +} diff --git a/app/duel-runner/source/module/halt-clock/halt-clock.ts b/app/duel-runner/source/module/halt-clock/halt-clock.ts new file mode 100644 index 0000000..b2b1688 --- /dev/null +++ b/app/duel-runner/source/module/halt-clock/halt-clock.ts @@ -0,0 +1,25 @@ + +export function SetTimeout(callback: () => void, delay: number): Timer { + let timerId: NodeJS.Timeout | undefined; + let start: number; + let remaining: number = delay; + + let timer: Timer = { + pause: function () { + if (timerId != undefined) clearTimeout(timerId); + timerId = undefined; + remaining -= Date.now() - start; + }, + resume: function () { + start = Date.now(); + timerId = setTimeout(callback, remaining); + }, + } + timer.resume(); + return timer; +}; + +export interface Timer { + pause: () => void; + resume: () => void; +} \ No newline at end of file diff --git a/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-arranger.ts b/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-arranger.ts new file mode 100644 index 0000000..a8747c2 --- /dev/null +++ b/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-arranger.ts @@ -0,0 +1,12 @@ +// import { REQDuelArenaSpawnPacket, WorkerMSGDTO } from "../../../../../data/dto/server2server-dto"; +// import { GadgetRecord } from "../../../../../data/entity/nexforge_db_record"; +// import { DuelArenaShape } from "../../../../../data/model/duel-shape/duel-arena-shape"; +// import * as duelArenaKeeper from "../duel-arena-unit/duel-arena"; + +// export default (data: any) => { +// let workerMSGData = data as WorkerMSGDTO; +// if (workerMSGData.event == "GenerateRoom") { +// let reqDTO = workerMSGData.data as REQDuelArenaSpawnPacket; +// duelArenaKeeper.GenerateDuelArena(reqDTO.roomUUID as string, reqDTO.duelArenaData as DuelArenaShape); +// } +// } \ No newline at end of file diff --git a/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-keeper.ts b/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-keeper.ts new file mode 100644 index 0000000..52a3ab4 --- /dev/null +++ b/app/duel-runner/source/module/parent-pipe-unit/parent-pipe-keeper.ts @@ -0,0 +1,15 @@ +// import { parentPort, MessagePort } from "worker_threads"; +// import parentPortMSGController from "./parent-pipe-arranger"; + +// let _parentPort: MessagePort; + +// export function InitializeParentPort(parentPort: MessagePort | null) { +// _parentPort = parentPort as MessagePort; +// _parentPort.addListener("message", parentPortMSGController); +// } + +// export function PostMessage(data: any) { +// _parentPort.postMessage(data); +// } + + diff --git a/app/duel-runner/source/router/close-bout-gateway.ts b/app/duel-runner/source/router/close-bout-gateway.ts new file mode 100644 index 0000000..d1c8746 --- /dev/null +++ b/app/duel-runner/source/router/close-bout-gateway.ts @@ -0,0 +1,4 @@ +import * as socketIO from "../module/duel-socket-io-module/duel-socket-io"; +import closeBoutUsecase from "../service/close-bout-usecase"; + +export default new socketIO.Route("end-game", closeBoutUsecase); diff --git a/app/duel-runner/source/router/close-phase-gateway.ts b/app/duel-runner/source/router/close-phase-gateway.ts new file mode 100644 index 0000000..2b16391 --- /dev/null +++ b/app/duel-runner/source/router/close-phase-gateway.ts @@ -0,0 +1,5 @@ +import * as socketIO from "../module/duel-socket-io-module/duel-socket-io"; +import closePhaseUsecase from "../service/close-phase-usecase"; + + +export default new socketIO.Route("end-turn", closePhaseUsecase); diff --git a/app/duel-runner/source/router/enter-bout-gateway.ts b/app/duel-runner/source/router/enter-bout-gateway.ts new file mode 100644 index 0000000..9a6f6ee --- /dev/null +++ b/app/duel-runner/source/router/enter-bout-gateway.ts @@ -0,0 +1,4 @@ +import * as socketIO from "../module/duel-socket-io-module/duel-socket-io"; +import enterBoutUsecase from "../service/enter-bout-usecase"; + +export default new socketIO.Route("join-game", enterBoutUsecase); diff --git a/app/duel-runner/source/router/forward-stream-gateway.ts b/app/duel-runner/source/router/forward-stream-gateway.ts new file mode 100644 index 0000000..d20a39a --- /dev/null +++ b/app/duel-runner/source/router/forward-stream-gateway.ts @@ -0,0 +1,4 @@ +import * as socketIO from "../module/duel-socket-io-module/duel-socket-io"; +import forwardStreamUsecase from "../service/forward-stream-usecase"; + +export default new socketIO.Route("relay", forwardStreamUsecase); \ No newline at end of file diff --git a/app/duel-runner/source/router/leave-bout-gateway.ts b/app/duel-runner/source/router/leave-bout-gateway.ts new file mode 100644 index 0000000..f14cffb --- /dev/null +++ b/app/duel-runner/source/router/leave-bout-gateway.ts @@ -0,0 +1,4 @@ +import * as socketIO from "../module/duel-socket-io-module/duel-socket-io"; +import leaveBoutUsecase from "../service/leave-bout-usecase"; + +export default new socketIO.Route("exit-game", leaveBoutUsecase); diff --git a/app/duel-runner/source/service/close-bout-usecase.ts b/app/duel-runner/source/service/close-bout-usecase.ts new file mode 100644 index 0000000..53be734 --- /dev/null +++ b/app/duel-runner/source/service/close-bout-usecase.ts @@ -0,0 +1,13 @@ +import { Socket } from "socket.io"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { GetDuelArena, ExistDuelArena } from "../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; +import { REQCloseBoutData } from "../../../../data/dto/duel-packet"; + + + +export default (socket: Socket, data: any) => { + if (!ExistDuelArena(socket.handshake.auth.roomID as string)) return; + let duelArenaData = GetDuelArena(socket.handshake.auth.roomID as string); + duelArenaData.CloseBout(socket, JSON.parse(data) as REQCloseBoutData); +} \ No newline at end of file diff --git a/app/duel-runner/source/service/close-phase-usecase.ts b/app/duel-runner/source/service/close-phase-usecase.ts new file mode 100644 index 0000000..21973aa --- /dev/null +++ b/app/duel-runner/source/service/close-phase-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { ExistDuelArena, GetDuelArena } from "../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; + + + +export default (socket: Socket, data: any) => { + if (!ExistDuelArena(socket.handshake.auth.roomID as string)) return; + let duelArenaData = GetDuelArena(socket.handshake.auth.roomID as string); + duelArenaData.ClosePhase(socket); +} \ No newline at end of file diff --git a/app/duel-runner/source/service/enter-bout-usecase.ts b/app/duel-runner/source/service/enter-bout-usecase.ts new file mode 100644 index 0000000..8f17116 --- /dev/null +++ b/app/duel-runner/source/service/enter-bout-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { ExistDuelArena, GetDuelArena } from "../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; + + + +export default (socket: Socket, data: any) => { + if (!ExistDuelArena(socket.handshake.auth.roomID as string)) return; + let duelArenaData = GetDuelArena(socket.handshake.auth.roomID as string); + duelArenaData.MarkCombatantEntered(socket); +} \ No newline at end of file diff --git a/app/duel-runner/source/service/forward-stream-usecase.ts b/app/duel-runner/source/service/forward-stream-usecase.ts new file mode 100644 index 0000000..c429ea4 --- /dev/null +++ b/app/duel-runner/source/service/forward-stream-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { ExistDuelArena, GetDuelArena } from "../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; + + + +export default (socket: Socket, data: any) => { + if (!ExistDuelArena(socket.handshake.auth.roomID as string)) return; + let duelArenaData = GetDuelArena(socket.handshake.auth.roomID as string); + duelArenaData.ForwardStream(socket, data); +} \ No newline at end of file diff --git a/app/duel-runner/source/service/leave-bout-usecase.ts b/app/duel-runner/source/service/leave-bout-usecase.ts new file mode 100644 index 0000000..52477dd --- /dev/null +++ b/app/duel-runner/source/service/leave-bout-usecase.ts @@ -0,0 +1,13 @@ +import { Socket } from "socket.io"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import { ExistDuelArena, GetDuelArena } from "../../../../manager/pocket-keeper/duel-arena-keeper/duel-arena-keeper"; +import { BoutCloseKind } from "../../../../data/dto/duel-packet"; + + + +export default (socket: Socket, data: any) => { + if (!ExistDuelArena(socket.handshake.auth.roomID as string)) return; + let duelArenaData = GetDuelArena(socket.handshake.auth.roomID as string); + duelArenaData.CloseBoutOnBackground(socket); +} \ No newline at end of file diff --git a/app/nexus-app/nexus-app.ts b/app/nexus-app/nexus-app.ts new file mode 100644 index 0000000..29b11f0 --- /dev/null +++ b/app/nexus-app/nexus-app.ts @@ -0,0 +1,56 @@ +import * as config from "../../config/host-spec"; +import { StampHostLabel } from "../../manager/trace-store-keeper/trace-store-spec"; +import express from "express"; +import https from "https"; +import ReadJsonFile from "../../modules/parse-unit/json-reader"; +import * as sqlUnit from "../../modules/sql-unit/sql-link"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; +import * as pocketLink from "../../modules/pocket-link/pocket-link"; +import MonoAppLoader from "./source/loader/nexus-hub-boot"; +import * as OccupancyClock from "./source/routine/occupancy-clock"; +import * as bracketPairingClock from "./source/routine/bracket-pairing-clock"; +import * as casualPairingClock from "./source/routine/casual-pairing-clock"; +import { BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; +import { FlushPocketIfAllowed } from "../../modules/pocket-link/pocket-boot-flush"; + +process.env["NODE_NO_WARNINGS"] = "1"; + +class App { + public application: express.Application; + constructor() { + this.application = express(); + } +} + +StartServer(); +StampHostLabel(__filename); + +export async function StartServer() { + await FlushPocketIfAllowed(pocketLink.LanePocketClient(), "lane"); + ServerIntro(); + sqlUnit.BootSqlPool(); + casualPairingClock.StartRoutine(); + bracketPairingClock.StartRoutine(); + OccupancyClock.StartRoutine(); + + const expressApp = new App().application; + + let httpsServer = https.createServer(config.httpsServerOption, expressApp).listen(config.nexusHostPort, () => { + stampLogPrinter.Log(__filename, config.ServerStartHttpsMSG(config.nexusHostPort)); + }); + + MonoAppLoader(httpsServer); + + BindFatalProcessGuards(); + + process.on("exit", function () { + OccupancyClock.StopRoutine(); + stampLogPrinter.Log(__filename, config.exitServerMessage); + }); +} + +function ServerIntro(): void { + console.log(config.serverLogo); + let packageData = ReadJsonFile(config.packagePath); + stampLogPrinter.Log(__filename, `${packageData["name"]} version: ${packageData["version"]}`); +} diff --git a/app/nexus-app/source/handler/member-battle-integrity-arranger.ts b/app/nexus-app/source/handler/member-battle-integrity-arranger.ts new file mode 100644 index 0000000..1344ce1 --- /dev/null +++ b/app/nexus-app/source/handler/member-battle-integrity-arranger.ts @@ -0,0 +1,46 @@ +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper" +import { BattleLoadoutLine, InspectBattleLoadout } from "./member-battle-integrity-policy"; + +export default (memberKey: string) => { + return new Promise(async (resolve) => { + let memberVesselLineMap = await memberPocketKeeper.memberVesselLinePocket.GetAllData(memberKey); + let memberVesselLineKeyList = Object.keys(memberVesselLineMap); + let lineSlots: BattleLoadoutLine[] = []; + let gadgetsByVesselCode: { [vesselCode: string]: string[] } = {}; + + for (let lineIndex = 0; lineIndex < memberVesselLineKeyList.length; lineIndex++) { + const lineKey = memberVesselLineKeyList[lineIndex]; + const memberVesselLine = memberVesselLineMap[lineKey]; + lineSlots.push(memberVesselLine); + const vesselCode = memberVesselLine._unitCode as number; + if (!vesselCode) { + continue; + } + gadgetsByVesselCode[String(vesselCode)] = (await memberPocketKeeper.memberVesselGadgetPocket.GetData(memberKey, String(vesselCode))).vesselMountedGadgetList; + } + + const inspection = InspectBattleLoadout({ lineSlots, gadgetsByVesselCode }); + if (!inspection.ok) { + if (inspection.reset == "gadget") { + memberPocketKeeper.memberVesselGadgetPocket.InitByGameData(memberKey); + } else { + memberPocketKeeper.memberVesselLinePocket.InitData(memberKey); + } + return resolve(false); + } + + for (let vesselIndex = 0; vesselIndex < inspection.vesselCodes.length; vesselIndex++) { + if (!await memberPocketKeeper.memberVesselPocket.ExistData(memberKey, inspection.vesselCodes[vesselIndex].toString())) { + memberPocketKeeper.memberVesselLinePocket.InitData(memberKey); + return resolve(false); + } + } + for (let gadgetIndex = 0; gadgetIndex < inspection.gadgetIds.length; gadgetIndex++) { + if (!await memberPocketKeeper.memberGadgetPocket.ExistData(memberKey, inspection.gadgetIds[gadgetIndex])) { + memberPocketKeeper.memberVesselGadgetPocket.InitByGameData(memberKey); + return resolve(false); + } + } + return resolve(true); + }) +} \ No newline at end of file diff --git a/app/nexus-app/source/handler/member-battle-integrity-policy.ts b/app/nexus-app/source/handler/member-battle-integrity-policy.ts new file mode 100644 index 0000000..e81a025 --- /dev/null +++ b/app/nexus-app/source/handler/member-battle-integrity-policy.ts @@ -0,0 +1,56 @@ +export type BattleLoadoutLine = { + _unitCode?: number; +}; + +export type BattleLoadoutInspection = + | { + ok: true; + vesselCodes: number[]; + gadgetIds: string[]; + } + | { + ok: false; + reason: "slot-count" | "duplicate-vessel" | "duplicate-gadget"; + reset: "line" | "gadget"; + }; + +export function InspectBattleLoadout(args: { + lineSlots: BattleLoadoutLine[]; + gadgetsByVesselCode: { [vesselCode: string]: string[] }; +}): BattleLoadoutInspection { + if (args.lineSlots.length != 3) { + return { ok: false, reason: "slot-count", reset: "line" }; + } + + const vesselCodes: number[] = []; + const seenVesselCodes: { [vesselCode: number]: true } = {}; + const gadgetIds: string[] = []; + const seenGadgetIds: { [gadgetId: string]: true } = {}; + + for (let lineIndex = 0; lineIndex < args.lineSlots.length; lineIndex++) { + const vesselCode = args.lineSlots[lineIndex]._unitCode as number; + if (!vesselCode) { + continue; + } + if (vesselCode in seenVesselCodes) { + return { ok: false, reason: "duplicate-vessel", reset: "line" }; + } + seenVesselCodes[vesselCode] = true; + vesselCodes.push(vesselCode); + + const mountedGadgets = args.gadgetsByVesselCode[String(vesselCode)] || []; + for (let gadgetIndex = 0; gadgetIndex < mountedGadgets.length; gadgetIndex++) { + const gadgetId = mountedGadgets[gadgetIndex]; + if (!gadgetId) { + continue; + } + if (gadgetId in seenGadgetIds) { + return { ok: false, reason: "duplicate-gadget", reset: "gadget" }; + } + seenGadgetIds[gadgetId] = true; + gadgetIds.push(gadgetId); + } + } + + return { ok: true, vesselCodes, gadgetIds }; +} diff --git a/app/nexus-app/source/handler/member-battle-payload-arranger.ts b/app/nexus-app/source/handler/member-battle-payload-arranger.ts new file mode 100644 index 0000000..f344a28 --- /dev/null +++ b/app/nexus-app/source/handler/member-battle-payload-arranger.ts @@ -0,0 +1,81 @@ +import CombatantShape from "../../../../data/model/member-shape/CombatantShape" +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import * as gameDataCachingManager from "../../../../manager/pocket-keeper/master-pocket-keeper/master-pocket-keeper"; +import * as gameConfig from "../../../../config/play-baseline-spec"; +import { RESAssembleMemberPlayPacket } from "../../../../data/dto/server2server-dto"; +import checkingMemberBattleIntegrity from "./member-battle-integrity-arranger"; + +export default async (memberKey: string): Promise => { + return new Promise(async (resolve, reject) => { + let result = await checkingMemberBattleIntegrity(memberKey); + if (result) { + //Checking All Parts + let combatantData = new CombatantShape(); + let memberCoreData = await memberPocketKeeper.memberCorePocket.GetData(memberKey); + let memberAliasData = await memberPocketKeeper.memberAliasPocket.GetData(memberKey); + let memberBracketData = await memberPocketKeeper.memberBracketPocket.GetData(memberKey); + + combatantData._gameData = { + _userLevel: memberCoreData.user_level, + _userExp: memberCoreData.user_exp, + _userCharacterCode: memberCoreData.user_character_code + } + combatantData._rankGameData = { + _userTrophy: memberBracketData.user_trophy, + _userWinningStreakCount: memberBracketData.user_winning_streak_count, + _useTierCode: memberBracketData.user_tier_code + } + combatantData._profileData = { + _userCountry: memberAliasData.user_country, + _userProfileName: memberAliasData.user_profile_name + } + + combatantData._partsDataDictionary = {}; + combatantData._unitDataDictionary = {}; + combatantData._unitOrderDataDictionary = {}; + + try { + for (let orderIndex = 1; orderIndex <= gameConfig.unitSlotCount; orderIndex++) { + let memberVesselLineList = await memberPocketKeeper.memberVesselLinePocket.GetData(memberKey, orderIndex.toString()); + if (memberVesselLineList._unitCode as number == 0) { + combatantData._unitOrderDataDictionary[memberVesselLineList._orderCode as number] = memberVesselLineList; + } else { + let memberVesselData = await memberPocketKeeper.memberVesselPocket.GetData(memberKey, (memberVesselLineList._unitCode as number).toString()); + let memberGadgetData = await memberPocketKeeper.memberVesselGadgetPocket.GetData(memberKey, (memberVesselLineList._unitCode as number).toString()); + for (let j = 0; j < memberGadgetData.vesselMountedGadgetList.length; j++) { + let partsUUID = memberGadgetData.vesselMountedGadgetList[j]; + let memberVesselGadgetData = await memberPocketKeeper.memberGadgetPocket.GetData(memberKey, partsUUID); + + combatantData._partsDataDictionary[partsUUID] = { + _partsCode: memberVesselGadgetData.parts_code, + _partsLevel: memberVesselGadgetData.parts_level, + _partsUUID: memberVesselGadgetData.parts_uuid + }; + } + combatantData._unitDataDictionary[memberVesselData.unit_code as number] = { + _unitCode: memberVesselData.unit_code, + _unitLevel: memberVesselData.unit_level, + _unitUsingPartsCodeList: memberGadgetData.vesselMountedGadgetList, + _unitClass: memberVesselData.unit_class + }; + combatantData._unitOrderDataDictionary[memberVesselLineList._orderCode as number] = memberVesselLineList; + } + } + } catch (err: NodeJS.ErrnoException | any) { + return resolve({ + combatantShape: undefined, + isSuccessGenerate: false + }); + } + return resolve({ + combatantShape: combatantData, + isSuccessGenerate: true + }); + } else { + return resolve({ + combatantShape: undefined, + isSuccessGenerate: false + }); + } + }) +} diff --git a/app/nexus-app/source/handler/occupancy-expired-arranger.ts b/app/nexus-app/source/handler/occupancy-expired-arranger.ts new file mode 100644 index 0000000..26d8906 --- /dev/null +++ b/app/nexus-app/source/handler/occupancy-expired-arranger.ts @@ -0,0 +1,10 @@ +import { DisconnectBySocketID, EmitBySocketID } from "../module/socket-io-module/socket-io-app"; +import { MemberSocketHash } from "../module/member-socket-unit/member-socket-keeper"; + + +export async function HandleOccupancyLapsed(memberKey: string) { + if (await MemberSocketHash.ExistData(memberKey)) { + let socketID = await MemberSocketHash.GetData(memberKey); + DisconnectBySocketID(socketID.socketID); + } +} \ No newline at end of file diff --git a/app/nexus-app/source/handler/pairing-arranger.ts b/app/nexus-app/source/handler/pairing-arranger.ts new file mode 100644 index 0000000..9ca37ba --- /dev/null +++ b/app/nexus-app/source/handler/pairing-arranger.ts @@ -0,0 +1,98 @@ +import * as socketIO from "../module/socket-io-module/socket-io-app"; +import CombatantShape from "../../../../data/model/member-shape/CombatantShape"; +import { PlayKind, CombatantSlot } from "../../../../data/model/play-shape/play-data-shape"; +import { PickDuelStageCode } from "../../../../manager/pocket-keeper/duel-stage-keeper/duel-stage-keeper"; +import { DuelPlayShape, DuelArenaShape, DuelKind } from "../../../../data/model/duel-shape/duel-arena-shape"; +import { EnterDuelHost } from "../../../../modules/duel-load-balancer/duel-load-balancer"; +import { RESDuelBoutPacket } from "../../../../data/dto/nexus-hub-packet"; +import memberBattlePayloadArranger from "./member-battle-payload-arranger"; +import * as memberPocketKeeper from "../../../../manager/pocket-keeper/member-pocket-keeper/member-pocket-keeper"; +import { MintId } from "../../../../modules/id-mint-unit/id-mint"; +import * as duelTicketModule from "../../../../modules/duel-ticket-unit/duel-ticket-unit"; +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec"; +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; + +const pairingReadyEventMSG = "success-matching"; +const arenaReadyEventMSG = "success-generate-game"; +const arenaFailEventMSG = "fail-generate-game"; + +export default async (combatantKeyList: string[], combatantSocketIdList: string[], duelKind: DuelKind) => { + let duelArenaData: DuelPlayShape = new DuelPlayShape(); + duelArenaData.duelArenaData.duelKind = duelKind; + + DispatchPairingReadyMember(pairingReadyEventMSG); + + function DispatchPairingReadyMember(event: string, data: string = "") { + for (let i = 0; i < combatantSocketIdList.length; i++) { + socketIO.EmitBySocketID(combatantSocketIdList[i], event, data); + } + } + + function GetCombatantSlot(combatantIndex: number): CombatantSlot { + return (combatantIndex == 0) ? CombatantSlot.CombatantA : CombatantSlot.CombatantB; + } + + async function AddCombatantShape(combatantShape: CombatantShape, combatantIndex: number) { + let memberCombatantSlot = GetCombatantSlot(combatantIndex); + (duelArenaData as DuelPlayShape).combatantShapeMap[memberCombatantSlot] = combatantShape; + (duelArenaData as DuelPlayShape).duelArenaData.memberSlotMap[combatantKeyList[combatantIndex]] = memberCombatantSlot; + if (Object.keys(duelArenaData.combatantShapeMap).length >= 2) { + await SpawnDuelArena(duelArenaData); + } + } + + for (let i = 0; i < combatantKeyList.length; i++) { + //Closure + let index = i; + let memberBattlePayload = await memberBattlePayloadArranger(combatantKeyList[index]) + if (memberBattlePayload.isSuccessGenerate) { + await AddCombatantShape(memberBattlePayload.combatantShape as CombatantShape, index); + } else { + DispatchPairingReadyMember(arenaFailEventMSG); + return; + } + } + + // spawn duel arena + async function SpawnDuelArena(duelPlayShape: DuelPlayShape) { + let duelHostIp: string; + try { + duelHostIp = await EnterDuelHost(); + } catch (err) { + stampLogPrinter.Log(__filename, "Failed to park duel arena: " + (err as Error).message); + DispatchPairingReadyMember(arenaFailEventMSG); + return; + } + let duelArenaId: string = MintId(); + + duelPlayShape.duelArenaData.mapCode = await PickDuelStageCode(); + duelPlayShape.duelArenaData.duelArenaId = duelArenaId; + + let duelTicket = await duelTicketModule.IssueDuelTicket({ + roomUUID: duelArenaId, + roomData: duelPlayShape.duelArenaData, + serverIP: duelHostIp + }) + + for (let index = 0; index < combatantKeyList.length; index++) { + let duelBoutData: RESDuelBoutPacket = { + _inGameData: { + _gameType: PlayKind.SharedBout, + _mapCode: duelPlayShape.duelArenaData.mapCode, + _userPlayer: GetCombatantSlot(index), + _duelKind: duelPlayShape.duelArenaData.duelKind as DuelKind + }, + _multiGameData: { + _roomID: duelArenaId, + _serverAddress: duelHostIp, + _duelTicket: duelTicket + }, + _combatantShapeMap: duelPlayShape.combatantShapeMap, + } + await occupancyKeeper.WriteMemberPresence(combatantKeyList[index], MemberPresence.InGame, duelArenaId); + socketIO.EmitBySocketID(combatantSocketIdList[index], arenaReadyEventMSG, JSON.stringify(duelBoutData)); + } + } +} + diff --git a/app/nexus-app/source/loader/nexus-hub-boot.ts b/app/nexus-app/source/loader/nexus-hub-boot.ts new file mode 100644 index 0000000..84e935d --- /dev/null +++ b/app/nexus-app/source/loader/nexus-hub-boot.ts @@ -0,0 +1,14 @@ +import * as monoSocketIO from "../module/socket-io-module/socket-io-app"; +import enterCasualPairingGateway from "../router/enter-casual-pairing-gateway"; +import leaveCasualPairingGateway from "../router/leave-casual-pairing-gateway"; +import enterBracketPairingGateway from "../router/enter-bracket-pairing-gateway"; +import leaveBracketPairingGateway from "../router/leave-bracket-pairing-gateway"; +import http from "http"; + +export default (httpsServer: http.Server) => { + monoSocketIO.AddRoute(enterCasualPairingGateway); + monoSocketIO.AddRoute(leaveCasualPairingGateway); + monoSocketIO.AddRoute(enterBracketPairingGateway); + monoSocketIO.AddRoute(leaveBracketPairingGateway); + monoSocketIO.LoadSocketIOServer(httpsServer); +}; \ No newline at end of file diff --git a/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-keeper.ts b/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-keeper.ts new file mode 100644 index 0000000..7d8bb63 --- /dev/null +++ b/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-keeper.ts @@ -0,0 +1,28 @@ +// import axios, { Axios, AxiosRequestHeaders, AxiosResponse, AxiosRequestConfig } from "axios"; +// import * as hubHostRegisterSpec from "./duel-arena-spawn-spec"; +// import Print from "../../../../../modules/trace-unit/stamp-log-printer" +// //import fileNameParser from "../../../../../modules/parse-unit/path-leaf-reader"; +// import { ErrorType } from "../../../../../modules/trace-unit/trace-unit-spec/fault-dispatch-spec"; +// import CombatantShape from "../../../../../data/model/member-shape/CombatantShape"; +// import { RESDuelArenaPickedPacket } from "../../../../../data/dto/server2server-dto"; +// import { DuelArenaShape } from "../../../../../data/model/duel-shape/duel-arena-shape"; + +// + +// export async function GenerateDuelArena(duelHostIp: string, duelArenaData: DuelArenaShape): Promise { +// return new Promise(async (resolve, rejects) => { +// Print.Log(__filename, duelHostIp + hubHostRegisterSpec.serviceAddress); +// try { +// let res: AxiosResponse = await axios.post(duelHostIp + hubHostRegisterSpec.serviceAddress, duelArenaData, hubHostRegisterSpec.config); +// if (res.status == 200) { +// resolve(res.data); +// Print.Log(__filename, hubHostRegisterSpec.arenaSpawnedMSG); +// } else { +// Print.Error(__filename, ErrorType.ExceptionHubOccupancyOpen); +// } +// } catch (error: NodeJS.ErrnoException | any) { +// Print.Error(__filename, ErrorType.ExceptionHubOccupancyOpen, error.message); +// } +// }); +// } + diff --git a/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-spec.ts b/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-spec.ts new file mode 100644 index 0000000..24f9c47 --- /dev/null +++ b/app/nexus-app/source/module/duel-arena-spawn-keeper/duel-arena-spawn-spec.ts @@ -0,0 +1,17 @@ +// import axios, { Axios, AxiosRequestHeaders, AxiosResponse, AxiosRequestConfig } from "axios"; +// import * as axiosConfig from "../../../../../config/axios-config/axios-config"; + +// import * as licenseAuthConfig from "../../../../../middleware/gate-spec/license-auth-md-config" +// import * as env from "../../../../../config/runtime-spec/runtime-spec"; + +// const header: AxiosRequestHeaders = { +// [licenseAuthConfig.serverLicenseHeaderName]: env.serverLicenseCode, +// } + +// export const config: AxiosRequestConfig = { +// httpsAgent: axiosConfig.httpsAgent, +// headers: header +// }; + +// export const serviceAddress: string = "/server/GenerateDuelArena"; +// export const arenaSpawnedMSG: string = "Success spawn duel arena"; \ No newline at end of file diff --git a/app/nexus-app/source/module/member-socket-unit/member-socket-keeper.ts b/app/nexus-app/source/module/member-socket-unit/member-socket-keeper.ts new file mode 100644 index 0000000..a132c3a --- /dev/null +++ b/app/nexus-app/source/module/member-socket-unit/member-socket-keeper.ts @@ -0,0 +1,4 @@ +import { Socket } from "socket.io" +import * as config from "./member-socket-spec"; + +export const MemberSocketHash = new config.MemberSocketHash(); \ No newline at end of file diff --git a/app/nexus-app/source/module/member-socket-unit/member-socket-spec.ts b/app/nexus-app/source/module/member-socket-unit/member-socket-spec.ts new file mode 100644 index 0000000..d9548db --- /dev/null +++ b/app/nexus-app/source/module/member-socket-unit/member-socket-spec.ts @@ -0,0 +1,15 @@ +import HashPocketStore from "../../../../../manager/pocket-shape-base/HashPocketStore"; + +export class MemberSocketShape { + public socketID: string; + + constructor(socketID: string) { + this.socketID = socketID; + } +} + +export class MemberSocketHash extends HashPocketStore { + constructor() { + super("MemberSocketHash"); + } +} \ No newline at end of file diff --git a/app/nexus-app/source/module/socket-io-module/socket-io-app.ts b/app/nexus-app/source/module/socket-io-module/socket-io-app.ts new file mode 100644 index 0000000..4d21cf5 --- /dev/null +++ b/app/nexus-app/source/module/socket-io-module/socket-io-app.ts @@ -0,0 +1,92 @@ + +import { Server, Socket } from 'socket.io' +import stampLogPrinter, { LogState } from '../../../../../modules/trace-unit/stamp-log-printer'; +import http from "http"; +//import fileNameParser from '../../../../../modules/parse-unit/path-leaf-reader'; +import * as config from "./socket-io-config"; +import { createAdapter } from '@socket.io/redis-adapter'; +import { LanePocketClient } from '../../../../../modules/pocket-link/pocket-link'; +import socketOccupancyGate from "../../../../../middleware/socket-occupancy-gate"; +import socketMemberGate from "../../../../../middleware/socket-member-gate"; +import * as env from "../../../../../config/runtime-spec"; +import { HubOccupancyTicketData } from '../../../../../middleware/gate-spec/socket-occupancy-gate-spec'; +import * as occupancyKeeper from "../../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberSocketHash } from '../member-socket-unit/member-socket-keeper'; +import leaveCasualPairingUsecase from '../../service/leave-casual-pairing-usecase'; +import leaveBracketPairingUsecase from '../../service/leave-bracket-pairing-usecase'; + + + +export class Route { + public route: string = ""; + public listener: (socket: Socket, data: string) => void | void = () => { }; + public constructor(route: string, listener: (socket: Socket, data: string) => void) { + this.route = route; + this.listener = listener; + } +} + +const routeList: Array = new Array(); + +const io = new Server({ + maxHttpBufferSize: 1e5, + pingTimeout: env.pingTimeOut, + pingInterval: 10 * 1000 //10쎈에 한번식 +}) + +function HandleSocketConnect(socket: Socket) { + stampLogPrinter.Log(__filename, config.memberSocketLinkedMSG(socket)); + + socket.on("disconnect", async (reason) => { + let tokenData = (socket.data as HubOccupancyTicketData); + if (!tokenData?.memberKey) { + return; + } + + leaveCasualPairingUsecase(socket); + leaveBracketPairingUsecase(socket); + + await MemberSocketHash.RemoveData(tokenData.memberKey); + await occupancyKeeper.CloseOccupancyUnlessInGame(tokenData.memberKey); + }); + + for (let index = 0; index < routeList.length; index++) { + const element = routeList[index]; + socket.on(element.route, (data: string) => { + element.listener(socket, data); + }); + } +} + +export function AddRoute(route: Route) { + stampLogPrinter.Log(__filename, config.SuccessRegisterRouteMsg(route.route)); + routeList.push(route); +} + +export function BroadCast(ev: string, data: string) { + io.emit(ev, data); +} + +export function EmitBySocketID(socketID: string, ev: string, data: string) { + io.to(socketID).emit(ev, data); +} + +export function DisconnectBySocketID(socketID: string) { + io.to(socketID).disconnectSockets(); +} + +export function LoadSocketIOServer(httpServer: http.Server) { + const pubClient = LanePocketClient(); + const subClient = pubClient.duplicate(); + + pubClient.on('error', err => stampLogPrinter.Log('[Redis-Pub Error]', err.stack, LogState.Error)); + subClient.on('error', err => stampLogPrinter.Log('[Redis-Sub Error]', err.stack, LogState.Error)); + + io.adapter(createAdapter(pubClient, subClient)); + + io.on('error', err => stampLogPrinter.Log(__filename, err, LogState.Error)); + io.on('connection', HandleSocketConnect) + io.use(socketOccupancyGate); + io.use(socketMemberGate); + io.listen(httpServer); +} diff --git a/app/nexus-app/source/module/socket-io-module/socket-io-config.ts b/app/nexus-app/source/module/socket-io-module/socket-io-config.ts new file mode 100644 index 0000000..96552d0 --- /dev/null +++ b/app/nexus-app/source/module/socket-io-module/socket-io-config.ts @@ -0,0 +1,7 @@ +import { Socket } from 'socket.io' +import { HubOccupancyTicketData } from "../../../../../middleware/gate-spec/socket-occupancy-gate-spec" + +export function SuccessLoadSocketIOMsg(port: number): string { return `Start Socket IO Server ` }; +export function SuccessRegisterRouteMsg(route: string): string { return 'Register Route :' + route }; +export function memberSocketLinkedMSG(socket: Socket): string { return `Success Connect Server MemberKey:${(socket.data as HubOccupancyTicketData).memberKey}` }; + diff --git a/app/nexus-app/source/router/enter-bracket-pairing-gateway.ts b/app/nexus-app/source/router/enter-bracket-pairing-gateway.ts new file mode 100644 index 0000000..29be7b0 --- /dev/null +++ b/app/nexus-app/source/router/enter-bracket-pairing-gateway.ts @@ -0,0 +1,6 @@ +import * as socketIO from "../module/socket-io-module/socket-io-app"; +import enterPairingUsecase from "../service/enter-bracket-pairing-usecase"; + +export default new socketIO.Route("start-rank-matching", (socket, data) => { + enterPairingUsecase(socket); +}); diff --git a/app/nexus-app/source/router/enter-casual-pairing-gateway.ts b/app/nexus-app/source/router/enter-casual-pairing-gateway.ts new file mode 100644 index 0000000..9199e43 --- /dev/null +++ b/app/nexus-app/source/router/enter-casual-pairing-gateway.ts @@ -0,0 +1,6 @@ +import * as socketIO from "../module/socket-io-module/socket-io-app"; +import enterPairingUsecase from "../service/enter-casual-pairing-usecase"; + +export default new socketIO.Route("start-free-matching", (socket, data) => { + enterPairingUsecase(socket); +}); diff --git a/app/nexus-app/source/router/leave-bracket-pairing-gateway.ts b/app/nexus-app/source/router/leave-bracket-pairing-gateway.ts new file mode 100644 index 0000000..61caeed --- /dev/null +++ b/app/nexus-app/source/router/leave-bracket-pairing-gateway.ts @@ -0,0 +1,6 @@ +import * as socketIO from "../module/socket-io-module/socket-io-app"; +import leavePairingUsecase from "../service/leave-bracket-pairing-usecase"; + +export default new socketIO.Route("stop-rank-matching", (socket, data) => { + leavePairingUsecase(socket); +}); diff --git a/app/nexus-app/source/router/leave-casual-pairing-gateway.ts b/app/nexus-app/source/router/leave-casual-pairing-gateway.ts new file mode 100644 index 0000000..9bc3492 --- /dev/null +++ b/app/nexus-app/source/router/leave-casual-pairing-gateway.ts @@ -0,0 +1,6 @@ +import * as socketIO from "../module/socket-io-module/socket-io-app"; +import leavePairingUsecase from "../service/leave-casual-pairing-usecase"; + +export default new socketIO.Route("stop-free-matching", (socket, data) => { + leavePairingUsecase(socket); +}); diff --git a/app/nexus-app/source/routine/bracket-pairing-clock.ts b/app/nexus-app/source/routine/bracket-pairing-clock.ts new file mode 100644 index 0000000..19f2ac6 --- /dev/null +++ b/app/nexus-app/source/routine/bracket-pairing-clock.ts @@ -0,0 +1,24 @@ +import { ScanPairingPool } from "../../../../manager/pocket-keeper/bracket-pairing-keeper/bracket-pairing-keeper"; +import { WithPairingScanLock } from "../../../../manager/pocket-keeper/pairing-scan-lock"; + +const pairingScanInterval = 1000; +let pairingClock: NodeJS.Timeout; +let scanInFlight = false; + +export function StartRoutine() { + pairingClock = setInterval(async () => { + if (scanInFlight) { + return; + } + scanInFlight = true; + try { + await WithPairingScanLock(ScanPairingPool); + } finally { + scanInFlight = false; + } + }, pairingScanInterval); +} + +export function StopScheduleWorker() { + clearInterval(pairingClock); +} diff --git a/app/nexus-app/source/routine/casual-pairing-clock.ts b/app/nexus-app/source/routine/casual-pairing-clock.ts new file mode 100644 index 0000000..3390bc2 --- /dev/null +++ b/app/nexus-app/source/routine/casual-pairing-clock.ts @@ -0,0 +1,24 @@ +import { ScanPairingPool } from "../../../../manager/pocket-keeper/casual-pairing-keeper/casual-pairing-keeper"; +import { WithPairingScanLock } from "../../../../manager/pocket-keeper/pairing-scan-lock"; + +const pairingScanInterval = 1000; +let pairingClock: NodeJS.Timeout; +let scanInFlight = false; + +export function StartRoutine() { + pairingClock = setInterval(async () => { + if (scanInFlight) { + return; + } + scanInFlight = true; + try { + await WithPairingScanLock(ScanPairingPool); + } finally { + scanInFlight = false; + } + }, pairingScanInterval); +} + +export function StopScheduleWorker() { + clearInterval(pairingClock); +} diff --git a/app/nexus-app/source/routine/occupancy-clock.ts b/app/nexus-app/source/routine/occupancy-clock.ts new file mode 100644 index 0000000..1713ddb --- /dev/null +++ b/app/nexus-app/source/routine/occupancy-clock.ts @@ -0,0 +1,21 @@ +import stampLogPrinter from "../../../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../../../modules/parse-unit/path-leaf-reader"; +import * as hubOccupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import * as cron from "node-cron" +import { HandleOccupancyLapsed } from "../handler/occupancy-expired-arranger"; + + +const minuteScheduleWorker = cron.schedule('* * * * *', MinuteScheduleWorker, { scheduled: false }); + +export async function StartRoutine() { + minuteScheduleWorker.start(); +} + +export async function StopRoutine() { + minuteScheduleWorker.stop(); +} + +async function MinuteScheduleWorker(date: any) { + await hubOccupancyKeeper.SweepLapsedOccupancy(HandleOccupancyLapsed); + stampLogPrinter.Log(__filename, "Checked Expired Session"); +} \ No newline at end of file diff --git a/app/nexus-app/source/service/enter-bracket-pairing-usecase.ts b/app/nexus-app/source/service/enter-bracket-pairing-usecase.ts new file mode 100644 index 0000000..b457062 --- /dev/null +++ b/app/nexus-app/source/service/enter-bracket-pairing-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io" +import { HubOccupancyTicketData } from "../../../../middleware/gate-spec/socket-occupancy-gate-spec"; +import { EnterPairingPool } from "../../../../manager/pocket-keeper/bracket-pairing-keeper/bracket-pairing-keeper" +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec"; + +export default async (socket: Socket) => { + let userStatus = await occupancyKeeper.ReadMemberPresence((socket.data as HubOccupancyTicketData).memberKey as string); + if (userStatus.userStatus == MemberPresence.Lobby) { + await EnterPairingPool((socket.data as HubOccupancyTicketData).memberKey as string, socket.id); + } +} diff --git a/app/nexus-app/source/service/enter-casual-pairing-usecase.ts b/app/nexus-app/source/service/enter-casual-pairing-usecase.ts new file mode 100644 index 0000000..64bc70d --- /dev/null +++ b/app/nexus-app/source/service/enter-casual-pairing-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io" +import { HubOccupancyTicketData } from "../../../../middleware/gate-spec/socket-occupancy-gate-spec"; +import { EnterPairingPool } from "../../../../manager/pocket-keeper/casual-pairing-keeper/casual-pairing-keeper" +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec"; + +export default async (socket: Socket) => { + let userStatus = await occupancyKeeper.ReadMemberPresence((socket.data as HubOccupancyTicketData).memberKey as string); + if (userStatus.userStatus == MemberPresence.Lobby) { + await EnterPairingPool((socket.data as HubOccupancyTicketData).memberKey as string, socket.id); + } +} diff --git a/app/nexus-app/source/service/leave-bracket-pairing-usecase.ts b/app/nexus-app/source/service/leave-bracket-pairing-usecase.ts new file mode 100644 index 0000000..65575e3 --- /dev/null +++ b/app/nexus-app/source/service/leave-bracket-pairing-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import { HubOccupancyTicketData } from "../../../../middleware/gate-spec/socket-occupancy-gate-spec"; +import { LeavePairingPool } from "../../../../manager/pocket-keeper/bracket-pairing-keeper/bracket-pairing-keeper"; +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec"; + +export default async (socket: Socket) => { + let userStatus = await occupancyKeeper.ReadMemberPresence((socket.data as HubOccupancyTicketData).memberKey as string); + if (userStatus.userStatus == MemberPresence.Lobby) { + await LeavePairingPool((socket.data as HubOccupancyTicketData).memberKey); + } +} diff --git a/app/nexus-app/source/service/leave-casual-pairing-usecase.ts b/app/nexus-app/source/service/leave-casual-pairing-usecase.ts new file mode 100644 index 0000000..4128928 --- /dev/null +++ b/app/nexus-app/source/service/leave-casual-pairing-usecase.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import { HubOccupancyTicketData } from "../../../../middleware/gate-spec/socket-occupancy-gate-spec"; +import { LeavePairingPool } from "../../../../manager/pocket-keeper/casual-pairing-keeper/casual-pairing-keeper"; +import * as occupancyKeeper from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper"; +import { MemberPresence } from "../../../../manager/pocket-keeper/occupancy-keeper/occupancy-keeper-spec"; + +export default async (socket: Socket) => { + let userStatus = await occupancyKeeper.ReadMemberPresence((socket.data as HubOccupancyTicketData).memberKey as string); + if (userStatus.userStatus == MemberPresence.Lobby) { + await LeavePairingPool((socket.data as HubOccupancyTicketData).memberKey); + } +} diff --git a/app/overseer-app/overseer-app.ts b/app/overseer-app/overseer-app.ts new file mode 100644 index 0000000..5d2a9bc --- /dev/null +++ b/app/overseer-app/overseer-app.ts @@ -0,0 +1,28 @@ +import * as config from "../../config/host-spec"; +import * as sqlUnit from "../../modules/sql-unit/sql-link"; +import ReadJsonFile from "../../modules/parse-unit/json-reader"; +import stampLogPrinter from "../../modules/trace-unit/stamp-log-printer"; +//import fileNameParser from "../../modules/parse-unit/path-leaf-reader"; +import AdminAppLoader from "./source/loader/overseer-boot"; +import https from "https"; +import { LoadCommand } from "./source/loader/overseer-command-boot"; +import { BindFatalProcessGuards } from "../../modules/trace-unit/fatal-process-guard"; + +StartServer(); + +function StartServer(): void { + LoadCommand(); + sqlUnit.BootSqlPool(); + + const expressApp = new config.App().application; + AdminAppLoader(expressApp); + + https.createServer(config.httpsServerOption, expressApp).listen(config.adminServerPort, () => { + let packageData = ReadJsonFile(config.packagePath); + console.log(config.serverLogo); + stampLogPrinter.Log(__filename, `${packageData["name"]} version: ${packageData["version"]}`); + }); + BindFatalProcessGuards(); +} + + diff --git a/app/overseer-app/source/loader/overseer-boot.ts b/app/overseer-app/source/loader/overseer-boot.ts new file mode 100644 index 0000000..6f902dd --- /dev/null +++ b/app/overseer-app/source/loader/overseer-boot.ts @@ -0,0 +1,30 @@ +import express, { Application } from "express"; +import bodyParser from "body-parser"; +import expressBasicAuth from "express-basic-auth"; +import commandManager from "../module/overseer-verb"; +import * as env from "../../../../config/runtime-spec"; + +const adminBuildPath = "./resource/public/admin_page/build"; + +export default (app: Application) => { + app.use(bodyParser.json()); + + app.use("/admin", expressBasicAuth({ + users: { + [env.overseerUser]: env.overseerPassword, + }, + challenge: true, + })); + + app.use("/static", express.static(adminBuildPath + "/static")); + app.use("/admin", express.static(adminBuildPath)); + + app.post("/admin/cmd", async (req, res) => { + try { + res.send(await commandManager.HandleCommandLine(req.body.cmd)); + } catch (err: Error | any) { + console.log(err); + res.send(err.message); + } + }); +}; diff --git a/app/overseer-app/source/loader/overseer-command-boot.ts b/app/overseer-app/source/loader/overseer-command-boot.ts new file mode 100644 index 0000000..e31d951 --- /dev/null +++ b/app/overseer-app/source/loader/overseer-command-boot.ts @@ -0,0 +1,187 @@ +import NodeCommand, { CommandNode } from "../module/overseer-verb"; +import * as inspectionManager from "../../../../manager/pocket-keeper/host-hold-keeper/host-hold-keeper"; +import { ServerStateType } from "../../../../data/dto/core-packet"; +import * as parcelKeeper from "../../../../manager/pocket-keeper/parcel-keeper/parcel-keeper" +import { StockKind } from "../../../../data/entity/nexforge_db_record"; +import * as grantVesselCmdHandler from "../service/grant-vessel-usecase"; +import * as grantGadgetCmdHandler from "../service/grant-gadget-usecase"; +import * as grantRelicCmdHandler from "../service/grant-relic-usecase"; + +export function LoadCommand() { + SetHelpCommandNode(); + SetAdminCommandNode(); + SetServerCommandNode(); +} + +function SetHelpCommandNode() { + let help_node: CommandNode = new CommandNode("help", (inputDataDictionary): Promise => { + return new Promise((resolve, reject) => { + if ("--help" in inputDataDictionary || "-h" in inputDataDictionary) { + + } else { + resolve(NodeCommand.ShowMainCommands()); + } + }) + }); + + NodeCommand.SetCommandNode(help_node); +} + +function SetServerCommandNode() { + let server_node: CommandNode = new CommandNode("server", (inputDataDictionary): Promise => { + return new Promise((resolve, reject) => { + if ("--help" in inputDataDictionary || "-h" in inputDataDictionary) { + + } else { + resolve("If you want to see server - related commands, use 'admin --help or -h'"); + } + resolve(""); + }); + }); + + server_node.GenerateCommand("status", (inputDataDictionary): Promise => { + return new Promise(async (resolve, reject) => { + if ("--inspection" in inputDataDictionary || "-i" in inputDataDictionary) { + await inspectionManager.StampHostHealth(ServerStateType.Inspection); + } else if ("--normal" in inputDataDictionary || "-n" in inputDataDictionary) { + await inspectionManager.StampHostHealth(ServerStateType.Normal); + } + resolve(""); + }); + }, "-i --inspection [Set Server Status Inspection] -n --normal [Set Server Status Normal]") + + NodeCommand.SetCommandNode(server_node); +} + +function SetAdminCommandNode() { + let admin_node: CommandNode = new CommandNode("admin", (inputDataDictionary): Promise => { + return new Promise(async (resolve, reject) => { + if ("--help" in inputDataDictionary || "-h" in inputDataDictionary) { + + } else { + resolve("If you want to see admin - related commands, use 'admin --help or -h'"); + } + resolve(""); + }); + }); + + let admin_give_node = admin_node.GenerateCommand("give", (inputDataDictionary): Promise => { + return new Promise(async (resolve, reject) => { + resolve(""); + }); + }); + + admin_give_node.GenerateCommand("gift", (inputDataDictionary): Promise => { + return new Promise(async (resolve, reject) => { + + let name: string = ""; + let expiryTime: string = ""; + let mCode: string = ""; + + if ("--expiry" in inputDataDictionary) { + expiryTime = inputDataDictionary["--expiry"]; + } else if ("-e" in inputDataDictionary) { + expiryTime = inputDataDictionary["-e"]; + } + + if ("--mCode" in inputDataDictionary) { + mCode = inputDataDictionary["--mCode"]; + } else if ("-m" in inputDataDictionary) { + mCode = inputDataDictionary["-m"]; + } + + if ("--name" in inputDataDictionary) { + name = inputDataDictionary["--name"]; + } else if ("-n" in inputDataDictionary) { + name = inputDataDictionary["-n"]; + } + if (name != "" && mCode != "" && expiryTime != "") { + let unitCode: string = ""; + let partsCode: string = ""; + let itemCode: string = ""; + + if ("--unit" in inputDataDictionary) { + unitCode = inputDataDictionary["--unit"]; + } else if ("-u" in inputDataDictionary) { + unitCode = inputDataDictionary["-u"]; + } else if ("--parts" in inputDataDictionary) { + partsCode = inputDataDictionary["--parts"]; + } else if ("-p" in inputDataDictionary) { + partsCode = inputDataDictionary["-p"]; + } else if ("--item" in inputDataDictionary) { + itemCode = inputDataDictionary["--item"]; + } else if ("-i" in inputDataDictionary) { + itemCode = inputDataDictionary["-i"]; + } + + if (unitCode != "") { + await parcelKeeper.SendParcelByAlias(name, Number(mCode), Number(expiryTime), Number(unitCode), StockKind.unit, 1); + } else if (partsCode != "") { + await parcelKeeper.SendParcelByAlias(name, Number(mCode), Number(expiryTime), Number(partsCode), StockKind.parts, 1); + } else if (itemCode != "") { + let amount: string = ""; + if ("--amount" in inputDataDictionary) { + amount = inputDataDictionary["--amount"]; + } else if ("-a" in inputDataDictionary) { + amount = inputDataDictionary["-a"]; + } + if (amount != "") { + await parcelKeeper.SendParcelByAlias(name, Number(mCode), Number(expiryTime), Number(itemCode), StockKind.item, Number(amount)); + } + } + } + resolve(""); + }); + + }, "-n -name -e --expiry