From 91626edf2a3d736610a2296d755fe8114f409975 Mon Sep 17 00:00:00 2001 From: Zak Shearman <34372536+ZakShearman@users.noreply.github.com> Date: Sat, 16 Sep 2023 21:55:18 +0100 Subject: [PATCH] First working iteration --- .github/workflows/ci.yaml | 64 +++------- go.mod | 9 +- go.sum | 23 +++- internal/app/game_player_data.go | 19 ++- internal/config/gamemode.go | 14 +++ internal/kafka/consumer.go | 115 ++++++++++++++++++ internal/repository/model/model.go | 11 +- internal/repository/mongo.go | 2 +- internal/repository/public.go | 2 +- internal/service/game_player_data.go | 40 +++--- tests/publish_update_game_player_data/main.go | 59 +++++++++ 11 files changed, 266 insertions(+), 92 deletions(-) create mode 100644 internal/config/gamemode.go create mode 100644 internal/kafka/consumer.go create mode 100644 tests/publish_update_game_player_data/main.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 3dfca49..3b99181 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,57 +1,29 @@ -name: ci +name: "Build and push" + on: push: - pull_request: - types: [ opened, reopened ] - -env: - IMAGE_NAME: ghcr.io/${{ github.repository }} + branches: [main] + pull_request: {} jobs: lint: - runs-on: ubuntu-latest + runs-on: "ubuntu-latest" steps: - - uses: actions/setup-go@v4 - with: - go-version: "1.20" - cache: false - - uses: actions/checkout@v3 - - name: go-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest - args: --timeout 2m0s - - build: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: [ lint ] - runs-on: ubuntu-latest - outputs: - image: ${{ steps.build.outputs.image }} + - uses: "emortalmc/actions/go/lint@main" + publish: + needs: "lint" + runs-on: "ubuntu-latest" steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Login to container registry - uses: docker/login-action@v2 + - uses: "emortalmc/actions/go/publish-single@main" with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} + registry-password: "${{ secrets.GITHUB_TOKEN }}" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push - uses: docker/build-push-action@v4 + deploy: + needs: "publish" + runs-on: "ubuntu-latest" + if: "github.ref == 'refs/heads/main'" + steps: + - uses: "emortalmc/actions/k8s-sync@main" with: - context: . - file: ./Dockerfile - push: true - platforms: linux/amd64,linux/arm64 - cache-from: type=registry,ref=${{ env.IMAGE_NAME }}:latest - cache-to: type=inline - tags: | - ${{ env.IMAGE_NAME }}:${{ github.sha }} - ${{ env.IMAGE_NAME }}:latest + manifest-path: "deployment.yaml" \ No newline at end of file diff --git a/go.mod b/go.mod index 147cfe6..a269db9 100644 --- a/go.mod +++ b/go.mod @@ -1,14 +1,17 @@ module game-player-data -go 1.20 +go 1.21 require ( - github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602153610-3e1cd1adcd5a + github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916202652-8975e470ce34 + github.com/gogo/protobuf v1.3.2 github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + github.com/segmentio/kafka-go v0.4.39 github.com/spf13/viper v1.16.0 go.mongodb.org/mongo-driver v1.11.6 go.uber.org/zap v1.24.0 + google.golang.org/grpc v1.55.0 ) require ( @@ -21,6 +24,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect @@ -39,7 +43,6 @@ require ( golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index d3c7502..5991fdb 100644 --- a/go.sum +++ b/go.sum @@ -51,10 +51,12 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602152823-6b49cef72900 h1:r6RfvQIDqiVzlYA9r/Wh/zWtGTgzePLShsbsu/VNeD0= -github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602152823-6b49cef72900/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY= github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602153610-3e1cd1adcd5a h1:8Koes2hRKavzHtFB6+mgjlZesdkCnZ4RQ7kWK+xv83M= github.com/emortalmc/proto-specs/gen/go v0.0.0-20230602153610-3e1cd1adcd5a/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY= +github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916193435-6abf652a5c2f h1:DvOyf+SLgDoqLwDlV3IqDetZO7DwANJZm9clQGHD67g= +github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916193435-6abf652a5c2f/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY= +github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916202652-8975e470ce34 h1:q095naqNtgsBaI8o1TgkIo3U1pkSbIEnRW1KqWWu8Ww= +github.com/emortalmc/proto-specs/gen/go v0.0.0-20230916202652-8975e470ce34/go.mod h1:se+tHcK9FWxeadkxLF5uj+SPauEye0X+Iq6cGczXGJY= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -62,6 +64,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -115,6 +118,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -154,9 +158,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -166,6 +172,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -175,6 +183,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/segmentio/kafka-go v0.4.39 h1:75smaomhvkYRwtuOwqLsdhgCG30B82NsbdkdDfFbvrw= +github.com/segmentio/kafka-go v0.4.39/go.mod h1:T0MLgygYvmqmBvC+s8aCcbVNfJN4znVne5j0Pzowp/Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= @@ -210,6 +221,10 @@ github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg/scram v1.0.5 h1:TuS0RFmt5Is5qm9Tm2SoD89OPqe4IRiFtyFY4iwWXsw= +github.com/xdg/scram v1.0.5/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4= +github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -229,6 +244,7 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= @@ -310,6 +326,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -371,10 +388,12 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/app/game_player_data.go b/internal/app/game_player_data.go index 2ff9cdb..5b03066 100644 --- a/internal/app/game_player_data.go +++ b/internal/app/game_player_data.go @@ -3,10 +3,9 @@ package app import ( "context" "game-player-data/internal/config" + "game-player-data/internal/kafka" "game-player-data/internal/repository" - "game-player-data/internal/repository/model" "game-player-data/internal/service" - "github.com/google/uuid" "go.uber.org/zap" "os/signal" "sync" @@ -26,15 +25,15 @@ func Run(cfg *config.Config, logger *zap.SugaredLogger) { logger.Fatalw("failed to create repository", err) } - if err := repo.SaveBlockSumoPlayer(ctx, &model.BlockSumoData{ - PlayerId: uuid.MustParse("8d36737e-1c0a-4a71-87de-9906f577845e"), - BlockSlot: 1, - ShearsSlot: 2, - }); err != nil { - panic(err) - } + //if err := repo.SaveBlockSumoPlayer(ctx, &model.BlockSumoData{ + // PlayerId: uuid.MustParse("8d36737e-1c0a-4a71-87de-9906f577845e"), + // BlockSlot: 1, + // ShearsSlot: 2, + //}); err != nil { + // panic(err) + //} - // todo kafka consumer + kafka.NewConsumer(ctx, wg, cfg.Kafka, logger, repo) service.RunServices(ctx, logger, wg, cfg, repo) diff --git a/internal/config/gamemode.go b/internal/config/gamemode.go new file mode 100644 index 0000000..1b21cae --- /dev/null +++ b/internal/config/gamemode.go @@ -0,0 +1,14 @@ +package config + +import ( + "game-player-data/internal/repository/model" + "github.com/google/uuid" +) + +func CreateDefaultBlockSumoData(playerId uuid.UUID) *model.BlockSumoData { + return &model.BlockSumoData{ + PlayerId: playerId, + BlockSlot: 2, + ShearsSlot: 1, + } +} diff --git a/internal/kafka/consumer.go b/internal/kafka/consumer.go new file mode 100644 index 0000000..53f29c5 --- /dev/null +++ b/internal/kafka/consumer.go @@ -0,0 +1,115 @@ +package kafka + +import ( + "context" + "errors" + "fmt" + "game-player-data/internal/config" + "game-player-data/internal/repository" + pbmsg "github.com/emortalmc/proto-specs/gen/go/message/gameplayerdata" + pbmodel "github.com/emortalmc/proto-specs/gen/go/model/gameplayerdata" + "github.com/emortalmc/proto-specs/gen/go/nongenerated/kafkautils" + "github.com/google/uuid" + "github.com/segmentio/kafka-go" + "go.mongodb.org/mongo-driver/mongo" + "go.uber.org/zap" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + "sync" +) + +const GamePlayerDataTopic = "game-player-data" + +type consumer struct { + logger *zap.SugaredLogger + repo repository.Repository + + reader *kafka.Reader +} + +func NewConsumer(ctx context.Context, wg *sync.WaitGroup, cfg *config.KafkaConfig, logger *zap.SugaredLogger, + repo repository.Repository) { + + reader := kafka.NewReader(kafka.ReaderConfig{ + Brokers: []string{fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)}, + GroupID: "game-player-data", + GroupTopics: []string{GamePlayerDataTopic}, + + Logger: kafka.LoggerFunc(func(format string, args ...interface{}) { + logger.Infow(fmt.Sprintf(format, args...)) + }), + ErrorLogger: kafka.LoggerFunc(func(format string, args ...interface{}) { + logger.Errorw(fmt.Sprintf(format, args...)) + }), + }) + + c := &consumer{ + logger: logger, + repo: repo, + + reader: reader, + } + + handler := kafkautils.NewConsumerHandler(logger, reader) + handler.RegisterHandler(&pbmsg.UpdateGamePlayerDataMessage{}, c.handleUpdateGamePlayerDataMessage) + + logger.Infow("starting listening for kafka messages", "topics", reader.Config().GroupTopics) + + wg.Add(1) + go func() { + defer wg.Done() + handler.Run(ctx) // Run is blocking until the context is cancelled + if err := reader.Close(); err != nil { + logger.Errorw("error closing kafka reader", "error", err) + } + }() +} + +func (c *consumer) handleUpdateGamePlayerDataMessage(ctx context.Context, _ *kafka.Message, uncast proto.Message) { + msg := uncast.(*pbmsg.UpdateGamePlayerDataMessage) + + pId, err := uuid.Parse(msg.PlayerId) + if err != nil { + c.logger.Errorw("failed to parse player id", "error", err) + return + } + + switch msg.GameMode { + case pbmodel.GameDataGameMode_BLOCK_SUMO: + err = c.handleBlockSumoUpdate(ctx, pId, msg) + } + + if err != nil { + c.logger.Errorw("failed to handle update", "error", err, "playerId", pId, "gameMode", msg.GameMode) + return + } +} + +func (c *consumer) handleBlockSumoUpdate(ctx context.Context, pId uuid.UUID, msg *pbmsg.UpdateGamePlayerDataMessage) error { + player, err := c.repo.GetBlockSumoData(ctx, pId) + + if err != nil { + if !errors.Is(err, mongo.ErrNoDocuments) { + return fmt.Errorf("failed to get block sumo data: %w", err) + } + + player = config.CreateDefaultBlockSumoData(pId) + } + + msgData := &pbmodel.BlockSumoPlayerData{} + + if err := anypb.UnmarshalTo(msg.Data, msgData, proto.UnmarshalOptions{}); err != nil { + return fmt.Errorf("failed to unmarshal data: %w", err) + } + + for _, path := range msg.DataMask.Paths { + switch path { + case "block_slot": + player.BlockSlot = msgData.BlockSlot + case "shears_slot": + player.ShearsSlot = msgData.ShearsSlot + } + } + + return c.repo.SaveBlockSumoPlayer(ctx, player) +} diff --git a/internal/repository/model/model.go b/internal/repository/model/model.go index 486094f..754284b 100644 --- a/internal/repository/model/model.go +++ b/internal/repository/model/model.go @@ -3,8 +3,13 @@ package model import ( "github.com/emortalmc/proto-specs/gen/go/model/gameplayerdata" "github.com/google/uuid" + "google.golang.org/protobuf/types/known/anypb" ) +type GameData interface { + ToAnyProto() (*anypb.Any, error) +} + type BlockSumoData struct { PlayerId uuid.UUID `bson:"_id"` @@ -12,11 +17,11 @@ type BlockSumoData struct { ShearsSlot uint32 `bson:"shearsSlot"` } -func (d *BlockSumoData) ToProto() *gameplayerdata.BlockSumoPlayerData { - return &gameplayerdata.BlockSumoPlayerData{ +func (d *BlockSumoData) ToAnyProto() (*anypb.Any, error) { + return anypb.New(&gameplayerdata.BlockSumoPlayerData{ BlockSlot: d.BlockSlot, ShearsSlot: d.ShearsSlot, - } + }) } func (d *BlockSumoData) FromProto(pId uuid.UUID, data *gameplayerdata.BlockSumoPlayerData) { diff --git a/internal/repository/mongo.go b/internal/repository/mongo.go index 3df0bb9..746cd64 100644 --- a/internal/repository/mongo.go +++ b/internal/repository/mongo.go @@ -58,7 +58,7 @@ func NewMongoRepository(ctx context.Context, logger *zap.SugaredLogger, wg *sync return repo, nil } -func (m *mongoRepository) GetBlockSumoPlayer(ctx context.Context, playerId uuid.UUID) (*model.BlockSumoData, error) { +func (m *mongoRepository) GetBlockSumoData(ctx context.Context, playerId uuid.UUID) (*model.BlockSumoData, error) { result := m.getData(ctx, playerId, m.blockSumoCollection) var data model.BlockSumoData diff --git a/internal/repository/public.go b/internal/repository/public.go index 0115940..8b664fa 100644 --- a/internal/repository/public.go +++ b/internal/repository/public.go @@ -7,7 +7,7 @@ import ( ) type Repository interface { - GetBlockSumoPlayer(ctx context.Context, playerId uuid.UUID) (*model.BlockSumoData, error) + GetBlockSumoData(ctx context.Context, playerId uuid.UUID) (*model.BlockSumoData, error) SaveBlockSumoPlayer(ctx context.Context, data *model.BlockSumoData) error GetTowerDefencePlayer(ctx context.Context, playerId uuid.UUID) (*model.TowerDefenceData, error) diff --git a/internal/service/game_player_data.go b/internal/service/game_player_data.go index 7437cf7..4d3d98e 100644 --- a/internal/service/game_player_data.go +++ b/internal/service/game_player_data.go @@ -2,8 +2,11 @@ package service import ( "context" + "errors" "game-player-data/internal/repository" + "game-player-data/internal/repository/model" pb "github.com/emortalmc/proto-specs/gen/go/grpc/gameplayerdata" + "github.com/emortalmc/proto-specs/gen/go/model/gameplayerdata" "github.com/google/uuid" "go.mongodb.org/mongo-driver/mongo" "google.golang.org/grpc/codes" @@ -22,50 +25,35 @@ func newGamePlayerDataService(repo repository.Repository) pb.GamePlayerDataServi } } -func (s *gamePlayerDataService) GetBlockSumoPlayerData(ctx context.Context, req *pb.PlayerIdRequest) (*pb.GetBlockSumoPlayerDataResponse, error) { +func (s *gamePlayerDataService) GetGamePlayerData(ctx context.Context, req *pb.GetGamePlayerDataRequest) (*pb.GetGamePlayerDataResponse, error) { pId, err := uuid.Parse(req.PlayerId) if err != nil { return nil, status.Error(codes.InvalidArgument, "invalid player id") } - data, err := s.repo.GetBlockSumoPlayer(ctx, pId) - if err != nil { - return nil, createDbErr(err) - } - - return &pb.GetBlockSumoPlayerDataResponse{PlayerData: data.ToProto()}, nil -} + var data model.GameData -func (s *gamePlayerDataService) GetTowerDefencePlayerData(ctx context.Context, req *pb.PlayerIdRequest) (*pb.GetTowerDefencePlayerDataResponse, error) { - pId, err := uuid.Parse(req.PlayerId) - if err != nil { - return nil, status.Error(codes.InvalidArgument, "invalid player id") + switch req.GameMode { + case gameplayerdata.GameDataGameMode_BLOCK_SUMO: + data, err = s.repo.GetBlockSumoData(ctx, pId) } - data, err := s.repo.GetTowerDefencePlayer(ctx, pId) if err != nil { return nil, createDbErr(err) } - return &pb.GetTowerDefencePlayerDataResponse{PlayerData: data.ToProto()}, nil -} - -func (s *gamePlayerDataService) GetMinesweeperPlayerData(ctx context.Context, req *pb.PlayerIdRequest) (*pb.GetMinesweeperPlayerDataResponse, error) { - pId, err := uuid.Parse(req.PlayerId) - if err != nil { - return nil, status.Error(codes.InvalidArgument, "invalid player id") - } - - data, err := s.repo.GetMinesweeperPlayer(ctx, pId) + anyData, err := data.ToAnyProto() if err != nil { - return nil, createDbErr(err) + return nil, status.Error(codes.Internal, "failed to convert data to proto") } - return &pb.GetMinesweeperPlayerDataResponse{PlayerData: data.ToProto()}, nil + return &pb.GetGamePlayerDataResponse{ + Data: anyData, + }, nil } func createDbErr(err error) error { - if err == mongo.ErrNoDocuments { + if errors.Is(err, mongo.ErrNoDocuments) { return status.Error(codes.NotFound, "player not found") } else { return status.Error(codes.Internal, "failed to get player data") diff --git a/tests/publish_update_game_player_data/main.go b/tests/publish_update_game_player_data/main.go new file mode 100644 index 0000000..732d3b1 --- /dev/null +++ b/tests/publish_update_game_player_data/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "context" + kafka2 "game-player-data/internal/kafka" + pbmsg "github.com/emortalmc/proto-specs/gen/go/message/gameplayerdata" + pbmodel "github.com/emortalmc/proto-specs/gen/go/model/gameplayerdata" + "github.com/segmentio/kafka-go" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/fieldmaskpb" + "time" +) + +const pIdStr = "8d36737e-1c0a-4a71-87de-9906f577845e" + +func main() { + w := &kafka.Writer{ + Addr: kafka.TCP("localhost:9092"), + Topic: kafka2.GamePlayerDataTopic, + BatchSize: 1, + Async: false, + } + + data := &pbmodel.BlockSumoPlayerData{ + BlockSlot: 12, + ShearsSlot: 13, + } + + dataAny, err := anypb.New(data) + if err != nil { + panic(err) + } + + msg := &pbmsg.UpdateGamePlayerDataMessage{ + PlayerId: pIdStr, + GameMode: pbmodel.GameDataGameMode_BLOCK_SUMO, + Data: dataAny, + DataMask: &fieldmaskpb.FieldMask{Paths: []string{"block_slot", "shears_slot"}}, + } + + if err := w.WriteMessages(context.Background(), createKafkaMessage(msg)); err != nil { + panic(err) + } +} + +func createKafkaMessage(pb proto.Message) kafka.Message { + bytes, err := proto.Marshal(pb) + if err != nil { + panic(err) + } + + return kafka.Message{ + Key: nil, + Value: bytes, + Headers: []kafka.Header{{Key: "X-Proto-Type", Value: []byte(pb.ProtoReflect().Descriptor().FullName())}}, + Time: time.Time{}, + } +}