Merge pull request 'feature/84-character-creation' (#88) from feature/84-character-creation into master
All checks were successful
Build documentation / preprocess (push) Successful in 2s
Build, Package and Push Images / preprocess (push) Successful in 2s
Build documentation / docs (push) Successful in 21s
Build, Package and Push Images / build (push) Successful in 24s
Build, Package and Push Images / sbom-scan (push) Successful in 41s
Build documentation / build-docs-container (push) Successful in 46s
Build documentation / deploy-wiki (push) Successful in 6s
Build, Package and Push Images / container-build (push) Successful in 1m47s
Build, Package and Push Images / sonarqube (push) Successful in 1m50s
Build, Package and Push Images / container-sbom-scan (push) Successful in 37s
All checks were successful
Build documentation / preprocess (push) Successful in 2s
Build, Package and Push Images / preprocess (push) Successful in 2s
Build documentation / docs (push) Successful in 21s
Build, Package and Push Images / build (push) Successful in 24s
Build, Package and Push Images / sbom-scan (push) Successful in 41s
Build documentation / build-docs-container (push) Successful in 46s
Build documentation / deploy-wiki (push) Successful in 6s
Build, Package and Push Images / container-build (push) Successful in 1m47s
Build, Package and Push Images / sonarqube (push) Successful in 1m50s
Build, Package and Push Images / container-sbom-scan (push) Successful in 37s
Reviewed-on: #88
This commit is contained in:
commit
17a2a49cfd
61 changed files with 2807 additions and 559 deletions
124
.gitea/workflows/docs.yaml
Normal file
124
.gitea/workflows/docs.yaml
Normal file
|
@ -0,0 +1,124 @@
|
|||
name: Build documentation
|
||||
run-name: ${{ gitea.actor }} is building the Wiki documentation
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- Wiki/**
|
||||
- Wiki.Dockerfile
|
||||
|
||||
env:
|
||||
# Name of module and id separated by a slash
|
||||
INSTANCE: Wiki/wiki
|
||||
# Replace HI with the ID of the instance in capital letters
|
||||
ARTIFACT: webHelpWIKI2-all.zip
|
||||
# Writerside docker image version
|
||||
DOCKER_VERSION: 232.10165.1
|
||||
ALGOLIA_ARTIFACT: algolia-indexes-wiki.zip
|
||||
|
||||
|
||||
jobs:
|
||||
preprocess:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
sanitized_branch_name: ${{ steps.sanitize.outputs.sanitized_branch_name }}
|
||||
steps:
|
||||
- name: Sanitize branch name
|
||||
id: sanitize
|
||||
run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')"
|
||||
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}}
|
||||
steps:
|
||||
- name: Install basic dependencies
|
||||
run: |
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
|
||||
echo "::add-path::$HOME/.nvm"
|
||||
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
nvm install 18
|
||||
nvm use 18
|
||||
echo "::add-path::$(dirname $(which npm))"
|
||||
nvm --version
|
||||
- name: Check Node.js version
|
||||
run: |
|
||||
node -v
|
||||
npm -v
|
||||
- name: Checkout repository
|
||||
uses: https://github.com/actions/checkout@v3
|
||||
- name: Build docs
|
||||
run: |
|
||||
set -e
|
||||
export DISPLAY=:99
|
||||
Xvfb :99 &
|
||||
/opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true
|
||||
echo "Test existing of ${{ env.ARTIFACT }} artifact"
|
||||
test -e artifacts/${{ env.ARTIFACT }}
|
||||
- name: rename artifact
|
||||
run: |
|
||||
mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip
|
||||
- name: Upload documentation
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wiki.zip
|
||||
path: artifacts/wiki.zip
|
||||
retention-days: 14
|
||||
- name: Upload algolia-indexes
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: algolia-indexes.zip
|
||||
path: artifacts/${{ env.ALGOLIA_ARTIFACT }}
|
||||
retention-days: 14
|
||||
|
||||
build-docs-container:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
needs: [docs, preprocess]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: https://github.com/actions/checkout@v3
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ github.server_url }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Retrieve docs artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: wiki.zip
|
||||
path: ${{ github.workspace }}
|
||||
- name: Unzip wiki.zip into .public
|
||||
run: |
|
||||
mkdir .public
|
||||
unzip -jo -qq ./wiki.zip/wiki.zip -d .public
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Wiki.Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Build and push to latest
|
||||
if: github.ref_name == 'master'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Wiki.Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:latest-wiki
|
||||
platforms: linux/amd64, linux/arm64
|
||||
|
||||
deploy-wiki:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
needs: [build-docs-container, docs, preprocess]
|
||||
steps:
|
||||
- name: Deploy Image to CapRrover
|
||||
run: |
|
||||
docker run caprover/cli-caprover:2.2.3 caprover deploy --caproverUrl ${{ secrets.CAPROVER_SERVER }} --appToken ${{ secrets.WIKI_APP_TOKEN }} --imageName forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}-wiki -a ${{ secrets.WIKI_APP_NAME }}
|
|
@ -1,16 +1,11 @@
|
|||
name: Build, Package and Push Images
|
||||
name: Build, Package and Push Images
|
||||
run-name: ${{ gitea.actor }} is building the Server application
|
||||
on: [ push ]
|
||||
|
||||
env:
|
||||
# Name of module and id separated by a slash
|
||||
INSTANCE: Wiki/wiki
|
||||
# Replace HI with the ID of the instance in capital letters
|
||||
ARTIFACT: webHelpWIKI2-all.zip
|
||||
# Writerside docker image version
|
||||
DOCKER_VERSION: 232.10165.1
|
||||
ALGOLIA_ARTIFACT: algolia-indexes-wiki.zip
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- Wiki/**
|
||||
- Benchmarks/**
|
||||
- .run/**
|
||||
|
||||
jobs:
|
||||
preprocess:
|
||||
|
@ -18,217 +13,167 @@ jobs:
|
|||
outputs:
|
||||
sanitized_branch_name: ${{ steps.sanitize.outputs.sanitized_branch_name }}
|
||||
steps:
|
||||
- name: Sanitize branch name
|
||||
id: sanitize
|
||||
run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')"
|
||||
|
||||
# docs:
|
||||
# runs-on: ubuntu-latest
|
||||
# container: registry.jetbrains.team/p/writerside/builder/writerside-builder:${{env.DOCKER_VERSION}}
|
||||
# steps:
|
||||
# - name: Install basic dependencies
|
||||
# run: |
|
||||
# wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
|
||||
# echo "::add-path::$HOME/.nvm"
|
||||
# export PATH="$HOME/.nvm:$PATH"
|
||||
# export NVM_DIR="$HOME/.nvm"
|
||||
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
# echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> ~/.bashrc
|
||||
# echo "$HOME/.nvm" >> $GITHUB_PATH
|
||||
# nvm --version
|
||||
# - name: Install Node
|
||||
# run: |
|
||||
# echo $PATH
|
||||
# echo $GITHUB_PATH
|
||||
# cat $GITHUB_PATH
|
||||
# echo $NVM_DIR
|
||||
# ls -la $HOME/.nvm
|
||||
# nvm install 18
|
||||
# nvm use 18
|
||||
# node -v
|
||||
# - name: Checkout repository
|
||||
# uses: https://github.com/actions/checkout@v3
|
||||
# - name: Build docs
|
||||
# run: |
|
||||
# set -e
|
||||
# export DISPLAY=:99
|
||||
# Xvfb :99 &
|
||||
# /opt/builder/bin/idea.sh helpbuilderinspect -source-dir . -product ${{env.INSTANCE}} -output-dir artifacts/ || true
|
||||
# echo "Test existing of ${{ env.ARTIFACT }} artifact"
|
||||
# test -e artifacts/${{ env.ARTIFACT }}
|
||||
# - name: rename artifact
|
||||
# run: |
|
||||
# mv artifacts/${{ env.ARTIFACT }} artifacts/wiki.zip
|
||||
# - name: Upload documentation
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: docs
|
||||
# path: artifacts/wiki.zip
|
||||
# retention-days: 14
|
||||
# - name: Upload algolia-indexes
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: algolia-indexes
|
||||
# path: artifacts/${{ env.ALGOLIA_ARTIFACT }}
|
||||
# retention-days: 14
|
||||
- name: Sanitize branch name
|
||||
id: sanitize
|
||||
run: echo "::set-output name=sanitized_branch_name::$(echo ${{ github.ref_name }} | sed 's/\//-/g')"
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: |
|
||||
dotnet build Server -c Release
|
||||
# dotnet test Server.Tests -c Release
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
run: |
|
||||
dotnet build Server -c Release
|
||||
# dotnet test Server.Tests -c Release
|
||||
|
||||
sonarqube:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref_name == 'master'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Sonarqube Dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install --yes openjdk-11-jre
|
||||
dotnet tool install --global dotnet-sonarscanner
|
||||
dotnet tool install --global dotnet-coverage
|
||||
- name: Sonarqube Begin
|
||||
run: |
|
||||
dotnet sonarscanner begin /key:"${{ secrets.SONAR_PROJECT_KEY }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}"
|
||||
- name: Sonarqube Scan
|
||||
run: |
|
||||
dotnet build Server -c Release
|
||||
# dotnet test --collect "Code Coverage" --logger trx --results-directory "TestsResults"
|
||||
# dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml'
|
||||
- name: Sonarqube End
|
||||
run: |
|
||||
dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Sonarqube Dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install --yes openjdk-11-jre
|
||||
dotnet tool install --global dotnet-sonarscanner
|
||||
dotnet tool install --global dotnet-coverage
|
||||
- name: Sonarqube Begin
|
||||
run: |
|
||||
dotnet sonarscanner begin /key:"${{ secrets.SONAR_PROJECT_KEY }}" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="${{ secrets.SONAR_HOST_URL }}"
|
||||
- name: Sonarqube Scan
|
||||
run: |
|
||||
dotnet build Server -c Release
|
||||
# dotnet test --collect "Code Coverage" --logger trx --results-directory "TestsResults"
|
||||
# dotnet-coverage collect 'dotnet test' -f xml -o 'coverage.xml'
|
||||
- name: Sonarqube End
|
||||
run: |
|
||||
dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"
|
||||
|
||||
sbom-scan:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
7.0
|
||||
8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Dependency Track Dependencies
|
||||
run: |
|
||||
dotnet tool install --global CycloneDX
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
dotnet CycloneDX Server/Server.csproj -o . -dgl
|
||||
- name: Upload SBOM
|
||||
uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1
|
||||
with:
|
||||
apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }}
|
||||
serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }}
|
||||
projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}
|
||||
autoCreate: true
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: |
|
||||
7.0
|
||||
8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Dependency Track Dependencies
|
||||
run: |
|
||||
dotnet tool install --global CycloneDX
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
dotnet CycloneDX Server/Server.csproj -o . -dgl
|
||||
- name: Upload SBOM
|
||||
uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1
|
||||
with:
|
||||
apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }}
|
||||
serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }}
|
||||
projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}
|
||||
autoCreate: true
|
||||
# set projectversion to be the branch name
|
||||
projectVersion: "${{ github.ref_name }}"
|
||||
bomFilename: "${{ github.workspace }}/bom.xml"
|
||||
projectVersion: ${{ github.ref_name }}
|
||||
bomFilename: ${{ github.workspace }}/bom.xml
|
||||
|
||||
container-build:
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
needs: [ build, preprocess ]
|
||||
needs: [build, preprocess]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
# Add support for more platforms with QEMU (optional)
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ github.server_url }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Server/Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Build and push to latest
|
||||
if: github.ref_name == 'master'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Server/Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:latest
|
||||
platforms: linux/amd64, linux/arm64
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
# Add support for more platforms with QEMU (optional)
|
||||
# https://github.com/docker/setup-qemu-action
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ github.server_url }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Server/Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
platforms: linux/amd64,linux/arm64
|
||||
- name: Build and push to latest
|
||||
if: github.ref_name == 'master'
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: Server/Dockerfile
|
||||
push: true
|
||||
tags: forge.rainote.dev/${{ github.repository }}:latest
|
||||
platforms: linux/amd64, linux/arm64
|
||||
|
||||
container-sbom-scan:
|
||||
needs: [ container-build, preprocess ]
|
||||
needs: [container-build, preprocess]
|
||||
runs-on: ubuntu-latest
|
||||
container: catthehacker/ubuntu:act-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Dependency Track Dependencies
|
||||
run: |
|
||||
mkdir ~/.docker
|
||||
curl -sSfL https://raw.githubusercontent.com/docker/sbom-cli-plugin/main/install.sh | sh -s --
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ github.server_url }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
echo forge.rainote.dev/${{ github.repository }}
|
||||
echo forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
docker pull forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
docker sbom -D forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} --format cyclonedx-json --output container-bom.json
|
||||
- name: Upload SBOM
|
||||
uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1
|
||||
with:
|
||||
apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }}
|
||||
serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }}
|
||||
projectName: "${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}-container"
|
||||
autoCreate: true
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: https://github.com/actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
dotnet restore
|
||||
echo "::add-path::$HOME/.dotnet/tools"
|
||||
- name: Setup Dependency Track Dependencies
|
||||
run: |
|
||||
mkdir ~/.docker
|
||||
curl -sSfL https://raw.githubusercontent.com/docker/sbom-cli-plugin/main/install.sh | sh -s --
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ github.server_url }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
echo forge.rainote.dev/${{ github.repository }}
|
||||
echo forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
docker pull forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }}
|
||||
docker sbom -D forge.rainote.dev/${{ github.repository }}:${{ needs.preprocess.outputs.sanitized_branch_name }} --format cyclonedx-json --output container-bom.json
|
||||
- name: Upload SBOM
|
||||
uses: https://github.com/DependencyTrack/gh-upload-sbom@v2.0.1
|
||||
with:
|
||||
apiKey: ${{ secrets.DEPENDENCY_TRACK_API_KEY }}
|
||||
serverHostname: ${{ secrets.DEPENDENCY_TRACK_URL }}
|
||||
projectName: ${{ secrets.DEPENDENCY_TRACK_PROJECT_NAME }}-container
|
||||
autoCreate: true
|
||||
# set projectversion to be the branch name
|
||||
projectVersion: "${{ github.ref_name }}"
|
||||
bomFilename: "${{ github.workspace }}/container-bom.json"
|
||||
projectVersion: ${{ github.ref_name }}
|
||||
bomFilename: ${{ github.workspace }}/container-bom.json
|
||||
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
#Use dotnet format already installed on your machine
|
||||
- id: dotnet-format
|
||||
name: dotnet-format
|
||||
language: system
|
||||
entry: dotnet format --include
|
||||
types_or: ["c#", "vb"]
|
||||
- id: dotnet-format
|
||||
name: dotnet-format
|
||||
language: system
|
||||
entry: dotnet format --include
|
||||
types_or: [c#, vb]
|
||||
- repo: https://github.com/Mateusz-Grzelinski/actionlint-py
|
||||
rev: v1.6.26.11
|
||||
hooks:
|
||||
- id: actionlint
|
||||
additional_dependencies: [pyflakes>=3.0.1, shellcheck-py>=0.9.0.5]
|
||||
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
|
||||
rev: v2.11.0
|
||||
hooks:
|
||||
- id: pretty-format-yaml
|
||||
args: [--autofix, --indent, '2']
|
||||
- repo: https://github.com/hadolint/hadolint
|
||||
rev: v2.12.0
|
||||
hooks:
|
||||
- id: hadolint-docker
|
||||
args: [--ignore, SC2086]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Net.Sockets;
|
||||
using Wonderking.Packets;
|
||||
|
||||
namespace Server;
|
||||
|
@ -31,7 +32,7 @@ public class AuthSession : TcpSession
|
|||
public void Send(IPacket packet)
|
||||
{
|
||||
var type = packet.GetType();
|
||||
this._logger.LogTrace("Packet of type {Type} is being serialized", type.Name);
|
||||
this._logger.LogInformation("Packet of type {Type} is being serialized", type.Name);
|
||||
var packetIdAttribute = type.GetCustomAttribute<PacketIdAttribute>();
|
||||
if (packetIdAttribute == null)
|
||||
{
|
||||
|
@ -59,8 +60,8 @@ public class AuthSession : TcpSession
|
|||
buffer[2 + i] = bytesOfOpcode[i];
|
||||
}
|
||||
|
||||
this._logger.LogTrace("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
||||
this._logger.LogTrace("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
||||
this._logger.LogInformation("Packet data being parsed is: {Data}", BitConverter.ToString(packetData.ToArray()));
|
||||
this._logger.LogInformation("Packet being parsed is: {Data}", BitConverter.ToString(buffer.ToArray()));
|
||||
|
||||
this.Send(buffer);
|
||||
}
|
||||
|
@ -105,4 +106,9 @@ public class AuthSession : TcpSession
|
|||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
protected override void OnError(SocketError error)
|
||||
{
|
||||
_logger.LogWarning("An error has occured: {Error}", error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
[Index(nameof(Username), IsUnique = true), Index(nameof(Id), IsUnique = true)]
|
||||
public class Account
|
||||
{
|
||||
public Account(string username, byte[] password, string email, byte permissionLevel, byte[] salt)
|
||||
|
@ -11,12 +16,18 @@ public class Account
|
|||
this.Salt = salt;
|
||||
}
|
||||
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[Column(TypeName = "varchar(20)")]
|
||||
[MaxLength(20)]
|
||||
public string Username { get; set; }
|
||||
public byte[] Password { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
[Column(TypeName = "bytea")] public byte[] Password { get; set; }
|
||||
|
||||
[EmailAddress] public string Email { get; set; }
|
||||
public byte PermissionLevel { get; set; }
|
||||
public byte[] Salt { get; set; }
|
||||
public ICollection<Character> Characters { get; } = new List<Character>();
|
||||
[Column(TypeName = "bytea")] public byte[] Salt { get; set; }
|
||||
public virtual ICollection<Character> Characters { get; } = new List<Character>();
|
||||
}
|
||||
|
|
|
@ -1,29 +1,38 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Wonderking.Game.Data.Character;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
[Index(nameof(Name), IsUnique = true), Index(nameof(Id), IsUnique = true)]
|
||||
public class Character
|
||||
{
|
||||
public byte ServerId { get; set; }
|
||||
public Guid AccountId { get; set; }
|
||||
public Account Account { get; set; }
|
||||
public virtual Account Account { get; set; }
|
||||
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public ushort MapId { get; set; }
|
||||
|
||||
[Column(TypeName = "varchar(20)")]
|
||||
[MaxLength(20)]
|
||||
public string Name { get; set; }
|
||||
|
||||
public short LastXCoordinate { get; set; }
|
||||
public short LastYCoordinate { get; set; }
|
||||
public PvPLevel PvPLevel { get; set; }
|
||||
public Gender Gender { get; set; }
|
||||
public long Experience { get; set; }
|
||||
public byte Level { get; set; }
|
||||
public ICollection<InventoryItem> InventoryItems { get; set; }
|
||||
public virtual ICollection<InventoryItem> InventoryItems { get; set; }
|
||||
|
||||
public BaseStats BaseStats { get; set; }
|
||||
|
||||
public JobData JobData { get; set; }
|
||||
public int Health { get; set; }
|
||||
public int Mana { get; set; }
|
||||
public Guid GuildId { get; set; }
|
||||
public Guild Guild { get; set; }
|
||||
public virtual Guild Guild { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
public class Guild
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Notice { get; set; }
|
||||
public ICollection<GuildMember> GuildMembers { get; set; }
|
||||
public virtual ICollection<GuildMember> GuildMembers { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Server.DB.Documents;
|
||||
|
||||
public class GuildMember
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
public Guid CharacterId { get; set; }
|
||||
public Character Character { get; set; }
|
||||
public Guid GuildId { get; set; }
|
||||
public Guild Guild { get; set; }
|
||||
|
||||
public virtual Character Character { get; set; }
|
||||
public virtual Guild Guild { get; set; }
|
||||
public GuildRank Rank { get; set; }
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ namespace Server.DB.Documents;
|
|||
public class InventoryItem
|
||||
{
|
||||
public Guid CharacterId { get; set; }
|
||||
public Character Character { get; set; }
|
||||
public virtual Character Character { get; set; }
|
||||
public Guid Id { get; set; }
|
||||
public ushort ItemId { get; set; }
|
||||
public ushort Count { get; set; }
|
||||
public byte Slot { get; set; }
|
||||
public ItemType ItemType { get; set; }
|
||||
public InventoryTab InventoryTab { get; set; }
|
||||
public byte Level { get; set; }
|
||||
public byte Rarity { get; set; }
|
||||
public byte AddOption { get; set; }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
namespace Server.DB.Documents;
|
||||
|
||||
public enum ItemType : byte
|
||||
public enum InventoryTab : byte
|
||||
{
|
||||
WornEquipment = 0,
|
||||
WornCashEquipment = 1,
|
333
Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs
generated
Normal file
333
Server/DB/Migrations/20231115174714_GuildIsNotRequired.Designer.cs
generated
Normal file
|
@ -0,0 +1,333 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Server.DB;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations
|
||||
{
|
||||
[DbContext(typeof(WonderkingContext))]
|
||||
[Migration("20231115174714_GuildIsNotRequired")]
|
||||
partial class GuildIsNotRequired
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte[]>("Password")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<byte>("PermissionLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte[]>("Salt")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("AccountId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("Experience")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<byte>("Gender")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Health")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<short>("LastXCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("LastYCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("Mana")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<byte>("PvPLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("ServerId")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccountId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Notice")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("Rank")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.ToTable("GuildMember");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("AddOption")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("InventoryTab")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Rarity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Slot")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.ToTable("InventoryItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany()
|
||||
.HasForeignKey("GuildId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<short>("Dexterity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Intelligence")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Luck")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Strength")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Vitality")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Wisdom")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<byte>("FirstJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("FourthJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("SecondJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("ThirdJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.Navigation("Account");
|
||||
|
||||
b.Navigation("BaseStats");
|
||||
|
||||
b.Navigation("Guild");
|
||||
|
||||
b.Navigation("JobData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany("GuildMembers")
|
||||
.HasForeignKey("GuildId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Character");
|
||||
|
||||
b.Navigation("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany("InventoryItems")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Character");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Navigation("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Navigation("InventoryItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Navigation("GuildMembers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
27
Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs
Normal file
27
Server/DB/Migrations/20231115174714_GuildIsNotRequired.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class GuildIsNotRequired : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "ItemType",
|
||||
table: "InventoryItem",
|
||||
newName: "InventoryTab");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.RenameColumn(
|
||||
name: "InventoryTab",
|
||||
table: "InventoryItem",
|
||||
newName: "ItemType");
|
||||
}
|
||||
}
|
322
Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs
generated
Normal file
322
Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.Designer.cs
generated
Normal file
|
@ -0,0 +1,322 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Server.DB;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations
|
||||
{
|
||||
[DbContext(typeof(WonderkingContext))]
|
||||
[Migration("20231115183824_SwitchToDataAnnotations")]
|
||||
partial class SwitchToDataAnnotations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte[]>("Password")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<byte>("PermissionLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte[]>("Salt")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("AccountId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("Experience")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<byte>("Gender")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Health")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<short>("LastXCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("LastYCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("Mana")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte>("PvPLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccountId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Notice")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("Rank")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.ToTable("GuildMember");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("AddOption")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("InventoryTab")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Rarity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Slot")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.ToTable("InventoryItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("AccountId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany()
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<short>("Dexterity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Intelligence")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Luck")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Strength")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Vitality")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Wisdom")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<byte>("FirstJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("FourthJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("SecondJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("ThirdJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.Navigation("Account");
|
||||
|
||||
b.Navigation("BaseStats");
|
||||
|
||||
b.Navigation("Guild");
|
||||
|
||||
b.Navigation("JobData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany("GuildMembers")
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.Navigation("Character");
|
||||
|
||||
b.Navigation("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany("InventoryItems")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Character");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Navigation("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Navigation("InventoryItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Navigation("GuildMembers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
230
Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs
Normal file
230
Server/DB/Migrations/20231115183824_SwitchToDataAnnotations.cs
Normal file
|
@ -0,0 +1,230 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class SwitchToDataAnnotations : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Characters_Accounts_AccountId",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Characters_Guild_GuildId",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GuildMember_Characters_CharacterId",
|
||||
table: "GuildMember");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GuildMember_Guild_GuildId",
|
||||
table: "GuildMember");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Characters_Name",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerId",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GuildId",
|
||||
table: "GuildMember",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "CharacterId",
|
||||
table: "GuildMember",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Characters",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(20)",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GuildId",
|
||||
table: "Characters",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "AccountId",
|
||||
table: "Characters",
|
||||
type: "uuid",
|
||||
nullable: true,
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Accounts_Id",
|
||||
table: "Accounts",
|
||||
column: "Id",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Characters_Accounts_AccountId",
|
||||
table: "Characters",
|
||||
column: "AccountId",
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Characters_Guild_GuildId",
|
||||
table: "Characters",
|
||||
column: "GuildId",
|
||||
principalTable: "Guild",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GuildMember_Characters_CharacterId",
|
||||
table: "GuildMember",
|
||||
column: "CharacterId",
|
||||
principalTable: "Characters",
|
||||
principalColumn: "Id");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GuildMember_Guild_GuildId",
|
||||
table: "GuildMember",
|
||||
column: "GuildId",
|
||||
principalTable: "Guild",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Characters_Accounts_AccountId",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Characters_Guild_GuildId",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GuildMember_Characters_CharacterId",
|
||||
table: "GuildMember");
|
||||
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_GuildMember_Guild_GuildId",
|
||||
table: "GuildMember");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Accounts_Id",
|
||||
table: "Accounts");
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GuildId",
|
||||
table: "GuildMember",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "CharacterId",
|
||||
table: "GuildMember",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Characters",
|
||||
type: "varchar(20)",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "GuildId",
|
||||
table: "Characters",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AlterColumn<Guid>(
|
||||
name: "AccountId",
|
||||
table: "Characters",
|
||||
type: "uuid",
|
||||
nullable: false,
|
||||
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"),
|
||||
oldClrType: typeof(Guid),
|
||||
oldType: "uuid",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<byte>(
|
||||
name: "ServerId",
|
||||
table: "Characters",
|
||||
type: "smallint",
|
||||
nullable: false,
|
||||
defaultValue: (byte)0);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Characters_Name",
|
||||
table: "Characters",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Characters_Accounts_AccountId",
|
||||
table: "Characters",
|
||||
column: "AccountId",
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Characters_Guild_GuildId",
|
||||
table: "Characters",
|
||||
column: "GuildId",
|
||||
principalTable: "Guild",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GuildMember_Characters_CharacterId",
|
||||
table: "GuildMember",
|
||||
column: "CharacterId",
|
||||
principalTable: "Characters",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_GuildMember_Guild_GuildId",
|
||||
table: "GuildMember",
|
||||
column: "GuildId",
|
||||
principalTable: "Guild",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
}
|
||||
}
|
333
Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs
generated
Normal file
333
Server/DB/Migrations/20231116110504_DBPoolingAndLazyLoadingSupport.Designer.cs
generated
Normal file
|
@ -0,0 +1,333 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
using Server.DB;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations
|
||||
{
|
||||
[DbContext(typeof(WonderkingContext))]
|
||||
[Migration("20231116110504_DBPoolingAndLazyLoadingSupport")]
|
||||
partial class DBPoolingAndLazyLoadingSupport
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<byte[]>("Password")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<byte>("PermissionLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte[]>("Salt")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("AccountId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("Experience")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<byte>("Gender")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Health")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<short>("LastXCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("LastYCoordinate")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("Mana")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("MapId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<byte>("PvPLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccountId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Notice")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("Rank")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.ToTable("GuildMember");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("AddOption")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("AddOption3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("InventoryTab")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option2")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<short>("Option3")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Rarity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Slot")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CharacterId");
|
||||
|
||||
b.ToTable("InventoryItem");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("AccountId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany()
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<short>("Dexterity")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Intelligence")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Luck")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Strength")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Vitality")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<short>("Wisdom")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.JobData", "JobData", b1 =>
|
||||
{
|
||||
b1.Property<Guid>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b1.Property<byte>("FirstJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("FourthJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("SecondJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.Property<byte>("ThirdJob")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b1.HasKey("CharacterId");
|
||||
|
||||
b1.ToTable("Characters");
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("CharacterId");
|
||||
});
|
||||
|
||||
b.Navigation("Account");
|
||||
|
||||
b.Navigation("BaseStats");
|
||||
|
||||
b.Navigation("Guild");
|
||||
|
||||
b.Navigation("JobData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.GuildMember", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany("GuildMembers")
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.Navigation("Character");
|
||||
|
||||
b.Navigation("Guild");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.InventoryItem", b =>
|
||||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany("InventoryItems")
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Character");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Account", b =>
|
||||
{
|
||||
b.Navigation("Characters");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Character", b =>
|
||||
{
|
||||
b.Navigation("InventoryItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Server.DB.Documents.Guild", b =>
|
||||
{
|
||||
b.Navigation("GuildMembers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Server.DB.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class DBPoolingAndLazyLoadingSupport : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Characters",
|
||||
type: "varchar(20)",
|
||||
maxLength: 20,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "text",
|
||||
oldNullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Characters_Id",
|
||||
table: "Characters",
|
||||
column: "Id",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Characters_Name",
|
||||
table: "Characters",
|
||||
column: "Name",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Characters_Id",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Characters_Name",
|
||||
table: "Characters");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Name",
|
||||
table: "Characters",
|
||||
type: "text",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(20)",
|
||||
oldMaxLength: 20,
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,9 @@ namespace Server.DB.Migrations
|
|||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.0")
|
||||
.HasAnnotation("Proxies:ChangeTracking", false)
|
||||
.HasAnnotation("Proxies:CheckEquality", false)
|
||||
.HasAnnotation("Proxies:LazyLoading", true)
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
@ -41,10 +44,14 @@ namespace Server.DB.Migrations
|
|||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
|
@ -57,7 +64,7 @@ namespace Server.DB.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("AccountId")
|
||||
b.Property<Guid?>("AccountId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("Experience")
|
||||
|
@ -66,7 +73,7 @@ namespace Server.DB.Migrations
|
|||
b.Property<byte>("Gender")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<Guid>("GuildId")
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Health")
|
||||
|
@ -88,20 +95,21 @@ namespace Server.DB.Migrations
|
|||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(20)
|
||||
.HasColumnType("varchar(20)");
|
||||
|
||||
b.Property<byte>("PvPLevel")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("ServerId")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AccountId");
|
||||
|
||||
b.HasIndex("GuildId");
|
||||
|
||||
b.HasIndex("Id")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("Name")
|
||||
.IsUnique();
|
||||
|
||||
|
@ -131,10 +139,10 @@ namespace Server.DB.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("CharacterId")
|
||||
b.Property<Guid?>("CharacterId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("GuildId")
|
||||
b.Property<Guid?>("GuildId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte>("Rank")
|
||||
|
@ -170,12 +178,12 @@ namespace Server.DB.Migrations
|
|||
b.Property<int>("Count")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("InventoryTab")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<byte>("ItemType")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
b.Property<byte>("Level")
|
||||
.HasColumnType("smallint");
|
||||
|
||||
|
@ -205,15 +213,11 @@ namespace Server.DB.Migrations
|
|||
{
|
||||
b.HasOne("Server.DB.Documents.Account", "Account")
|
||||
.WithMany("Characters")
|
||||
.HasForeignKey("AccountId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("AccountId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany()
|
||||
.HasForeignKey("GuildId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.OwnsOne("Wonderking.Packets.Outgoing.Data.BaseStats", "BaseStats", b1 =>
|
||||
{
|
||||
|
@ -284,15 +288,11 @@ namespace Server.DB.Migrations
|
|||
{
|
||||
b.HasOne("Server.DB.Documents.Character", "Character")
|
||||
.WithMany()
|
||||
.HasForeignKey("CharacterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("CharacterId");
|
||||
|
||||
b.HasOne("Server.DB.Documents.Guild", "Guild")
|
||||
.WithMany("GuildMembers")
|
||||
.HasForeignKey("GuildId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
.HasForeignKey("GuildId");
|
||||
|
||||
b.Navigation("Character");
|
||||
|
||||
|
|
|
@ -1,53 +1,16 @@
|
|||
using JetBrains.Annotations;
|
||||
|
||||
namespace Server.DB;
|
||||
|
||||
using Documents;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class WonderkingContext : DbContext
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
public WonderkingContext(ILoggerFactory loggerFactory, IConfiguration configuration)
|
||||
public WonderkingContext([NotNull] DbContextOptions options) : base(options)
|
||||
{
|
||||
this._loggerFactory = loggerFactory;
|
||||
this._configuration = configuration;
|
||||
}
|
||||
|
||||
public DbSet<Account> Accounts { get; set; }
|
||||
public DbSet<Character> Characters { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
|
||||
optionsBuilder
|
||||
.UseNpgsql(
|
||||
$"Host={this._configuration["DB:Host"]};Username={this._configuration["DB:Username"]};Password={this._configuration["DB:Password"]};Database={this._configuration["DB:Database"]};Port={this._configuration["DB:Port"]}")
|
||||
.EnableSensitiveDataLogging().UseLoggerFactory(this._loggerFactory);
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder) =>
|
||||
modelBuilder.Entity<Account>(builder =>
|
||||
{
|
||||
builder.Property(b => b.Username).HasColumnType("varchar(20)");
|
||||
builder.HasIndex(b => b.Username).IsUnique();
|
||||
builder.Property(b => b.Password).HasColumnType("bytea");
|
||||
builder.Property(b => b.Salt).HasColumnType("bytea");
|
||||
builder.HasKey(b => b.Id);
|
||||
builder.HasMany(e => e.Characters).WithOne(e => e.Account).HasForeignKey(e => e.AccountId)
|
||||
.IsRequired();
|
||||
}).Entity<Character>(builder =>
|
||||
{
|
||||
builder.HasKey(c => c.Id);
|
||||
builder.Property(c => c.Name).HasColumnType("varchar(20)");
|
||||
builder.HasIndex(c => c.Name).IsUnique();
|
||||
builder.HasMany(e => e.InventoryItems).WithOne(e => e.Character)
|
||||
.HasForeignKey(e => e.CharacterId).IsRequired();
|
||||
builder.OwnsOne(p => p.BaseStats);
|
||||
builder.OwnsOne(p => p.JobData);
|
||||
}).Entity<InventoryItem>(builder => { builder.HasKey(i => i.Id); }).Entity<Guild>(builder =>
|
||||
{
|
||||
builder.HasKey(g => g.Id);
|
||||
builder.HasMany(g => g.GuildMembers).WithOne(g => g.Guild).HasForeignKey(g => g.GuildId)
|
||||
.IsRequired();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,17 +7,16 @@ ENV TZ=Etc/UTC
|
|||
ENV DOTNET_TieredPGO=1
|
||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
|
||||
RUN echo "Target: $TARGETARCH"
|
||||
RUN echo "Build: $BUILDPLATFORM"
|
||||
RUN echo "Target: $TARGETARCH" && echo "Build: $BUILDPLATFORM"
|
||||
WORKDIR /src
|
||||
COPY ["Wonderking/Wonderking.csproj", "Wonderking/"]
|
||||
COPY ["Server/Server.csproj", "Server/"]
|
||||
RUN dotnet restore "Wonderking/Wonderking.csproj" -a $TARGETARCH
|
||||
RUN dotnet restore "Server/Server.csproj" -a $TARGETARCH
|
||||
RUN dotnet restore "Wonderking/Wonderking.csproj" -a $TARGETARCH && dotnet restore "Server/Server.csproj" -a $TARGETARCH
|
||||
COPY . .
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "Server/Server.csproj" -c Release -a $TARGETARCH --no-restore -f net8.0 -o /app
|
||||
COPY ../config /app/config
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Server.DB.Documents;
|
||||
using Wonderking.Game.Data.Character;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
@ -26,27 +25,23 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
|||
this._wonderkingContext = wonderkingContext;
|
||||
}
|
||||
|
||||
public ChannelSelectionHandler()
|
||||
{
|
||||
}
|
||||
|
||||
public Task HandleAsync(ChannelSelectionPacket packet, TcpSession session)
|
||||
public async Task HandleAsync(ChannelSelectionPacket packet, TcpSession session)
|
||||
{
|
||||
var authSession = (AuthSession)session;
|
||||
ChannelSelectionResponsePacket responsePacket;
|
||||
CharacterSelectionSetGuildNamePacket guildNameResponsePacket;
|
||||
var guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket { GuildNames = Array.Empty<string>() };
|
||||
|
||||
var hasCharacters = this._wonderkingContext.Accounts.Include(account => account.Characters)
|
||||
.FirstOrDefault(a => a.Id == authSession.AccountId)?.Characters.Count > 0;
|
||||
var testingChars = false;
|
||||
if (hasCharacters && !testingChars)
|
||||
var account = await this._wonderkingContext.Accounts
|
||||
.FirstOrDefaultAsync(a => a.Id == authSession.AccountId).ConfigureAwait(true);
|
||||
if (account != null && account.Characters.Count > 0)
|
||||
{
|
||||
responsePacket = new ChannelSelectionResponsePacket
|
||||
{
|
||||
ChannelIsFullFlag = 0,
|
||||
Endpoint = "127.0.0.1",
|
||||
Port = 12345,
|
||||
Characters = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId)
|
||||
Characters = await _wonderkingContext.Characters.AsNoTracking()
|
||||
.Where(c => c.Account.Id == authSession.AccountId)
|
||||
.Select(c =>
|
||||
new CharacterData
|
||||
{
|
||||
|
@ -54,127 +49,43 @@ public class ChannelSelectionHandler : IPacketHandler<ChannelSelectionPacket>
|
|||
Job = c.JobData,
|
||||
Gender = c.Gender,
|
||||
Level = c.Level,
|
||||
Experience = 0,
|
||||
// TODO: Calculate instead of clamping based on max experience for level
|
||||
Experience = Math.Clamp(c.Experience, 0, 100),
|
||||
Stats = c.BaseStats,
|
||||
Health = c.Health,
|
||||
Mana = c.Mana,
|
||||
EquippedItems =
|
||||
c.InventoryItems.Where(item => item.ItemType == ItemType.WornEquipment)
|
||||
c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment)
|
||||
.Select(item => item.ItemId)
|
||||
.ToArray(),
|
||||
EquippedCashItems = c.InventoryItems
|
||||
.Where(item => item.ItemType == ItemType.WornCashEquipment)
|
||||
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
|
||||
.Select(item => item.ItemId)
|
||||
.ToArray(),
|
||||
.ToArray()
|
||||
})
|
||||
.ToArray(),
|
||||
.ToArrayAsync().ConfigureAwait(true),
|
||||
};
|
||||
|
||||
guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket
|
||||
{
|
||||
GuildNames = this._wonderkingContext.Characters.Where(c => c.AccountId == authSession.AccountId)
|
||||
.Select(character => character.Guild.Name).ToArray()
|
||||
};
|
||||
guildNameResponsePacket.GuildNames = await _wonderkingContext.Characters
|
||||
.Where(c => c.Account.Id == authSession.AccountId)
|
||||
.Select(character => character.Guild.Name).ToArrayAsync().ConfigureAwait(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
responsePacket = testingChars
|
||||
? CreateTestChannelSelectionResponsePacket()
|
||||
: new ChannelSelectionResponsePacket
|
||||
{
|
||||
ChannelIsFullFlag = 0,
|
||||
Endpoint = "127.0.0.1",
|
||||
Port = 12345,
|
||||
Characters = Array.Empty<CharacterData>()
|
||||
};
|
||||
guildNameResponsePacket = new CharacterSelectionSetGuildNamePacket
|
||||
responsePacket = new ChannelSelectionResponsePacket
|
||||
{
|
||||
GuildNames = new[] { "ABCDEFGHIJKLMNOP", "QRSTUVWXYZ123456", "A Guild Name For" }
|
||||
ChannelIsFullFlag = 0,
|
||||
Endpoint = "127.0.0.1",
|
||||
Port = 2000,
|
||||
Characters = Array.Empty<CharacterData>()
|
||||
};
|
||||
}
|
||||
|
||||
authSession.Send(responsePacket);
|
||||
if (guildNameResponsePacket.GuildNames.Length > 0)
|
||||
if (guildNameResponsePacket.GuildNames.Length > 0 &&
|
||||
guildNameResponsePacket.GuildNames.Select(n => n != string.Empty).Any())
|
||||
{
|
||||
authSession.Send(guildNameResponsePacket);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static ChannelSelectionResponsePacket CreateTestChannelSelectionResponsePacket()
|
||||
{
|
||||
return new ChannelSelectionResponsePacket
|
||||
{
|
||||
ChannelIsFullFlag = 0,
|
||||
Endpoint = "127.0.0.1",
|
||||
Port = 12345,
|
||||
Characters = new[]
|
||||
{
|
||||
new CharacterData
|
||||
{
|
||||
Name = "1",
|
||||
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||
Gender = Gender.Female,
|
||||
Level = ushort.MaxValue - 1,
|
||||
Experience = 255,
|
||||
Stats = new BaseStats
|
||||
{
|
||||
Strength = 5,
|
||||
Dexterity = 5,
|
||||
Intelligence = 5,
|
||||
Vitality = 5,
|
||||
Luck = 5,
|
||||
Wisdom = 5
|
||||
},
|
||||
Health = int.MaxValue - 1,
|
||||
Mana = int.MaxValue - 1,
|
||||
EquippedItems = Enumerable.Repeat((ushort)25, 20).ToArray(),
|
||||
EquippedCashItems = Enumerable.Repeat((ushort)70, 20).ToArray()
|
||||
},
|
||||
new CharacterData
|
||||
{
|
||||
Name = "2",
|
||||
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||
Gender = Gender.Female,
|
||||
Level = ushort.MaxValue - 1,
|
||||
Experience = 255,
|
||||
Stats = new BaseStats
|
||||
{
|
||||
Strength = 5,
|
||||
Dexterity = 5,
|
||||
Intelligence = 5,
|
||||
Vitality = 5,
|
||||
Luck = 5,
|
||||
Wisdom = 5
|
||||
},
|
||||
Health = int.MaxValue - 1,
|
||||
Mana = int.MaxValue - 1,
|
||||
EquippedItems = Enumerable.Repeat((ushort)35, 20).ToArray(),
|
||||
EquippedCashItems = Enumerable.Repeat((ushort)55, 20).ToArray()
|
||||
},
|
||||
new CharacterData
|
||||
{
|
||||
Name = "3",
|
||||
Job = new JobData { FirstJob = 1, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||
Gender = Gender.Female,
|
||||
Level = ushort.MaxValue - 1,
|
||||
Experience = 255,
|
||||
Stats = new BaseStats
|
||||
{
|
||||
Strength = 5,
|
||||
Dexterity = 5,
|
||||
Intelligence = 5,
|
||||
Vitality = 5,
|
||||
Luck = 5,
|
||||
Wisdom = 5
|
||||
},
|
||||
Health = int.MaxValue - 1,
|
||||
Mana = int.MaxValue - 1,
|
||||
EquippedItems = Enumerable.Repeat((ushort)45, 20).ToArray(),
|
||||
EquippedCashItems = Enumerable.Repeat((ushort)65, 20).ToArray()
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
129
Server/PacketHandlers/CharacterCreationHandler.cs
Normal file
129
Server/PacketHandlers/CharacterCreationHandler.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using NetCoreServer;
|
||||
using Server.DB;
|
||||
using Server.DB.Documents;
|
||||
using Server.Services;
|
||||
using Wonderking.Game.Data.Character;
|
||||
using Wonderking.Game.Mapping;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
public class CharacterCreationHandler : IPacketHandler<CharacterCreationPacket>
|
||||
{
|
||||
private readonly WonderkingContext _wonderkingContext;
|
||||
private readonly ItemObjectPoolService _itemObjectPoolService;
|
||||
private readonly CharacterStatsMappingConfiguration _characterStatsMapping;
|
||||
|
||||
public CharacterCreationHandler(WonderkingContext wonderkingContext, ItemObjectPoolService itemObjectPoolService,
|
||||
CharacterStatsMappingConfiguration characterStatsMappingConfiguration)
|
||||
{
|
||||
_wonderkingContext = wonderkingContext;
|
||||
_itemObjectPoolService = itemObjectPoolService;
|
||||
_characterStatsMapping = characterStatsMappingConfiguration;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(CharacterCreationPacket packet, TcpSession session)
|
||||
{
|
||||
var authSession = session as AuthSession;
|
||||
var account =
|
||||
_wonderkingContext.Accounts.FirstOrDefault(a => authSession != null && a.Id == authSession.AccountId);
|
||||
var mappedDefaultItems = _characterStatsMapping.DefaultCharacterMapping.Items
|
||||
.Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray();
|
||||
|
||||
var firstJobConfig = packet.FirstJob switch
|
||||
{
|
||||
1 => _characterStatsMapping.Swordsman,
|
||||
2 => _characterStatsMapping.Mage,
|
||||
3 => _characterStatsMapping.Thief,
|
||||
4 => _characterStatsMapping.Scout,
|
||||
_ => _characterStatsMapping.Swordsman
|
||||
};
|
||||
|
||||
var mappedJobItems = firstJobConfig.Items
|
||||
.Select(i => _itemObjectPoolService.GetBaseInventoryItem(i.Id, i.Quantity)).ToArray();
|
||||
InventoryItem[] items =
|
||||
[
|
||||
.. mappedDefaultItems,
|
||||
.. mappedJobItems,
|
||||
_itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 +
|
||||
((byte)packet.Gender - 1) * 3 +
|
||||
packet.Hair + 1)),
|
||||
_itemObjectPoolService.GetBaseInventoryItem((ushort)((packet.FirstJob - 1) * 6 +
|
||||
((byte)packet.Gender - 1) * 3 +
|
||||
packet.Eyes + 25)),
|
||||
_itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 +
|
||||
packet.Shirt + 49)),
|
||||
_itemObjectPoolService.GetBaseInventoryItem((ushort)(((byte)packet.Gender - 1) * 3 +
|
||||
packet.Pants + 58)),
|
||||
];
|
||||
|
||||
var calculateCurrentMana = CalculateCurrentMana(1, firstJobConfig);
|
||||
var calculateCurrentHealth = CalculateCurrentHealth(1, firstJobConfig);
|
||||
var toBeAddedCharacter = new Character
|
||||
{
|
||||
Account = account,
|
||||
MapId = 300,
|
||||
Name = packet.Name,
|
||||
LastXCoordinate = 113,
|
||||
LastYCoordinate = 0,
|
||||
PvPLevel = PvPLevel.None,
|
||||
Gender = packet.Gender,
|
||||
Experience = 0,
|
||||
Level = 1,
|
||||
InventoryItems = items,
|
||||
BaseStats = firstJobConfig.BaseStats,
|
||||
JobData = new JobData { FirstJob = packet.FirstJob, SecondJob = 0, ThirdJob = 0, FourthJob = 0 },
|
||||
Health = calculateCurrentHealth,
|
||||
Mana = calculateCurrentMana
|
||||
};
|
||||
account?.Characters.Add(toBeAddedCharacter);
|
||||
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
|
||||
var amountOfCharacters = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution()
|
||||
.CountAsync(c => authSession != null && c.Account.Id == authSession.AccountId).ConfigureAwait(true);
|
||||
|
||||
var character = await _wonderkingContext.Characters.AsNoTrackingWithIdentityResolution()
|
||||
.Where(c => authSession != null && c.Account.Id == authSession.AccountId && c.Name == packet.Name)
|
||||
.Select(c =>
|
||||
new CharacterData
|
||||
{
|
||||
Name = c.Name,
|
||||
Job = c.JobData,
|
||||
Gender = c.Gender,
|
||||
Level = c.Level,
|
||||
Experience = 0,
|
||||
Stats = c.BaseStats,
|
||||
Health = c.Health,
|
||||
Mana = c.Mana,
|
||||
EquippedItems =
|
||||
c.InventoryItems.Where(item => item.InventoryTab == InventoryTab.WornEquipment)
|
||||
.Select(item => item.ItemId)
|
||||
.ToArray(),
|
||||
EquippedCashItems = c.InventoryItems
|
||||
.Where(item => item.InventoryTab == InventoryTab.WornCashEquipment)
|
||||
.Select(item => item.ItemId)
|
||||
.ToArray(),
|
||||
}).FirstAsync().ConfigureAwait(true);
|
||||
authSession?.Send(new CharacterCreationResponsePacket
|
||||
{
|
||||
Character = character,
|
||||
Slot = amountOfCharacters - 1,
|
||||
isDuplicate = false,
|
||||
});
|
||||
}
|
||||
|
||||
private static int CalculateCurrentHealth(ushort level, JobSpecificMapping firstJobConfig)
|
||||
{
|
||||
return (int)((level - 1) * firstJobConfig.DynamicStats.HealthPerLevel +
|
||||
firstJobConfig.BaseStats.Vitality * firstJobConfig.DynamicStats.HealthPerVitality);
|
||||
}
|
||||
|
||||
private static int CalculateCurrentMana(ushort level, JobSpecificMapping firstJobConfig)
|
||||
{
|
||||
return (int)((level - 1) * firstJobConfig.DynamicStats.ManaPerLevel +
|
||||
firstJobConfig.BaseStats.Wisdom * firstJobConfig.DynamicStats.ManaPerWisdom);
|
||||
}
|
||||
}
|
42
Server/PacketHandlers/CharacterDeletionHandler.cs
Normal file
42
Server/PacketHandlers/CharacterDeletionHandler.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using NetCoreServer;
|
||||
using Server.DB;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
public class CharacterDeletionHandler : IPacketHandler<CharacterDeletePacket>
|
||||
{
|
||||
private readonly WonderkingContext _wonderkingContext;
|
||||
|
||||
public CharacterDeletionHandler(WonderkingContext wonderkingContext)
|
||||
{
|
||||
_wonderkingContext = wonderkingContext;
|
||||
}
|
||||
|
||||
public async Task HandleAsync(CharacterDeletePacket packet, TcpSession session)
|
||||
{
|
||||
using var authSession = session as AuthSession;
|
||||
if (authSession == null)
|
||||
{
|
||||
session.Disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
var character = await _wonderkingContext.Characters.FirstOrDefaultAsync(x => x.Name == packet.Name &&
|
||||
x.Account.Id == authSession.AccountId)
|
||||
.ConfigureAwait(true);
|
||||
var response = new CharacterDeleteResponsePacket { IsDeleted = 0 };
|
||||
if (character == null)
|
||||
{
|
||||
authSession.Send(response);
|
||||
return;
|
||||
}
|
||||
|
||||
_wonderkingContext.Characters.Remove(character);
|
||||
await _wonderkingContext.SaveChangesAsync().ConfigureAwait(false);
|
||||
|
||||
authSession.Send(response);
|
||||
}
|
||||
}
|
10
Server/PacketHandlers/CharacterMappingItemEntry.cs
Normal file
10
Server/PacketHandlers/CharacterMappingItemEntry.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
public struct CharacterMappingItemEntry
|
||||
{
|
||||
public required ushort Id { get; set; }
|
||||
public required ushort Quantity { get; set; }
|
||||
}
|
25
Server/PacketHandlers/CharacterNameCheckHandler.cs
Normal file
25
Server/PacketHandlers/CharacterNameCheckHandler.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using NetCoreServer;
|
||||
using Server.DB;
|
||||
using Wonderking.Packets.Incoming;
|
||||
using Wonderking.Packets.Outgoing;
|
||||
|
||||
namespace Server.PacketHandlers;
|
||||
|
||||
public class CharacterNameCheckHandler : IPacketHandler<CharacterNameCheckPacket>
|
||||
{
|
||||
private readonly WonderkingContext _wonderkingContext;
|
||||
|
||||
public CharacterNameCheckHandler(WonderkingContext wonderkingContext)
|
||||
{
|
||||
_wonderkingContext = wonderkingContext;
|
||||
}
|
||||
|
||||
public Task HandleAsync(CharacterNameCheckPacket packet, TcpSession session)
|
||||
{
|
||||
var isTaken = _wonderkingContext.Characters.Any(c => c.Name == packet.Name);
|
||||
var responsePacket = new CharacterNameCheckPacketResponse { IsTaken = isTaken };
|
||||
var authSession = session as AuthSession;
|
||||
authSession?.Send(responsePacket);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
|
@ -108,7 +108,7 @@ public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
|||
var loginResponsePacket = new LoginResponsePacket
|
||||
{
|
||||
ResponseReason = loginResponseReason,
|
||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 75, ServerId = 0 } },
|
||||
ChannelData = new[] { new ServerChannelData { ChannelId = 0, LoadPercentage = 0, ServerId = 0 } },
|
||||
UnknownFlag = 1,
|
||||
IsGameMaster = true
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using MassTransit;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
@ -8,23 +9,42 @@ using Microsoft.Extensions.Hosting;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Server.DB;
|
||||
using Server.Services;
|
||||
using Wonderking.Game.Mapping;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder();
|
||||
#if DEBUG
|
||||
builder.Environment.EnvironmentName = "Development";
|
||||
#endif
|
||||
|
||||
builder.Configuration.AddJsonFile("settings.json", true, true)
|
||||
.AddJsonFile($"settings.{builder.Environment.EnvironmentName}.json", true)
|
||||
.AddEnvironmentVariables().Build();
|
||||
builder.Services.AddSingleton<CharacterStatsMappingConfiguration>(
|
||||
JsonSerializer.Deserialize<CharacterStatsMappingConfiguration>(
|
||||
File.ReadAllText("config/character-stats.mapping.json")) ?? throw new InvalidOperationException());
|
||||
|
||||
builder.Services.AddLogging();
|
||||
builder.Logging.AddFile("Logs/Server-{Date}.log", LogLevel.Trace);
|
||||
builder.Logging.AddFile("Logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true);
|
||||
builder.Services.AddEntityFrameworkNpgsql();
|
||||
builder.Services.AddDbContext<WonderkingContext>();
|
||||
var loggerFactory = LoggerFactory.Create(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.AddFile("logs/Server-{Date}.log", LogLevel.Trace);
|
||||
loggingBuilder.AddFile("logs/Server-{Date}.json.log", LogLevel.Trace, isJson: true);
|
||||
loggingBuilder.AddConsole();
|
||||
});
|
||||
|
||||
builder.Services.AddDbContextPool<WonderkingContext>(o =>
|
||||
{
|
||||
using var configuration = builder.Configuration;
|
||||
o.UseNpgsql(
|
||||
$"Host={configuration["DB:Host"]};Username={configuration["DB:Username"]};Password={configuration["DB:Password"]};Database={configuration["DB:Database"]};Port={configuration["DB:Port"]}")
|
||||
.EnableSensitiveDataLogging().UseLazyLoadingProxies().UseLoggerFactory(loggerFactory);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<ILoggerFactory>(loggerFactory);
|
||||
builder.Services.AddSingleton<PacketDistributorService>();
|
||||
builder.Services.AddSingleton<ItemObjectPoolService>();
|
||||
builder.Services.AddHostedService<ItemObjectPoolService>();
|
||||
builder.Services.AddHostedService(provider =>
|
||||
provider.GetService<PacketDistributorService>() ?? throw new InvalidOperationException());
|
||||
builder.Services.AddSingleton<ItemObjectPoolService>();
|
||||
builder.Services.AddMassTransit(x =>
|
||||
{
|
||||
x.UsingInMemory((context, configurator) => configurator.ConfigureEndpoints(context));
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Server.DB.Documents;
|
||||
using Wonderking.Game.Data;
|
||||
using Wonderking.Game.Reader;
|
||||
|
||||
|
@ -8,35 +10,48 @@ namespace Server.Services;
|
|||
|
||||
public class ItemObjectPoolService : IHostedService
|
||||
{
|
||||
readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool = new();
|
||||
readonly ConcurrentDictionary<uint, ItemObject> _itemObjectPool;
|
||||
private readonly ItemReader _itemReader;
|
||||
private readonly ILogger<ItemObjectPoolService> _logger;
|
||||
|
||||
public ItemObjectPoolService(IConfiguration configuration)
|
||||
public ItemObjectPoolService(IConfiguration configuration, ILogger<ItemObjectPoolService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
_itemReader = new ItemReader(configuration.GetSection("Game").GetSection("Data").GetValue<string>("Path") ??
|
||||
string.Empty);
|
||||
|
||||
_itemObjectPool = new ConcurrentDictionary<uint, ItemObject>();
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var amountOfEntries = _itemReader.GetAmountOfEntries();
|
||||
ParallelEnumerable.Range(0, (int)amountOfEntries).AsParallel().ForAll(i =>
|
||||
Parallel.For(0, (int)amountOfEntries, i =>
|
||||
{
|
||||
var itemObject = _itemReader.GetEntry((uint)i);
|
||||
_itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
|
||||
var result = _itemObjectPool.TryAdd(itemObject.ItemID, itemObject);
|
||||
if (!result)
|
||||
{
|
||||
throw new KeyNotFoundException($"Failed to add item {itemObject.ItemID} to the item object pool");
|
||||
}
|
||||
|
||||
_logger.LogTrace("Item with {ID} has been added", itemObject.ItemID);
|
||||
});
|
||||
_logger.LogInformation("A total of {AmountOfEntries} items have been added to the item object pool",
|
||||
_itemObjectPool.Count);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_itemReader.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public ItemObject GetItem(ushort itemId)
|
||||
{
|
||||
return _itemObjectPool[itemId];
|
||||
_ = _itemObjectPool.TryGetValue(itemId, out var itemObject);
|
||||
return itemObject;
|
||||
}
|
||||
|
||||
public bool ContainsItem(ushort itemId)
|
||||
|
@ -48,4 +63,24 @@ public class ItemObjectPoolService : IHostedService
|
|||
{
|
||||
return _itemObjectPool.AsReadOnly().Values.AsQueryable();
|
||||
}
|
||||
|
||||
public InventoryItem GetBaseInventoryItem(ushort itemId, ushort count = 1, bool isWorn = false)
|
||||
{
|
||||
var item = this.GetItem(itemId);
|
||||
return new InventoryItem
|
||||
{
|
||||
ItemId = itemId,
|
||||
Count = count,
|
||||
Slot = 0,
|
||||
InventoryTab = InventoryTab.WornEquipment,
|
||||
Level = item.MinimumLevelRequirement,
|
||||
Rarity = 0,
|
||||
AddOption = 0,
|
||||
AddOption2 = 0,
|
||||
AddOption3 = 0,
|
||||
Option = 0,
|
||||
Option2 = 0,
|
||||
Option3 = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,17 +22,22 @@ public class PacketDistributorService : IHostedService
|
|||
{
|
||||
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
|
||||
|
||||
private readonly
|
||||
ImmutableDictionary<OperationCode,
|
||||
Func<byte[], IPacket>> _deserializationMap;
|
||||
private ImmutableDictionary<OperationCode,
|
||||
Func<byte[], IPacket>> _deserializationMap;
|
||||
|
||||
private readonly ILogger<PacketDistributorService> _logger;
|
||||
private readonly ConcurrentDictionary<OperationCode, object> _packetHandlersInstantiation;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private ConcurrentDictionary<OperationCode, object> _packetHandlersInstantiation;
|
||||
|
||||
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
this._concurrentQueue = new ConcurrentQueue<RawPacket>();
|
||||
this._logger = logger;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var tempDeserializationMap =
|
||||
new Dictionary<OperationCode, Func<byte[], IPacket>>();
|
||||
|
||||
|
@ -43,7 +48,7 @@ public class PacketDistributorService : IHostedService
|
|||
packetHandlers.ForEach(x =>
|
||||
{
|
||||
var packetHandler =
|
||||
ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider,
|
||||
ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider,
|
||||
x.Value);
|
||||
this._packetHandlersInstantiation.TryAdd(x.Key, packetHandler);
|
||||
});
|
||||
|
@ -60,15 +65,14 @@ public class PacketDistributorService : IHostedService
|
|||
|
||||
Return(packetVariable);
|
||||
}).Compile();
|
||||
logger.PacketCreationFunctionCreated(packetsType.Key);
|
||||
_logger.PacketCreationFunctionCreated(packetsType.Key);
|
||||
tempDeserializationMap.Add(packetsType.Key, lambda);
|
||||
}
|
||||
|
||||
this._deserializationMap = tempDeserializationMap.ToImmutableDictionary();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
|
||||
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
|
||||
|
|
|
@ -58,5 +58,17 @@ public class WonderkingAuthServer : TcpServer, IHostedService
|
|||
base.OnStopped();
|
||||
}
|
||||
|
||||
protected override void OnConnected(TcpSession session)
|
||||
{
|
||||
this._logger.LogInformation("Client connected {Session}", session.Id);
|
||||
base.OnConnected(session);
|
||||
}
|
||||
|
||||
protected override void OnDisconnected(TcpSession session)
|
||||
{
|
||||
this._logger.LogInformation("Client disconnected {Session}", session.Id);
|
||||
base.OnDisconnected(session);
|
||||
}
|
||||
|
||||
protected override void OnError(SocketError error) => this._logger.LogError("An error has occured {Error}", error);
|
||||
}
|
||||
|
|
|
@ -1,44 +1,52 @@
|
|||
services:
|
||||
services:
|
||||
server:
|
||||
container_name: continuity-server
|
||||
image: continuity:latest
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
- db
|
||||
environment:
|
||||
- ENVIRONMENT=Development
|
||||
- Testing:CreateAccountOnLogin=true
|
||||
- DB:Host=db
|
||||
- DB:Port=5432
|
||||
- DB:Username=continuity
|
||||
- DB:Password=continuity
|
||||
- Game:Data:Path=/app/data
|
||||
- ENVIRONMENT=Development
|
||||
- Testing:CreateAccountOnLogin=true
|
||||
- DB:Host=db
|
||||
- DB:Port=5432
|
||||
- DB:Username=continuity
|
||||
- DB:Password=continuity
|
||||
- Game:Data:Path=/app/data/
|
||||
networks:
|
||||
- continuity
|
||||
- continuity
|
||||
ports:
|
||||
- "10001:10001"
|
||||
- 10001:10001
|
||||
volumes:
|
||||
- type: bind
|
||||
source: game-data
|
||||
target: /app/data
|
||||
read_only: true
|
||||
- type: bind
|
||||
source: ../wk-data
|
||||
target: /app/data
|
||||
read_only: true
|
||||
- type: bind
|
||||
source: ../wk-logs
|
||||
target: /app/logs
|
||||
read_only: false
|
||||
- type: bind
|
||||
source: ../config
|
||||
target: /app/config
|
||||
read_only: true
|
||||
|
||||
db:
|
||||
container_name: continuity-db
|
||||
image: postgres:16.1-alpine
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_USER=continuity
|
||||
- POSTGRES_DB=continuity
|
||||
- POSTGRES_PASSWORD=continuity
|
||||
- POSTGRES_USER=continuity
|
||||
- POSTGRES_DB=continuity
|
||||
- POSTGRES_PASSWORD=continuity
|
||||
networks:
|
||||
- continuity
|
||||
- continuity
|
||||
ports:
|
||||
- "5432:5432"
|
||||
- 5432:5432
|
||||
volumes:
|
||||
- db-data:/var/lib/postgresql/data
|
||||
- db-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ]
|
||||
test: [CMD-SHELL, 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
},
|
||||
"Game":{
|
||||
"Data":{
|
||||
"Path": "../wk-data"
|
||||
"Path": "../wk-data/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
Wiki.Dockerfile
Normal file
2
Wiki.Dockerfile
Normal file
|
@ -0,0 +1,2 @@
|
|||
FROM nginx:1.25.3-alpine3.18-slim
|
||||
COPY .public /usr/share/nginx/html
|
20
Wiki/topics/Character-Creation-Packet.md
Normal file
20
Wiki/topics/Character-Creation-Packet.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Character Creation Packet
|
||||
|
||||
## Metadata
|
||||
|
||||
**Operation Code**: 15
|
||||
|
||||
### Structure
|
||||
|
||||
| Identifier | Datatype | Size in bytes |
|
||||
|------------|----------|---------------|
|
||||
| Slot | byte | 1 |
|
||||
| Unknown | byte | 1 |
|
||||
| Id | ushort | 2 |
|
||||
| Name | string | 20 |
|
||||
| First Job | byte | 1 |
|
||||
| Gender | byte | 1 |
|
||||
| Hair | byte | 1 |
|
||||
| Eyes | byte | 1 |
|
||||
| Shirt | byte | 1 |
|
||||
| Pants | byte | 1 |
|
58
Wiki/topics/Character-Creation-Response-Packet.md
Normal file
58
Wiki/topics/Character-Creation-Response-Packet.md
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Character Creation Response Packet
|
||||
|
||||
## Metadata
|
||||
|
||||
**Operation Code**: 13
|
||||
|
||||
### Structure
|
||||
|
||||
Total size: 1 + 132
|
||||
|
||||
| Identifier | Datatype | Size in bytes |
|
||||
|----------------|---------------|---------------|
|
||||
| Is Duplicate | byte | 1 |
|
||||
| Character data | CharacterData | 132 |
|
||||
|
||||
### Subtypes
|
||||
|
||||
#### CharacterData
|
||||
|
||||
Total size: 132 bytes
|
||||
|
||||
| Identifier | Datatype | Size in bytes |
|
||||
|------------------------|------------------|---------------|
|
||||
| Character Slot | int | 4 |
|
||||
| Character Name | string | 20 |
|
||||
| Jobs | Job Data | 4 |
|
||||
| Gender | byte | 1 |
|
||||
| Level | unsigned short | 2 |
|
||||
| Exp? | byte | 1 |
|
||||
| Stats | BaseStats | 12 |
|
||||
| Health | int | 4 |
|
||||
| Mana | int | 4 |
|
||||
| Equipped Item Ids | unsigned short[] | 20 * 2 (40) |
|
||||
| Equipped Cash Item Ids | unsigned short[] | 20 * 2 (40) |
|
||||
|
||||
#### Job Data
|
||||
|
||||
Total size: 4 bytes
|
||||
|
||||
| Identifier | Datatype | Size in bytes |
|
||||
|------------|----------|---------------|
|
||||
| First Job | byte | 1 |
|
||||
| Second Job | byte | 1 |
|
||||
| Third Job | byte | 1 |
|
||||
| Fourth Job | byte | 1 |
|
||||
|
||||
#### BaseStats
|
||||
|
||||
Total size: 12 bytes
|
||||
|
||||
| Identifier | Datatype | Size in bytes |
|
||||
|--------------|----------|---------------|
|
||||
| Strength | short | 2 |
|
||||
| Dexterity | short | 2 |
|
||||
| Intelligence | short | 2 |
|
||||
| Vitality | short | 2 |
|
||||
| Luck | short | 2 |
|
||||
| Wisdom | short | 2 |
|
|
@ -7,6 +7,9 @@
|
|||
<li><a href="Login-Response.md">Login Response</a></li>
|
||||
<li><a href="Channel-Selection.md">Channel Selection</a></li>
|
||||
<li><a href="Channel-Selection-Response.md">Channel Selection Response</a></li>
|
||||
<li><a href="Character-Selection-Set-Guild-Name-Packet.md">Character Selection Set Guild Name Packet</a></li>
|
||||
<li><a href="Character-Creation-Packet.md">Character Creation Packet</a></li>
|
||||
<li><a href="Character-Creation-Response-Packet.md">Character Creation Response Packet</a></li>
|
||||
</list>
|
||||
|
||||
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
|
||||
<toc-element topic="Home.md"/>
|
||||
<toc-element topic="Packets.md" id="packets">
|
||||
<toc-element topic="Character-Creation-Response-Packet.md"/>
|
||||
<toc-element topic="Character-Creation-Packet.md"/>
|
||||
<toc-element topic="Channel-Selection-Response.md"/>
|
||||
<toc-element topic="Channel-Selection.md"/>
|
||||
<toc-element topic="Login-Info.md"/>
|
||||
<toc-element topic="Login-Response.md"/>
|
||||
<toc-element topic="Character-Selection-Set-Guild-Name-Packet.md"/>
|
||||
</toc-element>
|
||||
<toc-element topic="Character-Selection-Set-Guild-Name-Packet.md"/>
|
||||
</instance-profile>
|
|
@ -2,6 +2,6 @@ namespace Wonderking.Game.Data.Item;
|
|||
|
||||
public struct ItemOptions
|
||||
{
|
||||
public ICollection<byte> OptionIDs { get; internal set; }
|
||||
public uint[] OptionIDs { get; internal set; }
|
||||
public bool OptionAvailable { get; internal set; }
|
||||
}
|
||||
|
|
|
@ -7,7 +7,10 @@ public abstract class DataReader<T>
|
|||
protected DataReader(string path)
|
||||
{
|
||||
Path = path;
|
||||
DatFileContent = new(GetDatFileContent(path).ToArray());
|
||||
_xorKey = GetXorKey();
|
||||
SizeOfEntry = GetSizeOfEntry();
|
||||
_datFileName = GetDatFileName();
|
||||
DatFileContent = GetDatFileContent(path).ToArray();
|
||||
}
|
||||
|
||||
private protected string Path { get; init; }
|
||||
|
@ -15,7 +18,7 @@ public abstract class DataReader<T>
|
|||
public abstract uint GetAmountOfEntries();
|
||||
public abstract T GetEntry(uint entryId);
|
||||
|
||||
protected ushort GetSizeOfEntry()
|
||||
private static ushort GetSizeOfEntry()
|
||||
{
|
||||
return typeof(T).GetCustomAttribute<GameDataMetadataAttribute>()?.DataEntrySize ??
|
||||
throw new NotSupportedException("DataEntrySize is null");
|
||||
|
@ -33,16 +36,20 @@ public abstract class DataReader<T>
|
|||
throw new NotSupportedException("XorKey is null");
|
||||
}
|
||||
|
||||
protected MemoryStream DatFileContent { get; }
|
||||
private readonly byte _xorKey;
|
||||
protected readonly ushort SizeOfEntry;
|
||||
private readonly string _datFileName;
|
||||
|
||||
private static Span<byte> GetDatFileContent(string path)
|
||||
protected byte[] DatFileContent { get; }
|
||||
|
||||
private Span<byte> GetDatFileContent(string path)
|
||||
{
|
||||
var fileData = File.ReadAllBytes(path + GetDatFileName());
|
||||
var fileData = File.ReadAllBytes(path + this._datFileName);
|
||||
var data = new byte[fileData.Length];
|
||||
|
||||
for (var i = 0; i < fileData.Length; i++)
|
||||
{
|
||||
data[i] = (byte)(fileData[i] ^ GetXorKey());
|
||||
data[i] = (byte)(fileData[i] ^ this._xorKey);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wonderking.Game.Mapping;
|
||||
|
||||
public class CharacterStatsMappingConfiguration
|
||||
{
|
||||
[JsonPropertyName("default")] public DefaultCharacterMapping DefaultCharacterMapping { get; set; }
|
||||
[JsonPropertyName("1")] public JobSpecificMapping Swordsman { get; set; }
|
||||
|
||||
[JsonPropertyName("2")] public JobSpecificMapping Mage { get; set; }
|
||||
|
||||
[JsonPropertyName("3")] public JobSpecificMapping Thief { get; set; }
|
||||
|
||||
[JsonPropertyName("4")] public JobSpecificMapping Scout { get; set; }
|
||||
}
|
8
Wonderking/Game/Mapping/DefaultCharacterMapping.cs
Normal file
8
Wonderking/Game/Mapping/DefaultCharacterMapping.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wonderking.Game.Mapping;
|
||||
|
||||
public class DefaultCharacterMapping
|
||||
{
|
||||
[JsonPropertyName("items")] public List<Item> Items { get; set; }
|
||||
}
|
47
Wonderking/Game/Mapping/DynamicStats.cs
Normal file
47
Wonderking/Game/Mapping/DynamicStats.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wonderking.Game.Mapping;
|
||||
|
||||
public class DynamicStats
|
||||
{
|
||||
[JsonPropertyName("healthPerLevel")] public int HealthPerLevel { get; set; }
|
||||
[JsonPropertyName("manaPerLevel")] public int ManaPerLevel { get; set; }
|
||||
|
||||
[JsonPropertyName("meleeDamagePerStrength")]
|
||||
public double MeleeDamagePerStrength { get; set; }
|
||||
|
||||
[JsonPropertyName("rangedDamagePerDexterity")]
|
||||
public double RangedDamagePerDexterity { get; set; }
|
||||
|
||||
[JsonPropertyName("hitRatingPerDexterity")]
|
||||
public double HitRatingPerDexterity { get; set; }
|
||||
|
||||
[JsonPropertyName("magicPowerPerIntelligence")]
|
||||
public double MagicPowerPerIntelligence { get; set; }
|
||||
|
||||
[JsonPropertyName("meleeDamagePerLuck")]
|
||||
public double MeleeDamagePerLuck { get; set; }
|
||||
|
||||
[JsonPropertyName("rangedDamagePerLuck")]
|
||||
public double RangedDamagePerLuck { get; set; }
|
||||
|
||||
[JsonPropertyName("evasionPerLuck")] public double EvasionPerLuck { get; set; }
|
||||
[JsonPropertyName("criticalPerLuck")] public double CriticalPerLuck { get; set; }
|
||||
|
||||
[JsonPropertyName("healthPerVitality")]
|
||||
public double HealthPerVitality { get; set; }
|
||||
|
||||
[JsonPropertyName("physicalDefensePerVitality")]
|
||||
public double PhysicalDefensePerVitality { get; set; }
|
||||
|
||||
[JsonPropertyName("manaPerWisdom")] public double ManaPerWisdom { get; set; }
|
||||
|
||||
[JsonPropertyName("elementalDefensePerWisdom")]
|
||||
public double ElementalDefensePerWisdom { get; set; }
|
||||
|
||||
[JsonPropertyName("elementalPowerPerMagicPower")]
|
||||
public double ElementalPowerPerMagicPower { get; set; }
|
||||
|
||||
[JsonPropertyName("elementalDefensePerMagicPower")]
|
||||
public double ElementalDefensePerMagicPower { get; set; }
|
||||
}
|
11
Wonderking/Game/Mapping/Item.cs
Normal file
11
Wonderking/Game/Mapping/Item.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Wonderking.Game.Mapping;
|
||||
|
||||
public class Item
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public ushort Id { get; set; }
|
||||
[JsonPropertyName("quantity")]
|
||||
public ushort Quantity { get; set; }
|
||||
}
|
14
Wonderking/Game/Mapping/JobSpecificMapping.cs
Normal file
14
Wonderking/Game/Mapping/JobSpecificMapping.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
namespace Wonderking.Game.Mapping;
|
||||
|
||||
public class JobSpecificMapping
|
||||
{
|
||||
[JsonPropertyName("items")]
|
||||
public ICollection<Item> Items { get; set; }
|
||||
[JsonPropertyName("baseStats")]
|
||||
public BaseStats BaseStats { get; set; }
|
||||
[JsonPropertyName("dynamicStats")]
|
||||
public DynamicStats DynamicStats { get; set; }
|
||||
}
|
|
@ -1,107 +1,195 @@
|
|||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
using Wonderking.Game.Data;
|
||||
using Wonderking.Game.Data.Item;
|
||||
|
||||
namespace Wonderking.Game.Reader;
|
||||
|
||||
public class ItemReader(string path) : DataReader<ItemObject>(path), IDisposable
|
||||
public class ItemReader(string path) : DataReader<ItemObject>(path)
|
||||
{
|
||||
public override uint GetAmountOfEntries()
|
||||
{
|
||||
return (uint)((this.DatFileContent.Length - 9) / this.GetSizeOfEntry());
|
||||
return (uint)((this.DatFileContent.Length - 9) / this.SizeOfEntry);
|
||||
}
|
||||
|
||||
public override ItemObject GetEntry(uint entryId)
|
||||
{
|
||||
var item = new ItemObject();
|
||||
this.DatFileContent.Position = 9 + entryId * this.GetSizeOfEntry();
|
||||
var reader = new BinaryReader(this.DatFileContent);
|
||||
item.ItemID = reader.ReadUInt32(); //9
|
||||
item.Disabled = reader.ReadUInt32() == 1; //13
|
||||
item.ItemType = reader.ReadUInt32(); //17
|
||||
item.Unknown2 = reader.ReadBytes(4); //21
|
||||
item.Unknown3 = reader.ReadBytes(4); //25
|
||||
item.ClassNo1 = reader.ReadUInt32(); //29
|
||||
item.ClassNo2 = reader.ReadUInt32(); //33
|
||||
item.ClassNo3 = reader.ReadUInt32(); //37
|
||||
item.ClassNo4 = reader.ReadUInt32(); //41
|
||||
item.SlotNo1 = reader.ReadUInt32(); //45
|
||||
item.SlotNo2 = reader.ReadUInt32(); //49
|
||||
item.Unknown4 = reader.ReadBytes(4); //53
|
||||
item.IsCash = reader.ReadUInt32(); //57
|
||||
item.Unknown5 = reader.ReadBytes(4); //61
|
||||
item.Price = reader.ReadUInt32(); //65
|
||||
item.Unknown7 = reader.ReadBytes(4); //69
|
||||
item.MaxNumber = reader.ReadUInt32(); //73
|
||||
item.Unknown17 = reader.ReadBytes(12); //77
|
||||
item.MaximumLevelRequirement = reader.ReadUInt32(); //89
|
||||
item.SexNo = reader.ReadUInt32(); //93
|
||||
item.WeaponSomething = reader.ReadUInt32(); //97
|
||||
item.Unknown8 = reader.ReadBytes(4); //101
|
||||
item.R2C = reader.ReadBytes(16); //105
|
||||
item.Unknown9 = reader.ReadBytes(4); //121
|
||||
item.Stats = reader.ReadStats(); //125
|
||||
item.ElementalStats = reader.ReadElementalStats(); //149
|
||||
item.R7C = reader.ReadBytes(4); //213
|
||||
item.R8C = reader.ReadBytes(8); //217
|
||||
item.Speed = reader.ReadSingle(); //225
|
||||
item.Jump = reader.ReadSingle(); //229
|
||||
item.StatDefense = reader.ReadInt32(); //233
|
||||
item.MagicID = reader.ReadUInt32(); //237
|
||||
item.Unknown13 = reader.ReadBytes(4); //241
|
||||
item.Unknown14 = reader.ReadBytes(4); //245
|
||||
item.AdditionalHealthRecoveryVolume = reader.ReadInt32(); //249
|
||||
item.R9C_1 = reader.ReadBytes(4); //253
|
||||
item.AdditionalManaRecoveryVolume = reader.ReadInt32(); //257
|
||||
item.R9C_2 = reader.ReadBytes(4); //261
|
||||
item.R10C = reader.ReadBytes(8); //265
|
||||
item.AdditionalHealthPoints = reader.ReadInt32(); //273
|
||||
item.AdditionalManaPoints = reader.ReadInt32(); //277
|
||||
item.IsArrow = reader.ReadBoolean(); //281
|
||||
item.Unknown18 = reader.ReadBytes(7); //282
|
||||
item.AdditionalEvasionRate = reader.ReadInt32(); //289
|
||||
item.HitRate = reader.ReadInt32(); //293
|
||||
item.ChanceToHit = reader.ReadInt32(); //297
|
||||
item.MagicalDamage = reader.ReadInt32(); //301
|
||||
item.CriticalHitChance = reader.ReadInt32(); //305
|
||||
item.R12C = reader.ReadBytes(4); //309
|
||||
item.Unknown16 = reader.ReadBytes(4); //313
|
||||
item.MinimalAttackDamage = reader.ReadInt32(); //317
|
||||
item.MaximalAttackDamage = reader.ReadInt32(); //321
|
||||
item.PhysicalDamage = reader.ReadInt32(); //325
|
||||
item.CraftMaterial = reader.ReadCraftMaterial(); //329
|
||||
item.CraftResultAmount = reader.ReadUInt32(); //361
|
||||
item.R14C = reader.ReadBytes(4); //365
|
||||
item.CraftResultItem = reader.ReadUInt32(); //369
|
||||
item.R15C = reader.ReadBytes(4); //373
|
||||
item.R16C = reader.ReadBytes(20); //377
|
||||
item.InventoryX = reader.ReadInt32(); //397
|
||||
item.InventoryY = reader.ReadInt32(); //401
|
||||
item.InventoryWidth = reader.ReadInt32(); //405
|
||||
item.InventoryHeight = reader.ReadInt32(); //409
|
||||
item.SheetID = reader.ReadInt32(); //413
|
||||
item.Name = reader.ReadString(20); //417
|
||||
item.Description = reader.ReadString(85); //427
|
||||
item.Unknown1 = reader.ReadBytes(175); //493
|
||||
item.IsEnchantable = reader.ReadUInt32() == 1; //687
|
||||
item.Unknown1_2 = reader.ReadBytes(104); //687
|
||||
item.SetItems = reader.ReadArray<uint>(5);
|
||||
item.SetID = reader.ReadUInt32(); //691
|
||||
item.Options = reader.ReadItemOptions(); //819
|
||||
item.Unknown19 = reader.ReadBytes(23); //835
|
||||
item.PetID = reader.ReadByte(); //858
|
||||
item.Unknown20 = reader.ReadBytes(20); //859
|
||||
item.HitBoxScaling = reader.ReadByte(); //879
|
||||
item.Unknown20_2 = reader.ReadBytes(13); //880
|
||||
item.ContainedItems = reader.ReadContainedItems(); //893
|
||||
item.IsQuestItem = reader.ReadBoolean(); //923
|
||||
item.MinimumLevelRequirement = reader.ReadByte(); //924
|
||||
item.Unknown21_2 = reader.ReadBytes(6); //925
|
||||
reader.Dispose(); //931
|
||||
var arraySegment = new ArraySegment<byte>(DatFileContent,
|
||||
9 + (int)entryId * this.SizeOfEntry, this.SizeOfEntry);
|
||||
var data = new Span<byte>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
|
||||
item.ItemID = BitConverter.ToUInt32(data.Slice(0, 4)); // 0 -> 4
|
||||
item.Disabled = BitConverter.ToBoolean(data.Slice(4, 4)); // 4 -> 8
|
||||
item.ItemType = BitConverter.ToUInt32(data.Slice(8, 4)); // 8 -> 12
|
||||
item.Unknown2 = data.Slice(12, 4).ToArray(); // 12 -> 16
|
||||
item.Unknown3 = data.Slice(16, 4).ToArray(); // 16 -> 20
|
||||
item.ClassNo1 = BitConverter.ToUInt32(data.Slice(20, 4)); // 20 -> 24
|
||||
item.ClassNo2 = BitConverter.ToUInt32(data.Slice(24, 4)); // 24 -> 28
|
||||
item.ClassNo3 = BitConverter.ToUInt32(data.Slice(28, 4)); // 28 -> 32
|
||||
item.ClassNo4 = BitConverter.ToUInt32(data.Slice(32, 4)); // 32 -> 36
|
||||
item.SlotNo1 = BitConverter.ToUInt32(data.Slice(36, 4)); // 36 -> 40
|
||||
item.SlotNo2 = BitConverter.ToUInt32(data.Slice(40, 4)); // 40 -> 44
|
||||
item.Unknown4 = data.Slice(44, 4).ToArray(); // 44 -> 48
|
||||
item.IsCash = BitConverter.ToUInt32(data.Slice(48, 4)); // 48 -> 52
|
||||
item.Unknown5 = data.Slice(52, 4).ToArray(); // 52 -> 56
|
||||
item.Price = BitConverter.ToUInt32(data.Slice(56, 4)); // 56 -> 60
|
||||
item.Unknown7 = data.Slice(60, 4).ToArray(); // 60 -> 64
|
||||
item.MaxNumber = BitConverter.ToUInt32(data.Slice(64, 4)); // 64 -> 68
|
||||
item.Unknown17 = data.Slice(68, 12).ToArray(); // 68 -> 80
|
||||
item.MaximumLevelRequirement = BitConverter.ToUInt32(data.Slice(80, 4)); // 80 -> 84
|
||||
item.SexNo = BitConverter.ToUInt32(data.Slice(84, 4)); // 84 -> 88
|
||||
item.WeaponSomething = BitConverter.ToUInt32(data.Slice(88, 4)); // 88 -> 92
|
||||
item.Unknown8 = data.Slice(92, 4).ToArray(); // 92 -> 96
|
||||
item.R2C = data.Slice(96, 16).ToArray(); // 96 -> 112
|
||||
item.Unknown9 = data.Slice(112, 4).ToArray(); // 112 -> 116
|
||||
item.Stats = new Stats
|
||||
{
|
||||
Strength = BitConverter.ToInt32(data.Slice(116, 4)), // 116 -> 120
|
||||
Dexterity = BitConverter.ToInt32(data.Slice(120, 4)), // 120 -> 124
|
||||
Intelligence = BitConverter.ToInt32(data.Slice(124, 4)), // 124 -> 128
|
||||
Vitality = BitConverter.ToInt32(data.Slice(128, 4)), // 128 -> 132
|
||||
Luck = BitConverter.ToInt32(data.Slice(132, 4)), // 132 -> 136
|
||||
Wisdom = BitConverter.ToInt32(data.Slice(136, 4)), // 136 -> 140
|
||||
}; // 116 -> 140
|
||||
item.ElementalStats = new ElementalStats
|
||||
{
|
||||
MinimumFireDamage = BitConverter.ToInt32(data.Slice(140, 4)), // 140 -> 144
|
||||
MinimumWaterDamage = BitConverter.ToInt32(data.Slice(144, 4)), // 144 -> 148
|
||||
MinimumDarkDamage = BitConverter.ToInt32(data.Slice(148, 4)), // 148 -> 152
|
||||
MinimumHolyDamage = BitConverter.ToInt32(data.Slice(152, 4)), // 152 -> 156
|
||||
MaximumFireDamage = BitConverter.ToInt32(data.Slice(156, 4)), // 156 -> 160
|
||||
MaximumWaterDamage = BitConverter.ToInt32(data.Slice(160, 4)), // 160 -> 164
|
||||
MaximumDarkDamage = BitConverter.ToInt32(data.Slice(164, 4)), // 164 -> 168
|
||||
MaximumHolyDamage = BitConverter.ToInt32(data.Slice(168, 4)), // 168 -> 172
|
||||
ElementFire = BitConverter.ToUInt32(data.Slice(172, 4)), // 172 -> 176
|
||||
ElementWater = BitConverter.ToUInt32(data.Slice(176, 4)), // 176 -> 180
|
||||
ElementDark = BitConverter.ToUInt32(data.Slice(180, 4)), // 180 -> 184
|
||||
ElementHoly = BitConverter.ToUInt32(data.Slice(184, 4)), // 184 -> 188
|
||||
FireResistance = BitConverter.ToInt32(data.Slice(188, 4)), // 188 -> 192
|
||||
WaterResistance = BitConverter.ToInt32(data.Slice(192, 4)), // 192 -> 196
|
||||
DarkResistance = BitConverter.ToInt32(data.Slice(196, 4)), // 196 -> 200
|
||||
HolyResistance = BitConverter.ToInt32(data.Slice(200, 4)), // 200 -> 204
|
||||
}; // 140 -> 204
|
||||
item.R7C = data.Slice(204, 4).ToArray(); // 204 -> 208
|
||||
item.R8C = data.Slice(208, 8).ToArray(); // 208 -> 216
|
||||
item.Speed = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(216, 4)); // 216 -> 220
|
||||
item.Jump = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(220, 4)); // 220 -> 224
|
||||
item.StatDefense = BitConverter.ToInt32(data.Slice(224, 4)); // 224 -> 228
|
||||
item.MagicID = BitConverter.ToUInt32(data.Slice(228, 4)); // 228 -> 232
|
||||
item.Unknown13 = data.Slice(232, 4).ToArray(); // 232 -> 236
|
||||
item.Unknown14 = data.Slice(236, 4).ToArray(); // 236 -> 240
|
||||
item.AdditionalHealthRecoveryVolume = BitConverter.ToInt32(data.Slice(240, 4)); // 240 -> 244
|
||||
item.R9C_1 = data.Slice(244, 4).ToArray(); // 244 -> 248
|
||||
item.AdditionalManaRecoveryVolume = BitConverter.ToInt32(data.Slice(248, 4)); // 248 -> 252
|
||||
item.R9C_2 = data.Slice(252, 4).ToArray(); // 252 -> 256
|
||||
item.R10C = data.Slice(256, 8).ToArray(); // 256 -> 264
|
||||
item.AdditionalHealthPoints = BitConverter.ToInt32(data.Slice(264, 4)); // 264 -> 268
|
||||
item.AdditionalManaPoints = BitConverter.ToInt32(data.Slice(268, 4)); // 268 -> 272
|
||||
item.IsArrow = BitConverter.ToBoolean(data.Slice(272, 1)); // 272 -> 273
|
||||
item.Unknown18 = data.Slice(273, 7).ToArray(); // 273 -> 280
|
||||
item.AdditionalEvasionRate = BitConverter.ToInt32(data.Slice(280, 4)); // 280 -> 284
|
||||
item.HitRate = BitConverter.ToInt32(data.Slice(284, 4)); // 284 -> 288
|
||||
item.ChanceToHit = BitConverter.ToInt32(data.Slice(288, 4)); // 288 -> 292
|
||||
item.MagicalDamage = BitConverter.ToInt32(data.Slice(292, 4)); // 292 -> 296
|
||||
item.CriticalHitChance = BitConverter.ToInt32(data.Slice(296, 4)); // 296 -> 300
|
||||
item.R12C = data.Slice(300, 4).ToArray(); // 300 -> 304
|
||||
item.Unknown16 = data.Slice(304, 4).ToArray(); // 304 -> 308
|
||||
item.MinimalAttackDamage = BitConverter.ToInt32(data.Slice(308, 4)); // 308 -> 312
|
||||
item.MaximalAttackDamage = BitConverter.ToInt32(data.Slice(312, 4)); // 312 -> 316
|
||||
item.PhysicalDamage = BitConverter.ToInt32(data.Slice(316, 4)); // 316 -> 320
|
||||
item.CraftMaterial = new CraftMaterial[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
ID = BitConverter.ToUInt32(data.Slice(320, 4)), // 320 -> 324
|
||||
Amount = BitConverter.ToUInt32(data.Slice(336, 4)) // 336 -> 340
|
||||
},
|
||||
new()
|
||||
{
|
||||
ID = BitConverter.ToUInt32(data.Slice(324, 4)), // 324 -> 328
|
||||
Amount = BitConverter.ToUInt32(data.Slice(340, 4)) // 340 -> 344
|
||||
},
|
||||
new()
|
||||
{
|
||||
ID = BitConverter.ToUInt32(data.Slice(328, 4)), // 328 -> 332
|
||||
Amount = BitConverter.ToUInt32(data.Slice(344, 4)) // 344 -> 348
|
||||
},
|
||||
new()
|
||||
{
|
||||
ID = BitConverter.ToUInt32(data.Slice(332, 4)), // 332 -> 336
|
||||
Amount = BitConverter.ToUInt32(data.Slice(348, 4)) // 348 -> 352
|
||||
},
|
||||
}; // 320 -> 352
|
||||
item.CraftResultAmount = BitConverter.ToUInt32(data.Slice(352, 4)); // 352 -> 356
|
||||
item.R14C = data.Slice(356, 4).ToArray(); // 356 -> 360
|
||||
item.CraftResultItem = BitConverter.ToUInt32(data.Slice(360, 4)); // 360 -> 364
|
||||
item.R15C = data.Slice(364, 4).ToArray(); // 364 -> 368
|
||||
item.R16C = data.Slice(368, 20).ToArray(); // 368 -> 388
|
||||
item.InventoryX = BitConverter.ToInt32(data.Slice(388, 4)); // 388 -> 392
|
||||
item.InventoryY = BitConverter.ToInt32(data.Slice(392, 4)); // 392 -> 396
|
||||
item.InventoryWidth = BitConverter.ToInt32(data.Slice(396, 4)); // 396 -> 400
|
||||
item.InventoryHeight = BitConverter.ToInt32(data.Slice(400, 4)); // 400 -> 404
|
||||
item.SheetID = BitConverter.ToInt32(data.Slice(404, 4)); // 404 -> 408
|
||||
item.Name = Encoding.ASCII.GetString(data.Slice(408, 20)); // 408 -> 428
|
||||
item.Description = Encoding.ASCII.GetString(data.Slice(428, 85)); // 428 -> 513
|
||||
item.Unknown1 = data.Slice(513, 175).ToArray(); // 513 -> 688
|
||||
item.IsEnchantable = BitConverter.ToBoolean(data.Slice(688, 4)); // 688 -> 672
|
||||
item.Unknown1_2 = data.Slice(692, 104).ToArray(); // 692 -> 796
|
||||
item.SetItems = new[]
|
||||
{
|
||||
BitConverter.ToUInt32(data.Slice(796, 4)), // 796 -> 800
|
||||
BitConverter.ToUInt32(data.Slice(800, 4)), // 800 -> 804
|
||||
BitConverter.ToUInt32(data.Slice(804, 4)), // 804 -> 808
|
||||
BitConverter.ToUInt32(data.Slice(808, 4)), // 808 -> 812
|
||||
BitConverter.ToUInt32(data.Slice(812, 4)), // 812 -> 816
|
||||
}; // 796 -> 816
|
||||
item.SetID = BitConverter.ToUInt32(data.Slice(816, 4)); // 816 -> 820
|
||||
item.Options = new ItemOptions
|
||||
{
|
||||
OptionIDs = new[]
|
||||
{
|
||||
BitConverter.ToUInt32(data.Slice(824, 4)), // 824 -> 828
|
||||
BitConverter.ToUInt32(data.Slice(828, 4)), // 828 -> 832
|
||||
BitConverter.ToUInt32(data.Slice(832, 4)), // 832 -> 836
|
||||
BitConverter.ToUInt32(data.Slice(836, 4)), // 836 -> 840
|
||||
},
|
||||
OptionAvailable = BitConverter.ToBoolean(data.Slice(820, 4)), // 820 -> 824
|
||||
}; // 820 -> 840
|
||||
item.Unknown19 = data.Slice(840, 23).ToArray(); // 840 -> 863
|
||||
item.PetID = data[863]; // 863 -> 864
|
||||
item.Unknown20 = data.Slice(864, 20).ToArray(); // 864 -> 884
|
||||
item.HitBoxScaling = data[884]; // 884 -> 885
|
||||
item.Unknown20_2 = data.Slice(885, 13).ToArray(); // 885 -> 898
|
||||
item.ContainedItems = new[]
|
||||
{
|
||||
new ContainedItem
|
||||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(898, 2)), // 898 -> 900
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(908, 4)) // 908 -> 912
|
||||
},
|
||||
new ContainedItem
|
||||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(900, 2)), // 900 -> 902
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(912, 4)) // 912 -> 916
|
||||
},
|
||||
new ContainedItem
|
||||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(902, 2)), // 902 -> 904
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(916, 4)) // 916 -> 920
|
||||
},
|
||||
new ContainedItem
|
||||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(904, 2)), // 904 -> 906
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(920, 4)) // 920 -> 924
|
||||
},
|
||||
new ContainedItem
|
||||
{
|
||||
ID = BitConverter.ToInt16(data.Slice(906, 2)), // 906 -> 908
|
||||
ObtainChance = BitConverter.ToSingle(data.Slice(924, 4)) // 924 -> 928
|
||||
},
|
||||
};
|
||||
item.MinimumLevelRequirement = data[928]; // 928 -> 929
|
||||
item.Unknown21_2 = data.Slice(929, 3).ToArray(); // 929 -> 932
|
||||
return item;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.DatFileContent.Dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,14 +82,14 @@ public static class ItemReaderExtensions
|
|||
|
||||
options.OptionAvailable = reader.ReadInt32() == 1; //819
|
||||
|
||||
var optionIDs = new List<byte>(4);
|
||||
var optionIDs = new List<uint>(4);
|
||||
//823
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
optionIDs.Add((byte)reader.ReadUInt32());
|
||||
optionIDs.Add(reader.ReadUInt32());
|
||||
}
|
||||
|
||||
options.OptionIDs = optionIDs;
|
||||
options.OptionIDs = optionIDs.ToArray();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
|
39
Wonderking/Packets/Incoming/CharacterCreationPacket.cs
Normal file
39
Wonderking/Packets/Incoming/CharacterCreationPacket.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System.Text;
|
||||
using Wonderking.Game.Data.Character;
|
||||
|
||||
namespace Wonderking.Packets.Incoming;
|
||||
|
||||
[PacketId(OperationCode.CharacterCreation)]
|
||||
public class CharacterCreationPacket : IPacket
|
||||
{
|
||||
public byte Slot { get; set; }
|
||||
public byte Unknown { get; set; }
|
||||
public ushort Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public byte FirstJob { get; set; }
|
||||
public Gender Gender { get; set; }
|
||||
public byte Hair { get; set; }
|
||||
public byte Eyes { get; set; }
|
||||
public byte Shirt { get; set; }
|
||||
public byte Pants { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
Slot = data[0];
|
||||
Unknown = data[1];
|
||||
Id = BitConverter.ToUInt16(data, 2);
|
||||
Name = Encoding.ASCII.GetString(data, 4, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
FirstJob = data[24];
|
||||
Gender = (Gender)data[25];
|
||||
Hair = data[26];
|
||||
Eyes = data[27];
|
||||
Shirt = data[28];
|
||||
Pants = data[29];
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
24
Wonderking/Packets/Incoming/CharacterDeletePacket.cs
Normal file
24
Wonderking/Packets/Incoming/CharacterDeletePacket.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Wonderking.Packets.Incoming;
|
||||
|
||||
[PacketId(OperationCode.CharacterDeletion)]
|
||||
public class CharacterDeletePacket : IPacket
|
||||
{
|
||||
public byte Slot { get; set; }
|
||||
public string Name { get; set; }
|
||||
public uint Unknown { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
Span<byte> span = data;
|
||||
Slot = span[0];
|
||||
Name = Encoding.ASCII.GetString(span.Slice(1, 20)).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
Unknown = BitConverter.ToUInt32(span.Slice(21, 4));
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
19
Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs
Normal file
19
Wonderking/Packets/Incoming/CharacterNameCheckPacket.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Text;
|
||||
|
||||
namespace Wonderking.Packets.Incoming;
|
||||
|
||||
[PacketId(OperationCode.CharacterNameCheck)]
|
||||
public class CharacterNameCheckPacket : IPacket
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
Name = Encoding.ASCII.GetString(data, 0, 20).TrimEnd('\0').TrimEnd('\n').TrimEnd('\0');
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
|
@ -6,5 +6,12 @@ public enum OperationCode : ushort
|
|||
LoginResponse = 12,
|
||||
ChannelSelection = 13,
|
||||
ChannelSelectionResponse = 13,
|
||||
CharacterNameCheck = 14,
|
||||
CharacterNameCheckResponse = 14,
|
||||
CharacterCreation = 15,
|
||||
CharacterCreationResponse = 15,
|
||||
CharacterDeletion = 16,
|
||||
CharacterDeletionResponse = 16,
|
||||
CharacterSelection = 17,
|
||||
CharacterSelectionSetGuildName = 19,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
namespace Wonderking.Packets.Outgoing;
|
||||
|
||||
[PacketId(OperationCode.CharacterCreationResponse)]
|
||||
public class CharacterCreationResponsePacket : IPacket
|
||||
{
|
||||
public required CharacterData Character { get; set; }
|
||||
public required int Slot { get; set; }
|
||||
public required bool isDuplicate { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1 + 132];
|
||||
data[0] = isDuplicate ? (byte)1 : (byte)0;
|
||||
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(1, 4), Slot);
|
||||
Encoding.ASCII.GetBytes(Character.Name, data.Slice(5, 20));
|
||||
|
||||
// Job Data
|
||||
data[25] = Character.Job.FirstJob;
|
||||
data[26] = Character.Job.SecondJob;
|
||||
data[27] = Character.Job.ThirdJob;
|
||||
data[28] = Character.Job.FourthJob;
|
||||
|
||||
data[29] = (byte)Character.Gender;
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(30, 2), Character.Level);
|
||||
data[32] = (byte)Character.Experience;
|
||||
|
||||
// Stats
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(33, 2), Character.Stats.Strength);
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(35, 2), Character.Stats.Dexterity);
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(37, 2), Character.Stats.Intelligence);
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(39, 2), Character.Stats.Vitality);
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(41, 2), Character.Stats.Luck);
|
||||
BinaryPrimitives.WriteInt16LittleEndian(data.Slice(43, 2), Character.Stats.Wisdom);
|
||||
|
||||
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(45, 4), Character.Health);
|
||||
BinaryPrimitives.WriteInt32LittleEndian(data.Slice(49, 4), Character.Mana);
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
// Equipped Items
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(53 + i * 2, 2),
|
||||
Character.EquippedItems.Length > i ? Character.EquippedItems[i] : (ushort)0);
|
||||
|
||||
// Equipped Cash Items
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(data.Slice(93 + i * 2, 2),
|
||||
Character.EquippedCashItems.Length > i ? Character.EquippedCashItems[i] : (ushort)0);
|
||||
}
|
||||
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
19
Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs
Normal file
19
Wonderking/Packets/Outgoing/CharacterDeleteResponsePacket.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
namespace Wonderking.Packets.Outgoing;
|
||||
|
||||
[PacketId(OperationCode.CharacterDeletionResponse)]
|
||||
public class CharacterDeleteResponsePacket : IPacket
|
||||
{
|
||||
public required byte IsDeleted { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1];
|
||||
data[0] = IsDeleted;
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
namespace Wonderking.Packets.Outgoing;
|
||||
|
||||
[PacketId(OperationCode.CharacterNameCheckResponse)]
|
||||
public class CharacterNameCheckPacketResponse : IPacket
|
||||
{
|
||||
public required bool IsTaken { get; set; }
|
||||
|
||||
public void Deserialize(byte[] data)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
Span<byte> data = stackalloc byte[1];
|
||||
data[0] = this.IsTaken ? (byte)1 : (byte)0;
|
||||
return data.ToArray();
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
using System.Text.Json.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
[UsedImplicitly]
|
||||
[Owned]
|
||||
public class BaseStats
|
||||
{
|
||||
public required short Strength { get; set; }
|
||||
public required short Dexterity { get; set; }
|
||||
public required short Intelligence { get; set; }
|
||||
public required short Vitality { get; set; }
|
||||
public required short Luck { get; set; }
|
||||
public required short Wisdom { get; set; }
|
||||
[JsonPropertyName("strength")] public required short Strength { get; set; }
|
||||
[JsonPropertyName("dexterity")] public required short Dexterity { get; set; }
|
||||
[JsonPropertyName("intelligence")] public required short Intelligence { get; set; }
|
||||
[JsonPropertyName("vitality")] public required short Vitality { get; set; }
|
||||
[JsonPropertyName("luck")] public required short Luck { get; set; }
|
||||
[JsonPropertyName("wisdom")] public required short Wisdom { get; set; }
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using JetBrains.Annotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
[UsedImplicitly]
|
||||
[Owned]
|
||||
public class JobData
|
||||
{
|
||||
public required byte FirstJob { get; set; }
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Buffers.Binary;
|
||||
using Wonderking.Packets.Outgoing.Data;
|
||||
|
||||
namespace Wonderking.Packets.Outgoing;
|
||||
|
@ -34,9 +35,7 @@ public class LoginResponsePacket : IPacket
|
|||
dataSpan[0] = (byte)this.ResponseReason;
|
||||
dataSpan[1] = this.UnknownFlag;
|
||||
dataSpan[2] = BitConverter.GetBytes(this.IsGameMaster)[0];
|
||||
var bytesOfChannelAmount = BitConverter.GetBytes((ushort)this.ChannelData.Length);
|
||||
dataSpan[3] = bytesOfChannelAmount[0];
|
||||
dataSpan[4] = bytesOfChannelAmount[1];
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(dataSpan.Slice(3, 2), (ushort)this.ChannelData.Length);
|
||||
|
||||
for (var i = 0; i < this.ChannelData.Length; i++)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.8.14">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
|
150
config/character-stats.mapping.json
Normal file
150
config/character-stats.mapping.json
Normal file
|
@ -0,0 +1,150 @@
|
|||
{
|
||||
"default": {
|
||||
"items": [
|
||||
{
|
||||
"id": 841,
|
||||
"quantity": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"1": {
|
||||
"items": [
|
||||
{
|
||||
"id": 70,
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"baseStats": {
|
||||
"strength": 13,
|
||||
"dexterity": 9,
|
||||
"intelligence": 3,
|
||||
"luck": 5,
|
||||
"vitality": 17,
|
||||
"wisdom": 5
|
||||
},
|
||||
"dynamicStats": {
|
||||
"healthPerLevel": 20,
|
||||
"manaPerLevel": 8,
|
||||
"meleeDamagePerStrength": 0.4,
|
||||
"rangedDamagePerDexterity": 0,
|
||||
"hitRatingPerDexterity": 4.2,
|
||||
"magicPowerPerIntelligence": 0.55,
|
||||
"meleeDamagePerLuck": 0,
|
||||
"rangedDamagePerLuck": 0,
|
||||
"evasionPerLuck": 1.35,
|
||||
"criticalPerLuck": 0.25,
|
||||
"healthPerVitality": 5.75,
|
||||
"physicalDefensePerVitality": 0.11,
|
||||
"manaPerWisdom": 3.4,
|
||||
"elementalDefensePerWisdom": 0.045,
|
||||
"elementalPowerPerMagicPower": 0.4,
|
||||
"elementalDefensePerMagicPower": 0.1
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"items": [
|
||||
{
|
||||
"id": 150,
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"baseStats": {
|
||||
"strength": 6,
|
||||
"dexterity": 6,
|
||||
"intelligence": 14,
|
||||
"luck": 3,
|
||||
"vitality": 16,
|
||||
"wisdom": 11
|
||||
},
|
||||
"dynamicStats": {
|
||||
"healthPerLevel": 12,
|
||||
"manaPerLevel": 16,
|
||||
"meleeDamagePerStrength": 0.3,
|
||||
"rangedDamagePerDexterity": 0,
|
||||
"hitRatingPerDexterity": 6.2,
|
||||
"magicPowerPerIntelligence": 0.85,
|
||||
"meleeDamagePerLuck": 0,
|
||||
"rangedDamagePerLuck": 0,
|
||||
"evasionPerLuck": 1.35,
|
||||
"criticalPerLuck": 0.25,
|
||||
"healthPerVitality": 3.45,
|
||||
"physicalDefensePerVitality": 0.04,
|
||||
"manaPerWisdom": 3.6,
|
||||
"elementalDefensePerWisdom": 0.29,
|
||||
"elementalPowerPerMagicPower": 0.4,
|
||||
"elementalDefensePerMagicPower": 0.1
|
||||
}
|
||||
},
|
||||
"3": {
|
||||
"items": [
|
||||
{
|
||||
"id": 218,
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"baseStats": {
|
||||
"strength": 7,
|
||||
"dexterity": 11,
|
||||
"intelligence": 59,
|
||||
"luck": 5,
|
||||
"vitality": 15,
|
||||
"wisdom": 6
|
||||
},
|
||||
"dynamicStats": {
|
||||
"healthPerLevel": 13,
|
||||
"manaPerLevel": 12,
|
||||
"meleeDamagePerStrength": 0,
|
||||
"rangedDamagePerDexterity": 0,
|
||||
"hitRatingPerDexterity": 4.2,
|
||||
"magicPowerPerIntelligence": 0.55,
|
||||
"meleeDamagePerLuck": 0.3,
|
||||
"rangedDamagePerLuck": 0.15,
|
||||
"evasionPerLuck": 1.5,
|
||||
"criticalPerLuck": 0.25,
|
||||
"healthPerVitality": 4.55,
|
||||
"physicalDefensePerVitality": 0.04,
|
||||
"manaPerWisdom": 3.4,
|
||||
"elementalDefensePerWisdom": 0.065,
|
||||
"elementalPowerPerMagicPower": 0.4,
|
||||
"elementalDefensePerMagicPower": 0.1
|
||||
}
|
||||
},
|
||||
"4": {
|
||||
"items": [
|
||||
{
|
||||
"id": 299,
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"id": 305,
|
||||
"quantity": 1000
|
||||
}
|
||||
],
|
||||
"baseStats": {
|
||||
"strength": 6,
|
||||
"dexterity": 13,
|
||||
"intelligence": 8,
|
||||
"luck": 5,
|
||||
"vitality": 8,
|
||||
"wisdom": 7
|
||||
},
|
||||
"dynamicStats": {
|
||||
"healthPerLevel": 12,
|
||||
"manaPerLevel": 15,
|
||||
"meleeDamagePerStrength": 0,
|
||||
"rangedDamagePerDexterity": 0.25,
|
||||
"hitRatingPerDexterity": 3.0,
|
||||
"magicPowerPerIntelligence": 0.55,
|
||||
"meleeDamagePerLuck": 0,
|
||||
"rangedDamagePerLuck": 0,
|
||||
"evasionPerLuck": 1.35,
|
||||
"criticalPerLuck": 0.25,
|
||||
"healthPerVitality": 4.55,
|
||||
"physicalDefensePerVitality": 0.1,
|
||||
"manaPerWisdom": 3.5,
|
||||
"elementalDefensePerWisdom": 0.055,
|
||||
"elementalPowerPerMagicPower": 0.4,
|
||||
"elementalDefensePerMagicPower": 0.1
|
||||
}
|
||||
}
|
||||
}
|
14
scripts/dexor_binary_file.ps1
Normal file
14
scripts/dexor_binary_file.ps1
Normal file
|
@ -0,0 +1,14 @@
|
|||
$SourceFilePath = $args[0]
|
||||
$DestinationFilePath = $args[1]
|
||||
$XorKey = [byte]$args[2]
|
||||
|
||||
# Load the binary data from the file
|
||||
$data = [System.IO.File]::ReadAllBytes($SourceFilePath)
|
||||
|
||||
# Apply the XOR operation
|
||||
for ($i = 0; $i -lt $data.Length; $i++) {
|
||||
$data[$i] = $data[$i] -bxor $XorKey
|
||||
}
|
||||
|
||||
# Write the output to the destination file
|
||||
[System.IO.File]::WriteAllBytes($DestinationFilePath, $data)
|
36
scripts/split_dat_file.ps1
Normal file
36
scripts/split_dat_file.ps1
Normal file
|
@ -0,0 +1,36 @@
|
|||
$SourceFilePath = $args[0]
|
||||
$ChunkSize = $args[1]
|
||||
$Offset = $args[2]
|
||||
|
||||
# Function to create chunk file
|
||||
function Create-ChunkFile {
|
||||
param (
|
||||
[byte[]]$ChunkData,
|
||||
[int]$ChunkNumber
|
||||
)
|
||||
|
||||
$chunkFileName = Join-Path $subDir ("{0}_{1:D5}.bin" -f [System.IO.Path]::GetFileNameWithoutExtension($SourceFilePath), $ChunkNumber)
|
||||
[System.IO.File]::WriteAllBytes($chunkFileName, $ChunkData)
|
||||
}
|
||||
|
||||
# Create subdirectory named after the source file (without extension) for chunks
|
||||
$subDir = Join-Path ([System.IO.Path]::GetDirectoryName($SourceFilePath)) ([System.IO.Path]::GetFileNameWithoutExtension($SourceFilePath))
|
||||
if (!(Test-Path -Path $subDir)) {
|
||||
New-Item -ItemType Directory -Path $subDir
|
||||
}
|
||||
|
||||
# Read the binary data from the file
|
||||
$data = [System.IO.File]::ReadAllBytes($SourceFilePath)
|
||||
$dataLength = $data.Length
|
||||
$chunkNumber = 0
|
||||
|
||||
# Adjust the start position based on the offset
|
||||
$startPosition = [Math]::Min($Offset, $dataLength)
|
||||
|
||||
# Process chunks
|
||||
for ($i = $startPosition; $i -lt $dataLength; $i += $ChunkSize) {
|
||||
$chunkEnd = [Math]::Min($i + $ChunkSize, $dataLength)
|
||||
$chunkData = $data[$i..($chunkEnd - 1)]
|
||||
Create-ChunkFile -ChunkData $chunkData -ChunkNumber $chunkNumber
|
||||
$chunkNumber++
|
||||
}
|
Loading…
Reference in a new issue