diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 000000000..f49ec5d7c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,48 @@ +name: Create and publish a Docker image + +# Configures this workflow to run every time a tag is created. +on: + push: + tags: + - '*' + +# Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. +jobs: + build-and-push-image: + runs-on: ubuntu-latest + # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. + permissions: + contents: read + packages: write + # + steps: + - name: Checkout repository + uses: actions/checkout@v4 + # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels. + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. + # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. + # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. + - name: Build and push Docker image + uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index a36e91559..4101960d2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ vendor/ savedata/*/ *.exe *.lnk -*.bat \ No newline at end of file +*.bat +/docker/db-data \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 000000000..618d34264 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,29 @@ +# List of AUTHORS who contributed over time to the Erupe project + +## Point of current development +The project is currently developed under https://github.com/ZeruLight/Erupe + +## History of development +Development of this project dates back to 2019, and was developed under various umbrellas over time: +* Cappuccino (Fist/Ando/Ellie42) (The Erupe Developers), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source) +* Einherjar Team, ????-2022 Feb (There is no git history for this period, this team's work was taken and used as a foundation for future repositories) +* Community Edition, 2022 (https://github.com/xl3lackout/Erupe) +* Zerulight, 2022-2023 (https://github.com/ZeruLight/Erupe) + +## Authorship of the code +Authorship is assigned for each commit within the git history, which is stored in these git repos: +* https://github.com/ZeruLight/Erupe +* https://github.com/Ellie42/Erupe +* https://github.com/ricochhet/Erupe-Legacy +* https://github.com/xl3lackout/Erupe + +Note there is a divergence between Ellie42s branch and xl3lackout where history has been lost. + +Unfortunately, we have no detailed information on the history of the Erupe pre-2022 +if somebody can provide information, please contact us, so that we can make this history available. + +## Exceptions with third-party libraries +The third-party libraries have their own way of addressing authorship and the authorship of commits importing/updating +a third-party library reflects who did the importing instead of who wrote the code within the commit. + +The Authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries. \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..37015b19d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM golang:1.21-alpine3.19 + +ENV GO111MODULE=on + +WORKDIR /app/erupe + +COPY go.mod . +COPY go.sum . + +RUN go mod download + +COPY . . + +CMD [ "go", "run", "." ] \ No newline at end of file diff --git a/README.md b/README.md index 034e2a4ea..b9be1fb05 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,20 @@ If you want to modify or compile Erupe yourself, please read on. 3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup. 4. Run `go build` or `go run .` to compile Erupe. +## Docker + +Please see the readme in [docker/README.md](./docker/README.md). At the moment this is only really good for quick installs and checking out development not for production. + +## Schemas + +We source control the following schemas: +- Initialisation Schemas: These initialise the application database to a clean install from a specific version. +- Update Schemas: These are update files they should be ran in order of version to get to the latest schema. +- Patch Schemas: These are for development and should be ran from the lastest available update schema or initial schema. These eventually get condensed into `Update Schemas` and then deleted when updated to a new version. +- Bundled Schemas: These are demo reference files to allow servers to be able to roll their own shops, distributions gachas and scenarios set ups. + +Note: Patch schemas are subject to change! You should only be using them if you are following along with development. + ## Resources - [Quest and Scenario Binary Files](https://files.catbox.moe/xf0l7w.7z) diff --git a/bin/quests/desktop.ini b/bin/quests/desktop.ini deleted file mode 100644 index a71744976..000000000 --- a/bin/quests/desktop.ini +++ /dev/null @@ -1,2372 +0,0 @@ -[LocalizedFileNames] -23484d1.bin=@23484d1.bin,0 -23484d2.bin=@23484d2.bin,0 -23484n0.bin=@23484n0.bin,0 -23484n1.bin=@23484n1.bin,0 -23484n2.bin=@23484n2.bin,0 -23485d0.bin=@23485d0.bin,0 -23485d1.bin=@23485d1.bin,0 -23485d2.bin=@23485d2.bin,0 -23485n0.bin=@23485n0.bin,0 -23485n1.bin=@23485n1.bin,0 -23485n2.bin=@23485n2.bin,0 -23486d0.bin=@23486d0.bin,0 -23486d1.bin=@23486d1.bin,0 -23486d2.bin=@23486d2.bin,0 -23486n0.bin=@23486n0.bin,0 -23486n1.bin=@23486n1.bin,0 -23486n2.bin=@23486n2.bin,0 -23487d0.bin=@23487d0.bin,0 -23487d1.bin=@23487d1.bin,0 -23487d2.bin=@23487d2.bin,0 -23487n0.bin=@23487n0.bin,0 -23487n1.bin=@23487n1.bin,0 -23487n2.bin=@23487n2.bin,0 -23488d0.bin=@23488d0.bin,0 -23488d1.bin=@23488d1.bin,0 -23488d2.bin=@23488d2.bin,0 -23488n0.bin=@23488n0.bin,0 -23488n1.bin=@23488n1.bin,0 -23488n2.bin=@23488n2.bin,0 -23489d0.bin=@23489d0.bin,0 -23489d1.bin=@23489d1.bin,0 -23489d2.bin=@23489d2.bin,0 -23489n0.bin=@23489n0.bin,0 -23489n1.bin=@23489n1.bin,0 -23489n2.bin=@23489n2.bin,0 -23490d0.bin=@23490d0.bin,0 -23490d1.bin=@23490d1.bin,0 -23490d2.bin=@23490d2.bin,0 -23490n0.bin=@23490n0.bin,0 -23490n1.bin=@23490n1.bin,0 -23490n2.bin=@23490n2.bin,0 -23491d0.bin=@23491d0.bin,0 -23491d1.bin=@23491d1.bin,0 -23491d2.bin=@23491d2.bin,0 -23491n0.bin=@23491n0.bin,0 -23491n1.bin=@23491n1.bin,0 -23491n2.bin=@23491n2.bin,0 -23492d0.bin=@23492d0.bin,0 -23492d1.bin=@23492d1.bin,0 -23492d2.bin=@23492d2.bin,0 -23492n0.bin=@23492n0.bin,0 -23492n1.bin=@23492n1.bin,0 -23492n2.bin=@23492n2.bin,0 -23484d0.bin=@23484d0.bin,0 -23585d0.bin=@23585d0.bin,0 -23585d1.bin=@23585d1.bin,0 -23585d2.bin=@23585d2.bin,0 -23585n0.bin=@23585n0.bin,0 -23585n1.bin=@23585n1.bin,0 -23585n2.bin=@23585n2.bin,0 -23586d0.bin=@23586d0.bin,0 -23586d1.bin=@23586d1.bin,0 -23586d2.bin=@23586d2.bin,0 -23586n0.bin=@23586n0.bin,0 -23586n1.bin=@23586n1.bin,0 -23586n2.bin=@23586n2.bin,0 -23587d0.bin=@23587d0.bin,0 -23587d1.bin=@23587d1.bin,0 -23587d2.bin=@23587d2.bin,0 -23587n0.bin=@23587n0.bin,0 -23587n1.bin=@23587n1.bin,0 -23587n2.bin=@23587n2.bin,0 -23588d0.bin=@23588d0.bin,0 -23588d1.bin=@23588d1.bin,0 -23588d2.bin=@23588d2.bin,0 -23588n0.bin=@23588n0.bin,0 -23588n1.bin=@23588n1.bin,0 -23588n2.bin=@23588n2.bin,0 -23589d0.bin=@23589d0.bin,0 -23589d1.bin=@23589d1.bin,0 -23589d2.bin=@23589d2.bin,0 -23589n0.bin=@23589n0.bin,0 -23589n1.bin=@23589n1.bin,0 -23589n2.bin=@23589n2.bin,0 -23590d0.bin=@23590d0.bin,0 -23590d1.bin=@23590d1.bin,0 -23590d2.bin=@23590d2.bin,0 -23590n0.bin=@23590n0.bin,0 -23590n1.bin=@23590n1.bin,0 -23590n2.bin=@23590n2.bin,0 -23591d0.bin=@23591d0.bin,0 -23591d1.bin=@23591d1.bin,0 -23591d2.bin=@23591d2.bin,0 -23591n0.bin=@23591n0.bin,0 -23591n1.bin=@23591n1.bin,0 -23591n2.bin=@23591n2.bin,0 -23592d0.bin=@23592d0.bin,0 -23592d1.bin=@23592d1.bin,0 -23592d2.bin=@23592d2.bin,0 -23592n0.bin=@23592n0.bin,0 -23592n1.bin=@23592n1.bin,0 -23592n2.bin=@23592n2.bin,0 -23593d0.bin=@23593d0.bin,0 -23593d1.bin=@23593d1.bin,0 -23593d2.bin=@23593d2.bin,0 -23593n0.bin=@23593n0.bin,0 -23593n1.bin=@23593n1.bin,0 -23593n2.bin=@23593n2.bin,0 -23594d0.bin=@23594d0.bin,0 -23594d1.bin=@23594d1.bin,0 -23594d2.bin=@23594d2.bin,0 -23594n0.bin=@23594n0.bin,0 -23594n1.bin=@23594n1.bin,0 -23594n2.bin=@23594n2.bin,0 -23595d0.bin=@23595d0.bin,0 -23595d1.bin=@23595d1.bin,0 -23595d2.bin=@23595d2.bin,0 -23595n0.bin=@23595n0.bin,0 -23595n1.bin=@23595n1.bin,0 -23595n2.bin=@23595n2.bin,0 -23596d0.bin=@23596d0.bin,0 -23596d1.bin=@23596d1.bin,0 -23596d2.bin=@23596d2.bin,0 -23596n0.bin=@23596n0.bin,0 -23596n1.bin=@23596n1.bin,0 -23596n2.bin=@23596n2.bin,0 -23597d0.bin=@23597d0.bin,0 -23597d1.bin=@23597d1.bin,0 -23597d2.bin=@23597d2.bin,0 -23597n0.bin=@23597n0.bin,0 -23597n1.bin=@23597n1.bin,0 -23597n2.bin=@23597n2.bin,0 -23598d0.bin=@23598d0.bin,0 -23598d1.bin=@23598d1.bin,0 -23598d2.bin=@23598d2.bin,0 -23598n0.bin=@23598n0.bin,0 -23598n1.bin=@23598n1.bin,0 -23598n2.bin=@23598n2.bin,0 -23599d0.bin=@23599d0.bin,0 -23599d1.bin=@23599d1.bin,0 -23599d2.bin=@23599d2.bin,0 -23599n0.bin=@23599n0.bin,0 -23599n1.bin=@23599n1.bin,0 -23599n2.bin=@23599n2.bin,0 -23601d0.bin=@23601d0.bin,0 -23601d1.bin=@23601d1.bin,0 -23601d2.bin=@23601d2.bin,0 -23601n0.bin=@23601n0.bin,0 -23601n1.bin=@23601n1.bin,0 -23601n2.bin=@23601n2.bin,0 -23602d0.bin=@23602d0.bin,0 -23602d1.bin=@23602d1.bin,0 -23602d2.bin=@23602d2.bin,0 -23602n0.bin=@23602n0.bin,0 -23602n1.bin=@23602n1.bin,0 -23602n2.bin=@23602n2.bin,0 -23603d0.bin=@23603d0.bin,0 -23603d1.bin=@23603d1.bin,0 -23603d2.bin=@23603d2.bin,0 -23603n0.bin=@23603n0.bin,0 -23603n1.bin=@23603n1.bin,0 -23603n2.bin=@23603n2.bin,0 -23604d0.bin=@23604d0.bin,0 -23604d1.bin=@23604d1.bin,0 -23604d2.bin=@23604d2.bin,0 -23604n0.bin=@23604n0.bin,0 -23604n1.bin=@23604n1.bin,0 -23604n2.bin=@23604n2.bin,0 -23605d0.bin=@23605d0.bin,0 -23605d1.bin=@23605d1.bin,0 -23605d2.bin=@23605d2.bin,0 -23605n0.bin=@23605n0.bin,0 -23605n1.bin=@23605n1.bin,0 -23605n2.bin=@23605n2.bin,0 -40241n0.bin=@40241n0.bin,0 -40241n1.bin=@40241n1.bin,0 -40241n2.bin=@40241n2.bin,0 -54594d0.bin=@54594d0.bin,0 -54594d1.bin=@54594d1.bin,0 -54594d2.bin=@54594d2.bin,0 -54594n0.bin=@54594n0.bin,0 -54594n1.bin=@54594n1.bin,0 -54594n2.bin=@54594n2.bin,0 -54603d0.bin=@54603d0.bin,0 -54603d1.bin=@54603d1.bin,0 -54603d2.bin=@54603d2.bin,0 -54603n0.bin=@54603n0.bin,0 -54603n1.bin=@54603n1.bin,0 -54603n2.bin=@54603n2.bin,0 -40241d0.bin=@40241d0.bin,0 -40241d1.bin=@40241d1.bin,0 -40241d2.bin=@40241d2.bin,0 -23493n0.bin=@23493n0.bin,0 -23493n1.bin=@23493n1.bin,0 -23493n2.bin=@23493n2.bin,0 -23494d0.bin=@23494d0.bin,0 -23494d1.bin=@23494d1.bin,0 -23494d2.bin=@23494d2.bin,0 -23494n0.bin=@23494n0.bin,0 -23494n1.bin=@23494n1.bin,0 -23494n2.bin=@23494n2.bin,0 -23495d0.bin=@23495d0.bin,0 -23495d1.bin=@23495d1.bin,0 -23495d2.bin=@23495d2.bin,0 -23495n0.bin=@23495n0.bin,0 -23495n1.bin=@23495n1.bin,0 -23495n2.bin=@23495n2.bin,0 -23496d0.bin=@23496d0.bin,0 -23496d1.bin=@23496d1.bin,0 -23496d2.bin=@23496d2.bin,0 -23496n0.bin=@23496n0.bin,0 -23496n1.bin=@23496n1.bin,0 -23496n2.bin=@23496n2.bin,0 -23497d0.bin=@23497d0.bin,0 -23497d1.bin=@23497d1.bin,0 -23497d2.bin=@23497d2.bin,0 -23497n0.bin=@23497n0.bin,0 -23497n1.bin=@23497n1.bin,0 -23497n2.bin=@23497n2.bin,0 -23498d0.bin=@23498d0.bin,0 -23498d1.bin=@23498d1.bin,0 -23498d2.bin=@23498d2.bin,0 -23498n0.bin=@23498n0.bin,0 -23498n1.bin=@23498n1.bin,0 -23498n2.bin=@23498n2.bin,0 -23499d0.bin=@23499d0.bin,0 -23499d1.bin=@23499d1.bin,0 -23499d2.bin=@23499d2.bin,0 -23499n0.bin=@23499n0.bin,0 -23499n1.bin=@23499n1.bin,0 -23499n2.bin=@23499n2.bin,0 -23513d0.bin=@23513d0.bin,0 -23513d1.bin=@23513d1.bin,0 -23513d2.bin=@23513d2.bin,0 -23513n0.bin=@23513n0.bin,0 -23513n1.bin=@23513n1.bin,0 -23513n2.bin=@23513n2.bin,0 -23528d0.bin=@23528d0.bin,0 -23528d1.bin=@23528d1.bin,0 -23528d2.bin=@23528d2.bin,0 -23528n0.bin=@23528n0.bin,0 -23528n1.bin=@23528n1.bin,0 -23528n2.bin=@23528n2.bin,0 -23643d0.bin=@23643d0.bin,0 -23643d1.bin=@23643d1.bin,0 -23643d2.bin=@23643d2.bin,0 -23643n0.bin=@23643n0.bin,0 -23643n1.bin=@23643n1.bin,0 -23643n2.bin=@23643n2.bin,0 -23667d0.bin=@23667d0.bin,0 -23667d1.bin=@23667d1.bin,0 -23667d2.bin=@23667d2.bin,0 -23667n0.bin=@23667n0.bin,0 -23667n1.bin=@23667n1.bin,0 -23667n2.bin=@23667n2.bin,0 -55679d0.bin=@55679d0.bin,0 -55679d1.bin=@55679d1.bin,0 -55679d2.bin=@55679d2.bin,0 -55679n0.bin=@55679n0.bin,0 -55679n1.bin=@55679n1.bin,0 -55679n2.bin=@55679n2.bin,0 -55680d0.bin=@55680d0.bin,0 -55680d1.bin=@55680d1.bin,0 -55680d2.bin=@55680d2.bin,0 -55680n0.bin=@55680n0.bin,0 -55680n1.bin=@55680n1.bin,0 -55680n2.bin=@55680n2.bin,0 -55772d0.bin=@55772d0.bin,0 -55772d1.bin=@55772d1.bin,0 -55772d2.bin=@55772d2.bin,0 -55772n0.bin=@55772n0.bin,0 -55772n1.bin=@55772n1.bin,0 -55772n2.bin=@55772n2.bin,0 -55916d0.bin=@55916d0.bin,0 -55916d1.bin=@55916d1.bin,0 -55916d2.bin=@55916d2.bin,0 -55916n0.bin=@55916n0.bin,0 -55916n1.bin=@55916n1.bin,0 -55916n2.bin=@55916n2.bin,0 -23493d0.bin=@23493d0.bin,0 -23493d1.bin=@23493d1.bin,0 -23493d2.bin=@23493d2.bin,0 -23199n2.bin=@23199n2.bin,0 -23201d0.bin=@23201d0.bin,0 -23201d1.bin=@23201d1.bin,0 -23201d2.bin=@23201d2.bin,0 -23201n0.bin=@23201n0.bin,0 -23201n1.bin=@23201n1.bin,0 -23201n2.bin=@23201n2.bin,0 -23202d0.bin=@23202d0.bin,0 -23202d1.bin=@23202d1.bin,0 -23202d2.bin=@23202d2.bin,0 -23202n0.bin=@23202n0.bin,0 -23202n1.bin=@23202n1.bin,0 -23202n2.bin=@23202n2.bin,0 -23203d0.bin=@23203d0.bin,0 -23203d1.bin=@23203d1.bin,0 -23203d2.bin=@23203d2.bin,0 -23203n0.bin=@23203n0.bin,0 -23203n1.bin=@23203n1.bin,0 -23203n2.bin=@23203n2.bin,0 -23204d0.bin=@23204d0.bin,0 -23204d1.bin=@23204d1.bin,0 -23204d2.bin=@23204d2.bin,0 -23204n0.bin=@23204n0.bin,0 -23204n1.bin=@23204n1.bin,0 -23204n2.bin=@23204n2.bin,0 -23205d0.bin=@23205d0.bin,0 -23205d1.bin=@23205d1.bin,0 -23205d2.bin=@23205d2.bin,0 -23205n0.bin=@23205n0.bin,0 -23205n1.bin=@23205n1.bin,0 -23205n2.bin=@23205n2.bin,0 -23206d0.bin=@23206d0.bin,0 -23206d1.bin=@23206d1.bin,0 -23206d2.bin=@23206d2.bin,0 -23206n0.bin=@23206n0.bin,0 -23206n1.bin=@23206n1.bin,0 -23206n2.bin=@23206n2.bin,0 -23207d0.bin=@23207d0.bin,0 -23207d1.bin=@23207d1.bin,0 -23207d2.bin=@23207d2.bin,0 -23207n0.bin=@23207n0.bin,0 -23207n1.bin=@23207n1.bin,0 -23207n2.bin=@23207n2.bin,0 -23208d0.bin=@23208d0.bin,0 -23208d1.bin=@23208d1.bin,0 -23208d2.bin=@23208d2.bin,0 -23208n0.bin=@23208n0.bin,0 -23208n1.bin=@23208n1.bin,0 -23208n2.bin=@23208n2.bin,0 -23209d0.bin=@23209d0.bin,0 -23209d1.bin=@23209d1.bin,0 -23209d2.bin=@23209d2.bin,0 -23209n0.bin=@23209n0.bin,0 -23209n1.bin=@23209n1.bin,0 -23209n2.bin=@23209n2.bin,0 -23210d0.bin=@23210d0.bin,0 -23210d1.bin=@23210d1.bin,0 -23210d2.bin=@23210d2.bin,0 -23210n0.bin=@23210n0.bin,0 -23210n1.bin=@23210n1.bin,0 -23210n2.bin=@23210n2.bin,0 -23253d0.bin=@23253d0.bin,0 -23253d1.bin=@23253d1.bin,0 -23253d2.bin=@23253d2.bin,0 -23253n0.bin=@23253n0.bin,0 -23253n1.bin=@23253n1.bin,0 -23253n2.bin=@23253n2.bin,0 -23254d0.bin=@23254d0.bin,0 -23254d1.bin=@23254d1.bin,0 -23254d2.bin=@23254d2.bin,0 -23254n0.bin=@23254n0.bin,0 -23254n1.bin=@23254n1.bin,0 -23254n2.bin=@23254n2.bin,0 -23269d0.bin=@23269d0.bin,0 -23269d1.bin=@23269d1.bin,0 -23269d2.bin=@23269d2.bin,0 -23269n0.bin=@23269n0.bin,0 -23269n1.bin=@23269n1.bin,0 -23269n2.bin=@23269n2.bin,0 -23270d0.bin=@23270d0.bin,0 -23270d1.bin=@23270d1.bin,0 -23270d2.bin=@23270d2.bin,0 -23270n0.bin=@23270n0.bin,0 -23270n1.bin=@23270n1.bin,0 -23270n2.bin=@23270n2.bin,0 -23271d0.bin=@23271d0.bin,0 -23271d1.bin=@23271d1.bin,0 -23271d2.bin=@23271d2.bin,0 -23271n0.bin=@23271n0.bin,0 -23271n1.bin=@23271n1.bin,0 -23271n2.bin=@23271n2.bin,0 -23272d0.bin=@23272d0.bin,0 -23272d1.bin=@23272d1.bin,0 -23272d2.bin=@23272d2.bin,0 -23272n0.bin=@23272n0.bin,0 -23272n1.bin=@23272n1.bin,0 -23272n2.bin=@23272n2.bin,0 -23273d0.bin=@23273d0.bin,0 -23273d1.bin=@23273d1.bin,0 -23273d2.bin=@23273d2.bin,0 -23273n0.bin=@23273n0.bin,0 -23273n1.bin=@23273n1.bin,0 -23273n2.bin=@23273n2.bin,0 -23274d0.bin=@23274d0.bin,0 -23274d1.bin=@23274d1.bin,0 -23274d2.bin=@23274d2.bin,0 -23274n0.bin=@23274n0.bin,0 -23274n1.bin=@23274n1.bin,0 -23274n2.bin=@23274n2.bin,0 -23280d0.bin=@23280d0.bin,0 -23280d1.bin=@23280d1.bin,0 -23280d2.bin=@23280d2.bin,0 -23280n0.bin=@23280n0.bin,0 -23280n1.bin=@23280n1.bin,0 -23280n2.bin=@23280n2.bin,0 -23281d0.bin=@23281d0.bin,0 -23281d1.bin=@23281d1.bin,0 -23281d2.bin=@23281d2.bin,0 -23281n0.bin=@23281n0.bin,0 -23281n1.bin=@23281n1.bin,0 -23281n2.bin=@23281n2.bin,0 -23303d0.bin=@23303d0.bin,0 -23303d1.bin=@23303d1.bin,0 -23303d2.bin=@23303d2.bin,0 -23303n0.bin=@23303n0.bin,0 -23303n1.bin=@23303n1.bin,0 -23303n2.bin=@23303n2.bin,0 -23304d0.bin=@23304d0.bin,0 -23304d1.bin=@23304d1.bin,0 -23304d2.bin=@23304d2.bin,0 -23304n0.bin=@23304n0.bin,0 -23304n1.bin=@23304n1.bin,0 -23304n2.bin=@23304n2.bin,0 -23323d0.bin=@23323d0.bin,0 -23323d1.bin=@23323d1.bin,0 -23323d2.bin=@23323d2.bin,0 -23323n0.bin=@23323n0.bin,0 -23323n1.bin=@23323n1.bin,0 -23323n2.bin=@23323n2.bin,0 -23326d0.bin=@23326d0.bin,0 -23326d1.bin=@23326d1.bin,0 -23326d2.bin=@23326d2.bin,0 -23326n0.bin=@23326n0.bin,0 -23326n1.bin=@23326n1.bin,0 -23326n2.bin=@23326n2.bin,0 -23199d0.bin=@23199d0.bin,0 -23199d1.bin=@23199d1.bin,0 -23199d2.bin=@23199d2.bin,0 -23199n0.bin=@23199n0.bin,0 -23199n1.bin=@23199n1.bin,0 -23211d2.bin=@23211d2.bin,0 -23211n0.bin=@23211n0.bin,0 -23211n1.bin=@23211n1.bin,0 -23211n2.bin=@23211n2.bin,0 -23212d0.bin=@23212d0.bin,0 -23212d1.bin=@23212d1.bin,0 -23212d2.bin=@23212d2.bin,0 -23212n0.bin=@23212n0.bin,0 -23212n1.bin=@23212n1.bin,0 -23212n2.bin=@23212n2.bin,0 -23213d0.bin=@23213d0.bin,0 -23213d1.bin=@23213d1.bin,0 -23213d2.bin=@23213d2.bin,0 -23213n0.bin=@23213n0.bin,0 -23213n1.bin=@23213n1.bin,0 -23213n2.bin=@23213n2.bin,0 -23214d0.bin=@23214d0.bin,0 -23214d1.bin=@23214d1.bin,0 -23214d2.bin=@23214d2.bin,0 -23214n0.bin=@23214n0.bin,0 -23214n1.bin=@23214n1.bin,0 -23214n2.bin=@23214n2.bin,0 -23215d0.bin=@23215d0.bin,0 -23215d1.bin=@23215d1.bin,0 -23215d2.bin=@23215d2.bin,0 -23215n0.bin=@23215n0.bin,0 -23215n1.bin=@23215n1.bin,0 -23215n2.bin=@23215n2.bin,0 -23216d0.bin=@23216d0.bin,0 -23216d1.bin=@23216d1.bin,0 -23216d2.bin=@23216d2.bin,0 -23216n0.bin=@23216n0.bin,0 -23216n1.bin=@23216n1.bin,0 -23216n2.bin=@23216n2.bin,0 -23217d0.bin=@23217d0.bin,0 -23217d1.bin=@23217d1.bin,0 -23217d2.bin=@23217d2.bin,0 -23217n0.bin=@23217n0.bin,0 -23217n1.bin=@23217n1.bin,0 -23217n2.bin=@23217n2.bin,0 -23218d0.bin=@23218d0.bin,0 -23218d1.bin=@23218d1.bin,0 -23218d2.bin=@23218d2.bin,0 -23218n0.bin=@23218n0.bin,0 -23218n1.bin=@23218n1.bin,0 -23218n2.bin=@23218n2.bin,0 -23219d0.bin=@23219d0.bin,0 -23219d1.bin=@23219d1.bin,0 -23219d2.bin=@23219d2.bin,0 -23219n0.bin=@23219n0.bin,0 -23219n1.bin=@23219n1.bin,0 -23219n2.bin=@23219n2.bin,0 -23220d0.bin=@23220d0.bin,0 -23220d1.bin=@23220d1.bin,0 -23220d2.bin=@23220d2.bin,0 -23220n0.bin=@23220n0.bin,0 -23220n1.bin=@23220n1.bin,0 -23220n2.bin=@23220n2.bin,0 -23221d0.bin=@23221d0.bin,0 -23221d1.bin=@23221d1.bin,0 -23221d2.bin=@23221d2.bin,0 -23221n0.bin=@23221n0.bin,0 -23221n1.bin=@23221n1.bin,0 -23221n2.bin=@23221n2.bin,0 -23223d0.bin=@23223d0.bin,0 -23223d1.bin=@23223d1.bin,0 -23223d2.bin=@23223d2.bin,0 -23223n0.bin=@23223n0.bin,0 -23223n1.bin=@23223n1.bin,0 -23223n2.bin=@23223n2.bin,0 -23255d0.bin=@23255d0.bin,0 -23255d1.bin=@23255d1.bin,0 -23255d2.bin=@23255d2.bin,0 -23255n0.bin=@23255n0.bin,0 -23255n1.bin=@23255n1.bin,0 -23255n2.bin=@23255n2.bin,0 -23256d0.bin=@23256d0.bin,0 -23256d1.bin=@23256d1.bin,0 -23256d2.bin=@23256d2.bin,0 -23256n0.bin=@23256n0.bin,0 -23256n1.bin=@23256n1.bin,0 -23256n2.bin=@23256n2.bin,0 -23282d0.bin=@23282d0.bin,0 -23282d1.bin=@23282d1.bin,0 -23282d2.bin=@23282d2.bin,0 -23282n0.bin=@23282n0.bin,0 -23282n1.bin=@23282n1.bin,0 -23282n2.bin=@23282n2.bin,0 -23283d0.bin=@23283d0.bin,0 -23283d1.bin=@23283d1.bin,0 -23283d2.bin=@23283d2.bin,0 -23283n0.bin=@23283n0.bin,0 -23283n1.bin=@23283n1.bin,0 -23283n2.bin=@23283n2.bin,0 -23305d0.bin=@23305d0.bin,0 -23305d1.bin=@23305d1.bin,0 -23305d2.bin=@23305d2.bin,0 -23305n0.bin=@23305n0.bin,0 -23305n1.bin=@23305n1.bin,0 -23305n2.bin=@23305n2.bin,0 -23320d0.bin=@23320d0.bin,0 -23320d1.bin=@23320d1.bin,0 -23320d2.bin=@23320d2.bin,0 -23320n0.bin=@23320n0.bin,0 -23320n1.bin=@23320n1.bin,0 -23320n2.bin=@23320n2.bin,0 -23321d0.bin=@23321d0.bin,0 -23321d1.bin=@23321d1.bin,0 -23321d2.bin=@23321d2.bin,0 -23321n0.bin=@23321n0.bin,0 -23321n1.bin=@23321n1.bin,0 -23321n2.bin=@23321n2.bin,0 -23324d0.bin=@23324d0.bin,0 -23324d1.bin=@23324d1.bin,0 -23324d2.bin=@23324d2.bin,0 -23324n0.bin=@23324n0.bin,0 -23324n1.bin=@23324n1.bin,0 -23324n2.bin=@23324n2.bin,0 -23350d0.bin=@23350d0.bin,0 -23350d1.bin=@23350d1.bin,0 -23350d2.bin=@23350d2.bin,0 -23350n0.bin=@23350n0.bin,0 -23350n1.bin=@23350n1.bin,0 -23350n2.bin=@23350n2.bin,0 -23351d0.bin=@23351d0.bin,0 -23351d1.bin=@23351d1.bin,0 -23351d2.bin=@23351d2.bin,0 -23351n0.bin=@23351n0.bin,0 -23351n1.bin=@23351n1.bin,0 -23351n2.bin=@23351n2.bin,0 -23369d0.bin=@23369d0.bin,0 -23369d1.bin=@23369d1.bin,0 -23369d2.bin=@23369d2.bin,0 -23369n0.bin=@23369n0.bin,0 -23369n1.bin=@23369n1.bin,0 -23369n2.bin=@23369n2.bin,0 -23370d0.bin=@23370d0.bin,0 -23370d1.bin=@23370d1.bin,0 -23370d2.bin=@23370d2.bin,0 -23370n0.bin=@23370n0.bin,0 -23370n1.bin=@23370n1.bin,0 -23370n2.bin=@23370n2.bin,0 -23399d0.bin=@23399d0.bin,0 -23399d1.bin=@23399d1.bin,0 -23399d2.bin=@23399d2.bin,0 -23399n0.bin=@23399n0.bin,0 -23399n1.bin=@23399n1.bin,0 -23399n2.bin=@23399n2.bin,0 -23401d0.bin=@23401d0.bin,0 -23401d1.bin=@23401d1.bin,0 -23401d2.bin=@23401d2.bin,0 -23401n0.bin=@23401n0.bin,0 -23401n1.bin=@23401n1.bin,0 -23401n2.bin=@23401n2.bin,0 -23211d0.bin=@23211d0.bin,0 -23211d1.bin=@23211d1.bin,0 -23224n1.bin=@23224n1.bin,0 -23224n2.bin=@23224n2.bin,0 -23225d0.bin=@23225d0.bin,0 -23225d1.bin=@23225d1.bin,0 -23225d2.bin=@23225d2.bin,0 -23225n0.bin=@23225n0.bin,0 -23225n1.bin=@23225n1.bin,0 -23225n2.bin=@23225n2.bin,0 -23226d0.bin=@23226d0.bin,0 -23226d1.bin=@23226d1.bin,0 -23226d2.bin=@23226d2.bin,0 -23226n0.bin=@23226n0.bin,0 -23226n1.bin=@23226n1.bin,0 -23226n2.bin=@23226n2.bin,0 -23227d0.bin=@23227d0.bin,0 -23227d1.bin=@23227d1.bin,0 -23227d2.bin=@23227d2.bin,0 -23227n0.bin=@23227n0.bin,0 -23227n1.bin=@23227n1.bin,0 -23227n2.bin=@23227n2.bin,0 -23228d0.bin=@23228d0.bin,0 -23228d1.bin=@23228d1.bin,0 -23228d2.bin=@23228d2.bin,0 -23228n0.bin=@23228n0.bin,0 -23228n1.bin=@23228n1.bin,0 -23228n2.bin=@23228n2.bin,0 -23229d0.bin=@23229d0.bin,0 -23229d1.bin=@23229d1.bin,0 -23229d2.bin=@23229d2.bin,0 -23229n0.bin=@23229n0.bin,0 -23229n1.bin=@23229n1.bin,0 -23229n2.bin=@23229n2.bin,0 -23230d0.bin=@23230d0.bin,0 -23230d1.bin=@23230d1.bin,0 -23230d2.bin=@23230d2.bin,0 -23230n0.bin=@23230n0.bin,0 -23230n1.bin=@23230n1.bin,0 -23230n2.bin=@23230n2.bin,0 -23231d0.bin=@23231d0.bin,0 -23231d1.bin=@23231d1.bin,0 -23231d2.bin=@23231d2.bin,0 -23231n0.bin=@23231n0.bin,0 -23231n1.bin=@23231n1.bin,0 -23231n2.bin=@23231n2.bin,0 -23232d0.bin=@23232d0.bin,0 -23232d1.bin=@23232d1.bin,0 -23232d2.bin=@23232d2.bin,0 -23232n0.bin=@23232n0.bin,0 -23232n1.bin=@23232n1.bin,0 -23232n2.bin=@23232n2.bin,0 -23233d0.bin=@23233d0.bin,0 -23233d1.bin=@23233d1.bin,0 -23233d2.bin=@23233d2.bin,0 -23233n0.bin=@23233n0.bin,0 -23233n1.bin=@23233n1.bin,0 -23233n2.bin=@23233n2.bin,0 -23238d0.bin=@23238d0.bin,0 -23238d1.bin=@23238d1.bin,0 -23238d2.bin=@23238d2.bin,0 -23238n0.bin=@23238n0.bin,0 -23238n1.bin=@23238n1.bin,0 -23238n2.bin=@23238n2.bin,0 -23239d0.bin=@23239d0.bin,0 -23239d1.bin=@23239d1.bin,0 -23239d2.bin=@23239d2.bin,0 -23239n0.bin=@23239n0.bin,0 -23239n1.bin=@23239n1.bin,0 -23239n2.bin=@23239n2.bin,0 -23257d0.bin=@23257d0.bin,0 -23257d1.bin=@23257d1.bin,0 -23257d2.bin=@23257d2.bin,0 -23257n0.bin=@23257n0.bin,0 -23257n1.bin=@23257n1.bin,0 -23257n2.bin=@23257n2.bin,0 -23258d0.bin=@23258d0.bin,0 -23258d1.bin=@23258d1.bin,0 -23258d2.bin=@23258d2.bin,0 -23258n0.bin=@23258n0.bin,0 -23258n1.bin=@23258n1.bin,0 -23258n2.bin=@23258n2.bin,0 -23284d0.bin=@23284d0.bin,0 -23284d1.bin=@23284d1.bin,0 -23284d2.bin=@23284d2.bin,0 -23284n0.bin=@23284n0.bin,0 -23284n1.bin=@23284n1.bin,0 -23284n2.bin=@23284n2.bin,0 -23285d0.bin=@23285d0.bin,0 -23285d1.bin=@23285d1.bin,0 -23285d2.bin=@23285d2.bin,0 -23285n0.bin=@23285n0.bin,0 -23285n1.bin=@23285n1.bin,0 -23285n2.bin=@23285n2.bin,0 -23311d0.bin=@23311d0.bin,0 -23311d1.bin=@23311d1.bin,0 -23311d2.bin=@23311d2.bin,0 -23311n0.bin=@23311n0.bin,0 -23311n1.bin=@23311n1.bin,0 -23311n2.bin=@23311n2.bin,0 -23346d0.bin=@23346d0.bin,0 -23346d1.bin=@23346d1.bin,0 -23346d2.bin=@23346d2.bin,0 -23346n0.bin=@23346n0.bin,0 -23346n1.bin=@23346n1.bin,0 -23346n2.bin=@23346n2.bin,0 -23347d0.bin=@23347d0.bin,0 -23347d1.bin=@23347d1.bin,0 -23347d2.bin=@23347d2.bin,0 -23347n0.bin=@23347n0.bin,0 -23347n1.bin=@23347n1.bin,0 -23347n2.bin=@23347n2.bin,0 -23371d0.bin=@23371d0.bin,0 -23371d1.bin=@23371d1.bin,0 -23371d2.bin=@23371d2.bin,0 -23371n0.bin=@23371n0.bin,0 -23371n1.bin=@23371n1.bin,0 -23371n2.bin=@23371n2.bin,0 -23372d0.bin=@23372d0.bin,0 -23372d1.bin=@23372d1.bin,0 -23372d2.bin=@23372d2.bin,0 -23372n0.bin=@23372n0.bin,0 -23372n1.bin=@23372n1.bin,0 -23372n2.bin=@23372n2.bin,0 -23386d0.bin=@23386d0.bin,0 -23386d1.bin=@23386d1.bin,0 -23386d2.bin=@23386d2.bin,0 -23386n0.bin=@23386n0.bin,0 -23386n1.bin=@23386n1.bin,0 -23386n2.bin=@23386n2.bin,0 -23387d0.bin=@23387d0.bin,0 -23387d1.bin=@23387d1.bin,0 -23387d2.bin=@23387d2.bin,0 -23387n0.bin=@23387n0.bin,0 -23387n1.bin=@23387n1.bin,0 -23387n2.bin=@23387n2.bin,0 -23224d0.bin=@23224d0.bin,0 -23224d1.bin=@23224d1.bin,0 -23224d2.bin=@23224d2.bin,0 -23224n0.bin=@23224n0.bin,0 -23236n0.bin=@23236n0.bin,0 -23236n1.bin=@23236n1.bin,0 -23236n2.bin=@23236n2.bin,0 -23237d0.bin=@23237d0.bin,0 -23237d1.bin=@23237d1.bin,0 -23237d2.bin=@23237d2.bin,0 -23237n0.bin=@23237n0.bin,0 -23237n1.bin=@23237n1.bin,0 -23237n2.bin=@23237n2.bin,0 -23240d0.bin=@23240d0.bin,0 -23240d1.bin=@23240d1.bin,0 -23240d2.bin=@23240d2.bin,0 -23240n0.bin=@23240n0.bin,0 -23240n1.bin=@23240n1.bin,0 -23240n2.bin=@23240n2.bin,0 -23241d0.bin=@23241d0.bin,0 -23241d1.bin=@23241d1.bin,0 -23241d2.bin=@23241d2.bin,0 -23241n0.bin=@23241n0.bin,0 -23241n1.bin=@23241n1.bin,0 -23241n2.bin=@23241n2.bin,0 -23250d0.bin=@23250d0.bin,0 -23250d1.bin=@23250d1.bin,0 -23250d2.bin=@23250d2.bin,0 -23250n0.bin=@23250n0.bin,0 -23250n1.bin=@23250n1.bin,0 -23250n2.bin=@23250n2.bin,0 -23251d0.bin=@23251d0.bin,0 -23251d1.bin=@23251d1.bin,0 -23251d2.bin=@23251d2.bin,0 -23251n0.bin=@23251n0.bin,0 -23251n1.bin=@23251n1.bin,0 -23251n2.bin=@23251n2.bin,0 -23252d0.bin=@23252d0.bin,0 -23252d1.bin=@23252d1.bin,0 -23252d2.bin=@23252d2.bin,0 -23252n0.bin=@23252n0.bin,0 -23252n1.bin=@23252n1.bin,0 -23252n2.bin=@23252n2.bin,0 -23259d0.bin=@23259d0.bin,0 -23259d1.bin=@23259d1.bin,0 -23259d2.bin=@23259d2.bin,0 -23259n0.bin=@23259n0.bin,0 -23259n1.bin=@23259n1.bin,0 -23259n2.bin=@23259n2.bin,0 -23260d0.bin=@23260d0.bin,0 -23260d1.bin=@23260d1.bin,0 -23260d2.bin=@23260d2.bin,0 -23260n0.bin=@23260n0.bin,0 -23260n1.bin=@23260n1.bin,0 -23260n2.bin=@23260n2.bin,0 -23286d0.bin=@23286d0.bin,0 -23286d1.bin=@23286d1.bin,0 -23286d2.bin=@23286d2.bin,0 -23286n0.bin=@23286n0.bin,0 -23286n1.bin=@23286n1.bin,0 -23286n2.bin=@23286n2.bin,0 -23287d0.bin=@23287d0.bin,0 -23287d1.bin=@23287d1.bin,0 -23287d2.bin=@23287d2.bin,0 -23287n0.bin=@23287n0.bin,0 -23287n1.bin=@23287n1.bin,0 -23287n2.bin=@23287n2.bin,0 -23288d0.bin=@23288d0.bin,0 -23288d1.bin=@23288d1.bin,0 -23288d2.bin=@23288d2.bin,0 -23288n0.bin=@23288n0.bin,0 -23288n1.bin=@23288n1.bin,0 -23288n2.bin=@23288n2.bin,0 -23289d0.bin=@23289d0.bin,0 -23289d1.bin=@23289d1.bin,0 -23289d2.bin=@23289d2.bin,0 -23289n0.bin=@23289n0.bin,0 -23289n1.bin=@23289n1.bin,0 -23289n2.bin=@23289n2.bin,0 -23325d0.bin=@23325d0.bin,0 -23325d1.bin=@23325d1.bin,0 -23325d2.bin=@23325d2.bin,0 -23325n0.bin=@23325n0.bin,0 -23325n1.bin=@23325n1.bin,0 -23325n2.bin=@23325n2.bin,0 -23327d0.bin=@23327d0.bin,0 -23327d1.bin=@23327d1.bin,0 -23327d2.bin=@23327d2.bin,0 -23327n0.bin=@23327n0.bin,0 -23327n1.bin=@23327n1.bin,0 -23327n2.bin=@23327n2.bin,0 -23348d0.bin=@23348d0.bin,0 -23348d1.bin=@23348d1.bin,0 -23348d2.bin=@23348d2.bin,0 -23348n0.bin=@23348n0.bin,0 -23348n1.bin=@23348n1.bin,0 -23348n2.bin=@23348n2.bin,0 -23349d0.bin=@23349d0.bin,0 -23349d1.bin=@23349d1.bin,0 -23349d2.bin=@23349d2.bin,0 -23349n0.bin=@23349n0.bin,0 -23349n1.bin=@23349n1.bin,0 -23349n2.bin=@23349n2.bin,0 -23382d0.bin=@23382d0.bin,0 -23382d1.bin=@23382d1.bin,0 -23382d2.bin=@23382d2.bin,0 -23382n0.bin=@23382n0.bin,0 -23382n1.bin=@23382n1.bin,0 -23382n2.bin=@23382n2.bin,0 -23383d0.bin=@23383d0.bin,0 -23383d1.bin=@23383d1.bin,0 -23383d2.bin=@23383d2.bin,0 -23383n0.bin=@23383n0.bin,0 -23383n1.bin=@23383n1.bin,0 -23383n2.bin=@23383n2.bin,0 -23384d0.bin=@23384d0.bin,0 -23384d1.bin=@23384d1.bin,0 -23384d2.bin=@23384d2.bin,0 -23384n0.bin=@23384n0.bin,0 -23384n1.bin=@23384n1.bin,0 -23384n2.bin=@23384n2.bin,0 -23385d0.bin=@23385d0.bin,0 -23385d1.bin=@23385d1.bin,0 -23385d2.bin=@23385d2.bin,0 -23385n0.bin=@23385n0.bin,0 -23385n1.bin=@23385n1.bin,0 -23385n2.bin=@23385n2.bin,0 -23394d0.bin=@23394d0.bin,0 -23394d1.bin=@23394d1.bin,0 -23394d2.bin=@23394d2.bin,0 -23394n0.bin=@23394n0.bin,0 -23394n1.bin=@23394n1.bin,0 -23394n2.bin=@23394n2.bin,0 -23395d0.bin=@23395d0.bin,0 -23395d1.bin=@23395d1.bin,0 -23395d2.bin=@23395d2.bin,0 -23395n0.bin=@23395n0.bin,0 -23395n1.bin=@23395n1.bin,0 -23395n2.bin=@23395n2.bin,0 -23396d0.bin=@23396d0.bin,0 -23396d1.bin=@23396d1.bin,0 -23396d2.bin=@23396d2.bin,0 -23396n0.bin=@23396n0.bin,0 -23396n1.bin=@23396n1.bin,0 -23396n2.bin=@23396n2.bin,0 -23236d0.bin=@23236d0.bin,0 -23236d1.bin=@23236d1.bin,0 -23236d2.bin=@23236d2.bin,0 -23234d2.bin=@23234d2.bin,0 -23234n0.bin=@23234n0.bin,0 -23234n1.bin=@23234n1.bin,0 -23234n2.bin=@23234n2.bin,0 -23235d0.bin=@23235d0.bin,0 -23235d1.bin=@23235d1.bin,0 -23235d2.bin=@23235d2.bin,0 -23235n0.bin=@23235n0.bin,0 -23235n1.bin=@23235n1.bin,0 -23235n2.bin=@23235n2.bin,0 -23246d0.bin=@23246d0.bin,0 -23246d1.bin=@23246d1.bin,0 -23246d2.bin=@23246d2.bin,0 -23246n0.bin=@23246n0.bin,0 -23246n1.bin=@23246n1.bin,0 -23246n2.bin=@23246n2.bin,0 -23247d0.bin=@23247d0.bin,0 -23247d1.bin=@23247d1.bin,0 -23247d2.bin=@23247d2.bin,0 -23247n0.bin=@23247n0.bin,0 -23247n1.bin=@23247n1.bin,0 -23247n2.bin=@23247n2.bin,0 -23248d0.bin=@23248d0.bin,0 -23248d1.bin=@23248d1.bin,0 -23248d2.bin=@23248d2.bin,0 -23248n0.bin=@23248n0.bin,0 -23248n1.bin=@23248n1.bin,0 -23248n2.bin=@23248n2.bin,0 -23249d0.bin=@23249d0.bin,0 -23249d1.bin=@23249d1.bin,0 -23249d2.bin=@23249d2.bin,0 -23249n0.bin=@23249n0.bin,0 -23249n1.bin=@23249n1.bin,0 -23249n2.bin=@23249n2.bin,0 -23261d0.bin=@23261d0.bin,0 -23261d1.bin=@23261d1.bin,0 -23261d2.bin=@23261d2.bin,0 -23261n0.bin=@23261n0.bin,0 -23261n1.bin=@23261n1.bin,0 -23261n2.bin=@23261n2.bin,0 -23262d0.bin=@23262d0.bin,0 -23262d1.bin=@23262d1.bin,0 -23262d2.bin=@23262d2.bin,0 -23262n0.bin=@23262n0.bin,0 -23262n1.bin=@23262n1.bin,0 -23262n2.bin=@23262n2.bin,0 -23267d0.bin=@23267d0.bin,0 -23267d1.bin=@23267d1.bin,0 -23267d2.bin=@23267d2.bin,0 -23267n0.bin=@23267n0.bin,0 -23267n1.bin=@23267n1.bin,0 -23267n2.bin=@23267n2.bin,0 -23268d0.bin=@23268d0.bin,0 -23268d1.bin=@23268d1.bin,0 -23268d2.bin=@23268d2.bin,0 -23268n0.bin=@23268n0.bin,0 -23268n1.bin=@23268n1.bin,0 -23268n2.bin=@23268n2.bin,0 -23275d0.bin=@23275d0.bin,0 -23275d1.bin=@23275d1.bin,0 -23275d2.bin=@23275d2.bin,0 -23275n0.bin=@23275n0.bin,0 -23275n1.bin=@23275n1.bin,0 -23275n2.bin=@23275n2.bin,0 -23309d0.bin=@23309d0.bin,0 -23309d1.bin=@23309d1.bin,0 -23309d2.bin=@23309d2.bin,0 -23309n0.bin=@23309n0.bin,0 -23309n1.bin=@23309n1.bin,0 -23309n2.bin=@23309n2.bin,0 -23310d0.bin=@23310d0.bin,0 -23310d1.bin=@23310d1.bin,0 -23310d2.bin=@23310d2.bin,0 -23310n0.bin=@23310n0.bin,0 -23310n1.bin=@23310n1.bin,0 -23310n2.bin=@23310n2.bin,0 -23345d0.bin=@23345d0.bin,0 -23345d1.bin=@23345d1.bin,0 -23345d2.bin=@23345d2.bin,0 -23345n0.bin=@23345n0.bin,0 -23345n1.bin=@23345n1.bin,0 -23345n2.bin=@23345n2.bin,0 -23397d0.bin=@23397d0.bin,0 -23397d1.bin=@23397d1.bin,0 -23397d2.bin=@23397d2.bin,0 -23397n0.bin=@23397n0.bin,0 -23397n1.bin=@23397n1.bin,0 -23397n2.bin=@23397n2.bin,0 -23398d0.bin=@23398d0.bin,0 -23398d1.bin=@23398d1.bin,0 -23398d2.bin=@23398d2.bin,0 -23398n0.bin=@23398n0.bin,0 -23398n1.bin=@23398n1.bin,0 -23398n2.bin=@23398n2.bin,0 -23403d0.bin=@23403d0.bin,0 -23403d1.bin=@23403d1.bin,0 -23403d2.bin=@23403d2.bin,0 -23403n0.bin=@23403n0.bin,0 -23403n1.bin=@23403n1.bin,0 -23403n2.bin=@23403n2.bin,0 -23404d0.bin=@23404d0.bin,0 -23404d1.bin=@23404d1.bin,0 -23404d2.bin=@23404d2.bin,0 -23404n0.bin=@23404n0.bin,0 -23404n1.bin=@23404n1.bin,0 -23404n2.bin=@23404n2.bin,0 -23425d0.bin=@23425d0.bin,0 -23425d1.bin=@23425d1.bin,0 -23425d2.bin=@23425d2.bin,0 -23425n0.bin=@23425n0.bin,0 -23425n1.bin=@23425n1.bin,0 -23425n2.bin=@23425n2.bin,0 -23426d0.bin=@23426d0.bin,0 -23426d1.bin=@23426d1.bin,0 -23426d2.bin=@23426d2.bin,0 -23426n0.bin=@23426n0.bin,0 -23426n1.bin=@23426n1.bin,0 -23426n2.bin=@23426n2.bin,0 -23433d0.bin=@23433d0.bin,0 -23433d1.bin=@23433d1.bin,0 -23433d2.bin=@23433d2.bin,0 -23433n0.bin=@23433n0.bin,0 -23433n1.bin=@23433n1.bin,0 -23433n2.bin=@23433n2.bin,0 -23434d0.bin=@23434d0.bin,0 -23434d1.bin=@23434d1.bin,0 -23434d2.bin=@23434d2.bin,0 -23434n0.bin=@23434n0.bin,0 -23434n1.bin=@23434n1.bin,0 -23434n2.bin=@23434n2.bin,0 -23234d0.bin=@23234d0.bin,0 -23234d1.bin=@23234d1.bin,0 -23242n0.bin=@23242n0.bin,0 -23242n1.bin=@23242n1.bin,0 -23242n2.bin=@23242n2.bin,0 -23243d0.bin=@23243d0.bin,0 -23243d1.bin=@23243d1.bin,0 -23243d2.bin=@23243d2.bin,0 -23243n0.bin=@23243n0.bin,0 -23243n1.bin=@23243n1.bin,0 -23243n2.bin=@23243n2.bin,0 -23244d0.bin=@23244d0.bin,0 -23244d1.bin=@23244d1.bin,0 -23244d2.bin=@23244d2.bin,0 -23244n0.bin=@23244n0.bin,0 -23244n1.bin=@23244n1.bin,0 -23244n2.bin=@23244n2.bin,0 -23245d0.bin=@23245d0.bin,0 -23245d1.bin=@23245d1.bin,0 -23245d2.bin=@23245d2.bin,0 -23245n0.bin=@23245n0.bin,0 -23245n1.bin=@23245n1.bin,0 -23245n2.bin=@23245n2.bin,0 -23263d0.bin=@23263d0.bin,0 -23263d1.bin=@23263d1.bin,0 -23263d2.bin=@23263d2.bin,0 -23263n0.bin=@23263n0.bin,0 -23263n1.bin=@23263n1.bin,0 -23263n2.bin=@23263n2.bin,0 -23264d0.bin=@23264d0.bin,0 -23264d1.bin=@23264d1.bin,0 -23264d2.bin=@23264d2.bin,0 -23264n0.bin=@23264n0.bin,0 -23264n1.bin=@23264n1.bin,0 -23264n2.bin=@23264n2.bin,0 -23277d0.bin=@23277d0.bin,0 -23277d1.bin=@23277d1.bin,0 -23277d2.bin=@23277d2.bin,0 -23277n0.bin=@23277n0.bin,0 -23277n1.bin=@23277n1.bin,0 -23277n2.bin=@23277n2.bin,0 -23278d0.bin=@23278d0.bin,0 -23278d1.bin=@23278d1.bin,0 -23278d2.bin=@23278d2.bin,0 -23278n0.bin=@23278n0.bin,0 -23278n1.bin=@23278n1.bin,0 -23278n2.bin=@23278n2.bin,0 -23279d0.bin=@23279d0.bin,0 -23279d1.bin=@23279d1.bin,0 -23279d2.bin=@23279d2.bin,0 -23279n0.bin=@23279n0.bin,0 -23279n1.bin=@23279n1.bin,0 -23279n2.bin=@23279n2.bin,0 -23307d0.bin=@23307d0.bin,0 -23307d1.bin=@23307d1.bin,0 -23307d2.bin=@23307d2.bin,0 -23307n0.bin=@23307n0.bin,0 -23307n1.bin=@23307n1.bin,0 -23307n2.bin=@23307n2.bin,0 -23312d0.bin=@23312d0.bin,0 -23312d1.bin=@23312d1.bin,0 -23312d2.bin=@23312d2.bin,0 -23312n0.bin=@23312n0.bin,0 -23312n1.bin=@23312n1.bin,0 -23312n2.bin=@23312n2.bin,0 -23313d0.bin=@23313d0.bin,0 -23313d1.bin=@23313d1.bin,0 -23313d2.bin=@23313d2.bin,0 -23313n0.bin=@23313n0.bin,0 -23313n1.bin=@23313n1.bin,0 -23313n2.bin=@23313n2.bin,0 -23318d0.bin=@23318d0.bin,0 -23318d1.bin=@23318d1.bin,0 -23318d2.bin=@23318d2.bin,0 -23318n0.bin=@23318n0.bin,0 -23318n1.bin=@23318n1.bin,0 -23318n2.bin=@23318n2.bin,0 -23319d0.bin=@23319d0.bin,0 -23319d1.bin=@23319d1.bin,0 -23319d2.bin=@23319d2.bin,0 -23319n0.bin=@23319n0.bin,0 -23319n1.bin=@23319n1.bin,0 -23319n2.bin=@23319n2.bin,0 -23322d0.bin=@23322d0.bin,0 -23322d1.bin=@23322d1.bin,0 -23322d2.bin=@23322d2.bin,0 -23322n0.bin=@23322n0.bin,0 -23322n1.bin=@23322n1.bin,0 -23322n2.bin=@23322n2.bin,0 -23342d0.bin=@23342d0.bin,0 -23342d1.bin=@23342d1.bin,0 -23342d2.bin=@23342d2.bin,0 -23342n0.bin=@23342n0.bin,0 -23342n1.bin=@23342n1.bin,0 -23342n2.bin=@23342n2.bin,0 -23343d0.bin=@23343d0.bin,0 -23343d1.bin=@23343d1.bin,0 -23343d2.bin=@23343d2.bin,0 -23343n0.bin=@23343n0.bin,0 -23343n1.bin=@23343n1.bin,0 -23343n2.bin=@23343n2.bin,0 -23344d0.bin=@23344d0.bin,0 -23344d1.bin=@23344d1.bin,0 -23344d2.bin=@23344d2.bin,0 -23344n0.bin=@23344n0.bin,0 -23344n1.bin=@23344n1.bin,0 -23344n2.bin=@23344n2.bin,0 -23373d0.bin=@23373d0.bin,0 -23373d1.bin=@23373d1.bin,0 -23373d2.bin=@23373d2.bin,0 -23373n0.bin=@23373n0.bin,0 -23373n1.bin=@23373n1.bin,0 -23373n2.bin=@23373n2.bin,0 -23429d0.bin=@23429d0.bin,0 -23429d1.bin=@23429d1.bin,0 -23429d2.bin=@23429d2.bin,0 -23429n0.bin=@23429n0.bin,0 -23429n1.bin=@23429n1.bin,0 -23429n2.bin=@23429n2.bin,0 -23430d0.bin=@23430d0.bin,0 -23430d1.bin=@23430d1.bin,0 -23430d2.bin=@23430d2.bin,0 -23430n0.bin=@23430n0.bin,0 -23430n1.bin=@23430n1.bin,0 -23430n2.bin=@23430n2.bin,0 -23431d0.bin=@23431d0.bin,0 -23431d1.bin=@23431d1.bin,0 -23431d2.bin=@23431d2.bin,0 -23431n0.bin=@23431n0.bin,0 -23431n1.bin=@23431n1.bin,0 -23431n2.bin=@23431n2.bin,0 -23432d0.bin=@23432d0.bin,0 -23432d1.bin=@23432d1.bin,0 -23432d2.bin=@23432d2.bin,0 -23432n0.bin=@23432n0.bin,0 -23432n1.bin=@23432n1.bin,0 -23432n2.bin=@23432n2.bin,0 -23242d0.bin=@23242d0.bin,0 -23242d1.bin=@23242d1.bin,0 -23242d2.bin=@23242d2.bin,0 -23265n2.bin=@23265n2.bin,0 -23266d0.bin=@23266d0.bin,0 -23266d1.bin=@23266d1.bin,0 -23266d2.bin=@23266d2.bin,0 -23266n0.bin=@23266n0.bin,0 -23266n1.bin=@23266n1.bin,0 -23266n2.bin=@23266n2.bin,0 -23306d0.bin=@23306d0.bin,0 -23306d1.bin=@23306d1.bin,0 -23306d2.bin=@23306d2.bin,0 -23306n0.bin=@23306n0.bin,0 -23306n1.bin=@23306n1.bin,0 -23306n2.bin=@23306n2.bin,0 -23308d0.bin=@23308d0.bin,0 -23308d1.bin=@23308d1.bin,0 -23308d2.bin=@23308d2.bin,0 -23308n0.bin=@23308n0.bin,0 -23308n1.bin=@23308n1.bin,0 -23308n2.bin=@23308n2.bin,0 -23314d0.bin=@23314d0.bin,0 -23314d1.bin=@23314d1.bin,0 -23314d2.bin=@23314d2.bin,0 -23314n0.bin=@23314n0.bin,0 -23314n1.bin=@23314n1.bin,0 -23314n2.bin=@23314n2.bin,0 -23367d0.bin=@23367d0.bin,0 -23367d1.bin=@23367d1.bin,0 -23367d2.bin=@23367d2.bin,0 -23367n0.bin=@23367n0.bin,0 -23367n1.bin=@23367n1.bin,0 -23367n2.bin=@23367n2.bin,0 -23368d0.bin=@23368d0.bin,0 -23368d1.bin=@23368d1.bin,0 -23368d2.bin=@23368d2.bin,0 -23368n0.bin=@23368n0.bin,0 -23368n1.bin=@23368n1.bin,0 -23368n2.bin=@23368n2.bin,0 -23380d0.bin=@23380d0.bin,0 -23380d1.bin=@23380d1.bin,0 -23380d2.bin=@23380d2.bin,0 -23380n0.bin=@23380n0.bin,0 -23380n1.bin=@23380n1.bin,0 -23380n2.bin=@23380n2.bin,0 -23381d0.bin=@23381d0.bin,0 -23381d1.bin=@23381d1.bin,0 -23381d2.bin=@23381d2.bin,0 -23381n0.bin=@23381n0.bin,0 -23381n1.bin=@23381n1.bin,0 -23381n2.bin=@23381n2.bin,0 -23392d0.bin=@23392d0.bin,0 -23392d1.bin=@23392d1.bin,0 -23392d2.bin=@23392d2.bin,0 -23392n0.bin=@23392n0.bin,0 -23392n1.bin=@23392n1.bin,0 -23392n2.bin=@23392n2.bin,0 -23393d0.bin=@23393d0.bin,0 -23393d1.bin=@23393d1.bin,0 -23393d2.bin=@23393d2.bin,0 -23393n0.bin=@23393n0.bin,0 -23393n1.bin=@23393n1.bin,0 -23393n2.bin=@23393n2.bin,0 -23402d0.bin=@23402d0.bin,0 -23402d1.bin=@23402d1.bin,0 -23402d2.bin=@23402d2.bin,0 -23402n0.bin=@23402n0.bin,0 -23402n1.bin=@23402n1.bin,0 -23402n2.bin=@23402n2.bin,0 -23424d0.bin=@23424d0.bin,0 -23424d1.bin=@23424d1.bin,0 -23424d2.bin=@23424d2.bin,0 -23424n0.bin=@23424n0.bin,0 -23424n1.bin=@23424n1.bin,0 -23424n2.bin=@23424n2.bin,0 -23427d0.bin=@23427d0.bin,0 -23427d1.bin=@23427d1.bin,0 -23427d2.bin=@23427d2.bin,0 -23427n0.bin=@23427n0.bin,0 -23427n1.bin=@23427n1.bin,0 -23427n2.bin=@23427n2.bin,0 -23428d0.bin=@23428d0.bin,0 -23428d1.bin=@23428d1.bin,0 -23428d2.bin=@23428d2.bin,0 -23428n0.bin=@23428n0.bin,0 -23428n1.bin=@23428n1.bin,0 -23428n2.bin=@23428n2.bin,0 -23514d0.bin=@23514d0.bin,0 -23514d1.bin=@23514d1.bin,0 -23514d2.bin=@23514d2.bin,0 -23514n0.bin=@23514n0.bin,0 -23514n1.bin=@23514n1.bin,0 -23514n2.bin=@23514n2.bin,0 -23515d0.bin=@23515d0.bin,0 -23515d1.bin=@23515d1.bin,0 -23515d2.bin=@23515d2.bin,0 -23515n0.bin=@23515n0.bin,0 -23515n1.bin=@23515n1.bin,0 -23515n2.bin=@23515n2.bin,0 -23626d0.bin=@23626d0.bin,0 -23626d1.bin=@23626d1.bin,0 -23626d2.bin=@23626d2.bin,0 -23626n0.bin=@23626n0.bin,0 -23626n1.bin=@23626n1.bin,0 -23626n2.bin=@23626n2.bin,0 -55016d0.bin=@55016d0.bin,0 -55016d1.bin=@55016d1.bin,0 -55016d2.bin=@55016d2.bin,0 -55016n0.bin=@55016n0.bin,0 -55016n1.bin=@55016n1.bin,0 -55016n2.bin=@55016n2.bin,0 -55202d0.bin=@55202d0.bin,0 -55202d1.bin=@55202d1.bin,0 -55202d2.bin=@55202d2.bin,0 -55202n0.bin=@55202n0.bin,0 -55202n1.bin=@55202n1.bin,0 -55202n2.bin=@55202n2.bin,0 -58073d0.bin=@58073d0.bin,0 -58073d1.bin=@58073d1.bin,0 -58073d2.bin=@58073d2.bin,0 -58073n0.bin=@58073n0.bin,0 -58073n1.bin=@58073n1.bin,0 -58073n2.bin=@58073n2.bin,0 -64551d0.bin=@64551d0.bin,0 -64551d1.bin=@64551d1.bin,0 -64551d2.bin=@64551d2.bin,0 -23265d0.bin=@23265d0.bin,0 -23265d1.bin=@23265d1.bin,0 -23265d2.bin=@23265d2.bin,0 -23265n0.bin=@23265n0.bin,0 -23265n1.bin=@23265n1.bin,0 -23648n1.bin=@23648n1.bin,0 -23648n2.bin=@23648n2.bin,0 -23649d0.bin=@23649d0.bin,0 -23649d1.bin=@23649d1.bin,0 -23649d2.bin=@23649d2.bin,0 -23649n0.bin=@23649n0.bin,0 -23649n1.bin=@23649n1.bin,0 -23649n2.bin=@23649n2.bin,0 -54926d0.bin=@54926d0.bin,0 -54926d1.bin=@54926d1.bin,0 -54926d2.bin=@54926d2.bin,0 -54926n0.bin=@54926n0.bin,0 -54926n1.bin=@54926n1.bin,0 -54926n2.bin=@54926n2.bin,0 -55195d0.bin=@55195d0.bin,0 -55195d1.bin=@55195d1.bin,0 -55195d2.bin=@55195d2.bin,0 -55195n0.bin=@55195n0.bin,0 -55195n1.bin=@55195n1.bin,0 -55195n2.bin=@55195n2.bin,0 -55529d0.bin=@55529d0.bin,0 -55529d1.bin=@55529d1.bin,0 -55529d2.bin=@55529d2.bin,0 -55529n0.bin=@55529n0.bin,0 -55529n1.bin=@55529n1.bin,0 -55529n2.bin=@55529n2.bin,0 -55530d0.bin=@55530d0.bin,0 -55530d1.bin=@55530d1.bin,0 -55530d2.bin=@55530d2.bin,0 -55530n0.bin=@55530n0.bin,0 -55530n1.bin=@55530n1.bin,0 -55530n2.bin=@55530n2.bin,0 -55531d0.bin=@55531d0.bin,0 -55531d1.bin=@55531d1.bin,0 -55531d2.bin=@55531d2.bin,0 -55531n0.bin=@55531n0.bin,0 -55531n1.bin=@55531n1.bin,0 -55531n2.bin=@55531n2.bin,0 -55532d0.bin=@55532d0.bin,0 -55532d1.bin=@55532d1.bin,0 -55532d2.bin=@55532d2.bin,0 -55532n0.bin=@55532n0.bin,0 -55532n1.bin=@55532n1.bin,0 -55532n2.bin=@55532n2.bin,0 -55534d0.bin=@55534d0.bin,0 -55534d1.bin=@55534d1.bin,0 -55534d2.bin=@55534d2.bin,0 -55534n0.bin=@55534n0.bin,0 -55534n1.bin=@55534n1.bin,0 -55534n2.bin=@55534n2.bin,0 -55535d0.bin=@55535d0.bin,0 -55535d1.bin=@55535d1.bin,0 -55535d2.bin=@55535d2.bin,0 -55535n0.bin=@55535n0.bin,0 -55535n1.bin=@55535n1.bin,0 -55535n2.bin=@55535n2.bin,0 -55714d0.bin=@55714d0.bin,0 -55714d1.bin=@55714d1.bin,0 -55714d2.bin=@55714d2.bin,0 -55714n0.bin=@55714n0.bin,0 -55714n1.bin=@55714n1.bin,0 -55714n2.bin=@55714n2.bin,0 -55917d0.bin=@55917d0.bin,0 -55917d1.bin=@55917d1.bin,0 -55917d2.bin=@55917d2.bin,0 -55917n0.bin=@55917n0.bin,0 -55917n1.bin=@55917n1.bin,0 -55917n2.bin=@55917n2.bin,0 -55918d0.bin=@55918d0.bin,0 -55918d1.bin=@55918d1.bin,0 -55918d2.bin=@55918d2.bin,0 -55918n0.bin=@55918n0.bin,0 -55918n1.bin=@55918n1.bin,0 -55918n2.bin=@55918n2.bin,0 -55920d0.bin=@55920d0.bin,0 -55920d1.bin=@55920d1.bin,0 -55920d2.bin=@55920d2.bin,0 -55920n0.bin=@55920n0.bin,0 -55920n1.bin=@55920n1.bin,0 -55920n2.bin=@55920n2.bin,0 -55921d0.bin=@55921d0.bin,0 -55921d1.bin=@55921d1.bin,0 -55921d2.bin=@55921d2.bin,0 -55921n0.bin=@55921n0.bin,0 -55921n1.bin=@55921n1.bin,0 -55921n2.bin=@55921n2.bin,0 -55922d0.bin=@55922d0.bin,0 -55922d1.bin=@55922d1.bin,0 -55922d2.bin=@55922d2.bin,0 -55922n0.bin=@55922n0.bin,0 -55922n1.bin=@55922n1.bin,0 -55922n2.bin=@55922n2.bin,0 -55935d0.bin=@55935d0.bin,0 -55935d1.bin=@55935d1.bin,0 -55935d2.bin=@55935d2.bin,0 -55935n0.bin=@55935n0.bin,0 -55935n1.bin=@55935n1.bin,0 -55935n2.bin=@55935n2.bin,0 -55936d0.bin=@55936d0.bin,0 -55936d1.bin=@55936d1.bin,0 -55936d2.bin=@55936d2.bin,0 -55936n0.bin=@55936n0.bin,0 -55936n1.bin=@55936n1.bin,0 -55936n2.bin=@55936n2.bin,0 -55948d0.bin=@55948d0.bin,0 -55948d1.bin=@55948d1.bin,0 -55948d2.bin=@55948d2.bin,0 -55948n0.bin=@55948n0.bin,0 -55948n1.bin=@55948n1.bin,0 -55948n2.bin=@55948n2.bin,0 -55949d0.bin=@55949d0.bin,0 -55949d1.bin=@55949d1.bin,0 -55949d2.bin=@55949d2.bin,0 -55949n0.bin=@55949n0.bin,0 -55949n1.bin=@55949n1.bin,0 -55949n2.bin=@55949n2.bin,0 -55950d0.bin=@55950d0.bin,0 -55950d1.bin=@55950d1.bin,0 -55950d2.bin=@55950d2.bin,0 -55950n0.bin=@55950n0.bin,0 -55950n1.bin=@55950n1.bin,0 -55950n2.bin=@55950n2.bin,0 -55951d0.bin=@55951d0.bin,0 -55951d1.bin=@55951d1.bin,0 -55951d2.bin=@55951d2.bin,0 -55951n0.bin=@55951n0.bin,0 -55951n1.bin=@55951n1.bin,0 -55951n2.bin=@55951n2.bin,0 -56106d0.bin=@56106d0.bin,0 -56106d1.bin=@56106d1.bin,0 -56106d2.bin=@56106d2.bin,0 -56106n0.bin=@56106n0.bin,0 -56106n1.bin=@56106n1.bin,0 -56106n2.bin=@56106n2.bin,0 -56115d0.bin=@56115d0.bin,0 -56115d1.bin=@56115d1.bin,0 -56115d2.bin=@56115d2.bin,0 -56115n0.bin=@56115n0.bin,0 -56115n1.bin=@56115n1.bin,0 -56115n2.bin=@56115n2.bin,0 -56126d0.bin=@56126d0.bin,0 -56126d1.bin=@56126d1.bin,0 -56126d2.bin=@56126d2.bin,0 -56126n0.bin=@56126n0.bin,0 -56126n1.bin=@56126n1.bin,0 -56126n2.bin=@56126n2.bin,0 -56127d0.bin=@56127d0.bin,0 -56127d1.bin=@56127d1.bin,0 -56127d2.bin=@56127d2.bin,0 -56127n0.bin=@56127n0.bin,0 -56127n1.bin=@56127n1.bin,0 -56127n2.bin=@56127n2.bin,0 -56128d0.bin=@56128d0.bin,0 -56128d1.bin=@56128d1.bin,0 -56128d2.bin=@56128d2.bin,0 -56128n0.bin=@56128n0.bin,0 -56128n1.bin=@56128n1.bin,0 -56128n2.bin=@56128n2.bin,0 -56130d0.bin=@56130d0.bin,0 -56130d1.bin=@56130d1.bin,0 -56130d2.bin=@56130d2.bin,0 -56130n0.bin=@56130n0.bin,0 -56130n1.bin=@56130n1.bin,0 -56130n2.bin=@56130n2.bin,0 -56131d0.bin=@56131d0.bin,0 -56131d1.bin=@56131d1.bin,0 -56131d2.bin=@56131d2.bin,0 -56131n0.bin=@56131n0.bin,0 -56131n1.bin=@56131n1.bin,0 -56131n2.bin=@56131n2.bin,0 -56133d0.bin=@56133d0.bin,0 -56133d1.bin=@56133d1.bin,0 -56133d2.bin=@56133d2.bin,0 -56133n0.bin=@56133n0.bin,0 -56133n1.bin=@56133n1.bin,0 -56133n2.bin=@56133n2.bin,0 -56152d0.bin=@56152d0.bin,0 -56152d1.bin=@56152d1.bin,0 -56152d2.bin=@56152d2.bin,0 -56152n0.bin=@56152n0.bin,0 -56152n1.bin=@56152n1.bin,0 -56152n2.bin=@56152n2.bin,0 -56153d0.bin=@56153d0.bin,0 -56153d1.bin=@56153d1.bin,0 -56153d2.bin=@56153d2.bin,0 -56153n0.bin=@56153n0.bin,0 -56153n1.bin=@56153n1.bin,0 -56153n2.bin=@56153n2.bin,0 -56158d0.bin=@56158d0.bin,0 -56158d1.bin=@56158d1.bin,0 -56158d2.bin=@56158d2.bin,0 -56158n0.bin=@56158n0.bin,0 -56158n1.bin=@56158n1.bin,0 -56158n2.bin=@56158n2.bin,0 -23648d0.bin=@23648d0.bin,0 -23648d1.bin=@23648d1.bin,0 -23648d2.bin=@23648d2.bin,0 -23648n0.bin=@23648n0.bin,0 -23468n0.bin=@23468n0.bin,0 -23468n1.bin=@23468n1.bin,0 -23468n2.bin=@23468n2.bin,0 -23472d0.bin=@23472d0.bin,0 -23472d1.bin=@23472d1.bin,0 -23472d2.bin=@23472d2.bin,0 -23472n0.bin=@23472n0.bin,0 -23472n1.bin=@23472n1.bin,0 -23472n2.bin=@23472n2.bin,0 -23476d0.bin=@23476d0.bin,0 -23476d1.bin=@23476d1.bin,0 -23476d2.bin=@23476d2.bin,0 -23476n0.bin=@23476n0.bin,0 -23476n1.bin=@23476n1.bin,0 -23476n2.bin=@23476n2.bin,0 -23480d0.bin=@23480d0.bin,0 -23480d1.bin=@23480d1.bin,0 -23480d2.bin=@23480d2.bin,0 -23480n0.bin=@23480n0.bin,0 -23480n1.bin=@23480n1.bin,0 -23480n2.bin=@23480n2.bin,0 -23516d0.bin=@23516d0.bin,0 -23516d1.bin=@23516d1.bin,0 -23516d2.bin=@23516d2.bin,0 -23516n0.bin=@23516n0.bin,0 -23516n1.bin=@23516n1.bin,0 -23516n2.bin=@23516n2.bin,0 -23520d0.bin=@23520d0.bin,0 -23520d1.bin=@23520d1.bin,0 -23520d2.bin=@23520d2.bin,0 -23520n0.bin=@23520n0.bin,0 -23520n1.bin=@23520n1.bin,0 -23520n2.bin=@23520n2.bin,0 -23536d0.bin=@23536d0.bin,0 -23536d1.bin=@23536d1.bin,0 -23536d2.bin=@23536d2.bin,0 -23536n0.bin=@23536n0.bin,0 -23536n1.bin=@23536n1.bin,0 -23536n2.bin=@23536n2.bin,0 -23540d0.bin=@23540d0.bin,0 -23540d1.bin=@23540d1.bin,0 -23540d2.bin=@23540d2.bin,0 -23540n0.bin=@23540n0.bin,0 -23540n1.bin=@23540n1.bin,0 -23540n2.bin=@23540n2.bin,0 -23606d0.bin=@23606d0.bin,0 -23606d1.bin=@23606d1.bin,0 -23606d2.bin=@23606d2.bin,0 -23606n0.bin=@23606n0.bin,0 -23606n1.bin=@23606n1.bin,0 -23606n2.bin=@23606n2.bin,0 -23610d0.bin=@23610d0.bin,0 -23610d1.bin=@23610d1.bin,0 -23610d2.bin=@23610d2.bin,0 -23610n0.bin=@23610n0.bin,0 -23610n1.bin=@23610n1.bin,0 -23610n2.bin=@23610n2.bin,0 -23614d0.bin=@23614d0.bin,0 -23614d1.bin=@23614d1.bin,0 -23614d2.bin=@23614d2.bin,0 -23614n0.bin=@23614n0.bin,0 -23614n1.bin=@23614n1.bin,0 -23614n2.bin=@23614n2.bin,0 -23618d0.bin=@23618d0.bin,0 -23618d1.bin=@23618d1.bin,0 -23618d2.bin=@23618d2.bin,0 -23618n0.bin=@23618n0.bin,0 -23618n1.bin=@23618n1.bin,0 -23618n2.bin=@23618n2.bin,0 -23622d0.bin=@23622d0.bin,0 -23622d1.bin=@23622d1.bin,0 -23622d2.bin=@23622d2.bin,0 -23622n0.bin=@23622n0.bin,0 -23622n1.bin=@23622n1.bin,0 -23622n2.bin=@23622n2.bin,0 -23644d0.bin=@23644d0.bin,0 -23644d1.bin=@23644d1.bin,0 -23644d2.bin=@23644d2.bin,0 -23644n0.bin=@23644n0.bin,0 -23644n1.bin=@23644n1.bin,0 -23644n2.bin=@23644n2.bin,0 -23655d0.bin=@23655d0.bin,0 -23655d1.bin=@23655d1.bin,0 -23655d2.bin=@23655d2.bin,0 -23655n0.bin=@23655n0.bin,0 -23655n1.bin=@23655n1.bin,0 -23655n2.bin=@23655n2.bin,0 -23659d0.bin=@23659d0.bin,0 -23659d1.bin=@23659d1.bin,0 -23659d2.bin=@23659d2.bin,0 -23659n0.bin=@23659n0.bin,0 -23659n1.bin=@23659n1.bin,0 -23659n2.bin=@23659n2.bin,0 -23668d0.bin=@23668d0.bin,0 -23668d1.bin=@23668d1.bin,0 -23668d2.bin=@23668d2.bin,0 -23668n0.bin=@23668n0.bin,0 -23668n1.bin=@23668n1.bin,0 -23668n2.bin=@23668n2.bin,0 -23705d0.bin=@23705d0.bin,0 -23705d1.bin=@23705d1.bin,0 -23705d2.bin=@23705d2.bin,0 -23705n0.bin=@23705n0.bin,0 -23705n1.bin=@23705n1.bin,0 -23705n2.bin=@23705n2.bin,0 -23709d0.bin=@23709d0.bin,0 -23709d1.bin=@23709d1.bin,0 -23709d2.bin=@23709d2.bin,0 -23709n0.bin=@23709n0.bin,0 -23709n1.bin=@23709n1.bin,0 -23709n2.bin=@23709n2.bin,0 -23713d0.bin=@23713d0.bin,0 -23713d1.bin=@23713d1.bin,0 -23713d2.bin=@23713d2.bin,0 -23713n0.bin=@23713n0.bin,0 -23713n1.bin=@23713n1.bin,0 -23713n2.bin=@23713n2.bin,0 -23718d0.bin=@23718d0.bin,0 -23718d1.bin=@23718d1.bin,0 -23718d2.bin=@23718d2.bin,0 -23718n0.bin=@23718n0.bin,0 -23718n1.bin=@23718n1.bin,0 -23718n2.bin=@23718n2.bin,0 -40236d0.bin=@40236d0.bin,0 -40236d1.bin=@40236d1.bin,0 -40236d2.bin=@40236d2.bin,0 -40236n0.bin=@40236n0.bin,0 -40236n1.bin=@40236n1.bin,0 -40236n2.bin=@40236n2.bin,0 -55691d0.bin=@55691d0.bin,0 -55691d1.bin=@55691d1.bin,0 -55691d2.bin=@55691d2.bin,0 -55691n0.bin=@55691n0.bin,0 -55691n1.bin=@55691n1.bin,0 -55691n2.bin=@55691n2.bin,0 -55692d0.bin=@55692d0.bin,0 -55692d1.bin=@55692d1.bin,0 -55692d2.bin=@55692d2.bin,0 -55692n0.bin=@55692n0.bin,0 -55692n1.bin=@55692n1.bin,0 -55692n2.bin=@55692n2.bin,0 -55693d0.bin=@55693d0.bin,0 -55693d1.bin=@55693d1.bin,0 -55693d2.bin=@55693d2.bin,0 -55693n0.bin=@55693n0.bin,0 -55693n1.bin=@55693n1.bin,0 -55693n2.bin=@55693n2.bin,0 -55694d0.bin=@55694d0.bin,0 -55694d1.bin=@55694d1.bin,0 -55694d2.bin=@55694d2.bin,0 -55694n0.bin=@55694n0.bin,0 -55694n1.bin=@55694n1.bin,0 -55694n2.bin=@55694n2.bin,0 -55695d0.bin=@55695d0.bin,0 -55695d1.bin=@55695d1.bin,0 -55695d2.bin=@55695d2.bin,0 -55695n0.bin=@55695n0.bin,0 -55695n1.bin=@55695n1.bin,0 -55695n2.bin=@55695n2.bin,0 -55696d0.bin=@55696d0.bin,0 -55696d1.bin=@55696d1.bin,0 -55696d2.bin=@55696d2.bin,0 -55696n0.bin=@55696n0.bin,0 -55696n1.bin=@55696n1.bin,0 -55696n2.bin=@55696n2.bin,0 -55697d0.bin=@55697d0.bin,0 -55697d1.bin=@55697d1.bin,0 -55697d2.bin=@55697d2.bin,0 -55697n0.bin=@55697n0.bin,0 -55697n1.bin=@55697n1.bin,0 -55697n2.bin=@55697n2.bin,0 -55698d0.bin=@55698d0.bin,0 -55698d1.bin=@55698d1.bin,0 -55698d2.bin=@55698d2.bin,0 -55698n0.bin=@55698n0.bin,0 -55698n1.bin=@55698n1.bin,0 -55698n2.bin=@55698n2.bin,0 -55728d0.bin=@55728d0.bin,0 -55728d1.bin=@55728d1.bin,0 -55728d2.bin=@55728d2.bin,0 -55728n0.bin=@55728n0.bin,0 -55728n1.bin=@55728n1.bin,0 -55728n2.bin=@55728n2.bin,0 -55738d0.bin=@55738d0.bin,0 -55738d1.bin=@55738d1.bin,0 -55738d2.bin=@55738d2.bin,0 -55738n0.bin=@55738n0.bin,0 -55738n1.bin=@55738n1.bin,0 -55738n2.bin=@55738n2.bin,0 -55923d0.bin=@55923d0.bin,0 -55923d1.bin=@55923d1.bin,0 -55923d2.bin=@55923d2.bin,0 -55923n0.bin=@55923n0.bin,0 -55923n1.bin=@55923n1.bin,0 -55923n2.bin=@55923n2.bin,0 -55929d0.bin=@55929d0.bin,0 -55929d1.bin=@55929d1.bin,0 -55929d2.bin=@55929d2.bin,0 -55929n0.bin=@55929n0.bin,0 -55929n1.bin=@55929n1.bin,0 -55929n2.bin=@55929n2.bin,0 -56076d0.bin=@56076d0.bin,0 -56076d1.bin=@56076d1.bin,0 -56076d2.bin=@56076d2.bin,0 -56076n0.bin=@56076n0.bin,0 -56076n1.bin=@56076n1.bin,0 -56076n2.bin=@56076n2.bin,0 -56077d0.bin=@56077d0.bin,0 -56077d1.bin=@56077d1.bin,0 -56077d2.bin=@56077d2.bin,0 -56077n0.bin=@56077n0.bin,0 -56077n1.bin=@56077n1.bin,0 -56077n2.bin=@56077n2.bin,0 -56078d0.bin=@56078d0.bin,0 -56078d1.bin=@56078d1.bin,0 -56078d2.bin=@56078d2.bin,0 -56078n0.bin=@56078n0.bin,0 -56078n1.bin=@56078n1.bin,0 -56078n2.bin=@56078n2.bin,0 -56079d0.bin=@56079d0.bin,0 -56079d1.bin=@56079d1.bin,0 -56079d2.bin=@56079d2.bin,0 -56079n0.bin=@56079n0.bin,0 -56079n1.bin=@56079n1.bin,0 -56079n2.bin=@56079n2.bin,0 -56080d0.bin=@56080d0.bin,0 -56080d1.bin=@56080d1.bin,0 -56080d2.bin=@56080d2.bin,0 -56080n0.bin=@56080n0.bin,0 -56080n1.bin=@56080n1.bin,0 -56080n2.bin=@56080n2.bin,0 -56125d0.bin=@56125d0.bin,0 -56125d1.bin=@56125d1.bin,0 -56125d2.bin=@56125d2.bin,0 -56125n0.bin=@56125n0.bin,0 -56125n1.bin=@56125n1.bin,0 -56125n2.bin=@56125n2.bin,0 -56144d0.bin=@56144d0.bin,0 -56144d1.bin=@56144d1.bin,0 -56144d2.bin=@56144d2.bin,0 -56144n0.bin=@56144n0.bin,0 -56144n1.bin=@56144n1.bin,0 -56144n2.bin=@56144n2.bin,0 -56145d0.bin=@56145d0.bin,0 -56145d1.bin=@56145d1.bin,0 -56145d2.bin=@56145d2.bin,0 -56145n0.bin=@56145n0.bin,0 -56145n1.bin=@56145n1.bin,0 -56145n2.bin=@56145n2.bin,0 -56150d0.bin=@56150d0.bin,0 -56150d1.bin=@56150d1.bin,0 -56150d2.bin=@56150d2.bin,0 -56150n0.bin=@56150n0.bin,0 -56150n1.bin=@56150n1.bin,0 -56150n2.bin=@56150n2.bin,0 -56151d0.bin=@56151d0.bin,0 -56151d1.bin=@56151d1.bin,0 -56151d2.bin=@56151d2.bin,0 -56151n0.bin=@56151n0.bin,0 -56151n1.bin=@56151n1.bin,0 -56151n2.bin=@56151n2.bin,0 -56154d0.bin=@56154d0.bin,0 -56154d1.bin=@56154d1.bin,0 -56154d2.bin=@56154d2.bin,0 -56154n0.bin=@56154n0.bin,0 -56154n1.bin=@56154n1.bin,0 -56154n2.bin=@56154n2.bin,0 -56155d0.bin=@56155d0.bin,0 -56155d1.bin=@56155d1.bin,0 -56155d2.bin=@56155d2.bin,0 -56155n0.bin=@56155n0.bin,0 -56155n1.bin=@56155n1.bin,0 -56155n2.bin=@56155n2.bin,0 -56156d0.bin=@56156d0.bin,0 -56156d1.bin=@56156d1.bin,0 -56156d2.bin=@56156d2.bin,0 -56156n0.bin=@56156n0.bin,0 -56156n1.bin=@56156n1.bin,0 -56156n2.bin=@56156n2.bin,0 -23468d0.bin=@23468d0.bin,0 -23468d1.bin=@23468d1.bin,0 -23468d2.bin=@23468d2.bin,0 -23469n2.bin=@23469n2.bin,0 -23473d0.bin=@23473d0.bin,0 -23473d1.bin=@23473d1.bin,0 -23473d2.bin=@23473d2.bin,0 -23473n0.bin=@23473n0.bin,0 -23473n1.bin=@23473n1.bin,0 -23473n2.bin=@23473n2.bin,0 -23477d0.bin=@23477d0.bin,0 -23477d1.bin=@23477d1.bin,0 -23477d2.bin=@23477d2.bin,0 -23477n0.bin=@23477n0.bin,0 -23477n1.bin=@23477n1.bin,0 -23477n2.bin=@23477n2.bin,0 -23481d0.bin=@23481d0.bin,0 -23481d1.bin=@23481d1.bin,0 -23481d2.bin=@23481d2.bin,0 -23481n0.bin=@23481n0.bin,0 -23481n1.bin=@23481n1.bin,0 -23481n2.bin=@23481n2.bin,0 -23517d0.bin=@23517d0.bin,0 -23517d1.bin=@23517d1.bin,0 -23517d2.bin=@23517d2.bin,0 -23517n0.bin=@23517n0.bin,0 -23517n1.bin=@23517n1.bin,0 -23517n2.bin=@23517n2.bin,0 -23521d0.bin=@23521d0.bin,0 -23521d1.bin=@23521d1.bin,0 -23521d2.bin=@23521d2.bin,0 -23521n0.bin=@23521n0.bin,0 -23521n1.bin=@23521n1.bin,0 -23521n2.bin=@23521n2.bin,0 -23537d0.bin=@23537d0.bin,0 -23537d1.bin=@23537d1.bin,0 -23537d2.bin=@23537d2.bin,0 -23537n0.bin=@23537n0.bin,0 -23537n1.bin=@23537n1.bin,0 -23537n2.bin=@23537n2.bin,0 -23541d0.bin=@23541d0.bin,0 -23541d1.bin=@23541d1.bin,0 -23541d2.bin=@23541d2.bin,0 -23541n0.bin=@23541n0.bin,0 -23541n1.bin=@23541n1.bin,0 -23541n2.bin=@23541n2.bin,0 -23607d0.bin=@23607d0.bin,0 -23607d1.bin=@23607d1.bin,0 -23607d2.bin=@23607d2.bin,0 -23607n0.bin=@23607n0.bin,0 -23607n1.bin=@23607n1.bin,0 -23607n2.bin=@23607n2.bin,0 -23611d0.bin=@23611d0.bin,0 -23611d1.bin=@23611d1.bin,0 -23611d2.bin=@23611d2.bin,0 -23611n0.bin=@23611n0.bin,0 -23611n1.bin=@23611n1.bin,0 -23611n2.bin=@23611n2.bin,0 -23615d0.bin=@23615d0.bin,0 -23615d1.bin=@23615d1.bin,0 -23615d2.bin=@23615d2.bin,0 -23615n0.bin=@23615n0.bin,0 -23615n1.bin=@23615n1.bin,0 -23615n2.bin=@23615n2.bin,0 -23619d0.bin=@23619d0.bin,0 -23619d1.bin=@23619d1.bin,0 -23619d2.bin=@23619d2.bin,0 -23619n0.bin=@23619n0.bin,0 -23619n1.bin=@23619n1.bin,0 -23619n2.bin=@23619n2.bin,0 -23623d0.bin=@23623d0.bin,0 -23623d1.bin=@23623d1.bin,0 -23623d2.bin=@23623d2.bin,0 -23623n0.bin=@23623n0.bin,0 -23623n1.bin=@23623n1.bin,0 -23623n2.bin=@23623n2.bin,0 -23645d0.bin=@23645d0.bin,0 -23645d1.bin=@23645d1.bin,0 -23645d2.bin=@23645d2.bin,0 -23645n0.bin=@23645n0.bin,0 -23645n1.bin=@23645n1.bin,0 -23645n2.bin=@23645n2.bin,0 -23656d0.bin=@23656d0.bin,0 -23656d1.bin=@23656d1.bin,0 -23656d2.bin=@23656d2.bin,0 -23656n0.bin=@23656n0.bin,0 -23656n1.bin=@23656n1.bin,0 -23656n2.bin=@23656n2.bin,0 -23660d0.bin=@23660d0.bin,0 -23660d1.bin=@23660d1.bin,0 -23660d2.bin=@23660d2.bin,0 -23660n0.bin=@23660n0.bin,0 -23660n1.bin=@23660n1.bin,0 -23660n2.bin=@23660n2.bin,0 -23669d0.bin=@23669d0.bin,0 -23669d1.bin=@23669d1.bin,0 -23669d2.bin=@23669d2.bin,0 -23669n0.bin=@23669n0.bin,0 -23669n1.bin=@23669n1.bin,0 -23669n2.bin=@23669n2.bin,0 -23706d0.bin=@23706d0.bin,0 -23706d1.bin=@23706d1.bin,0 -23706d2.bin=@23706d2.bin,0 -23706n0.bin=@23706n0.bin,0 -23706n1.bin=@23706n1.bin,0 -23706n2.bin=@23706n2.bin,0 -23710d0.bin=@23710d0.bin,0 -23710d1.bin=@23710d1.bin,0 -23710d2.bin=@23710d2.bin,0 -23710n0.bin=@23710n0.bin,0 -23710n1.bin=@23710n1.bin,0 -23710n2.bin=@23710n2.bin,0 -23714d0.bin=@23714d0.bin,0 -23714d1.bin=@23714d1.bin,0 -23714d2.bin=@23714d2.bin,0 -23714n0.bin=@23714n0.bin,0 -23714n1.bin=@23714n1.bin,0 -23714n2.bin=@23714n2.bin,0 -23719d0.bin=@23719d0.bin,0 -23719d1.bin=@23719d1.bin,0 -23719d2.bin=@23719d2.bin,0 -23719n0.bin=@23719n0.bin,0 -23719n1.bin=@23719n1.bin,0 -23719n2.bin=@23719n2.bin,0 -55513d0.bin=@55513d0.bin,0 -55513d1.bin=@55513d1.bin,0 -55513d2.bin=@55513d2.bin,0 -55513n0.bin=@55513n0.bin,0 -55513n1.bin=@55513n1.bin,0 -55513n2.bin=@55513n2.bin,0 -55924d0.bin=@55924d0.bin,0 -55924d1.bin=@55924d1.bin,0 -55924d2.bin=@55924d2.bin,0 -55924n0.bin=@55924n0.bin,0 -55924n1.bin=@55924n1.bin,0 -55924n2.bin=@55924n2.bin,0 -55930d0.bin=@55930d0.bin,0 -55930d1.bin=@55930d1.bin,0 -55930d2.bin=@55930d2.bin,0 -55930n0.bin=@55930n0.bin,0 -55930n1.bin=@55930n1.bin,0 -55930n2.bin=@55930n2.bin,0 -23469d0.bin=@23469d0.bin,0 -23469d1.bin=@23469d1.bin,0 -23469d2.bin=@23469d2.bin,0 -23469n0.bin=@23469n0.bin,0 -23469n1.bin=@23469n1.bin,0 -23470n2.bin=@23470n2.bin,0 -23474d0.bin=@23474d0.bin,0 -23474d1.bin=@23474d1.bin,0 -23474d2.bin=@23474d2.bin,0 -23474n0.bin=@23474n0.bin,0 -23474n1.bin=@23474n1.bin,0 -23474n2.bin=@23474n2.bin,0 -23478d0.bin=@23478d0.bin,0 -23478d1.bin=@23478d1.bin,0 -23478d2.bin=@23478d2.bin,0 -23478n0.bin=@23478n0.bin,0 -23478n1.bin=@23478n1.bin,0 -23478n2.bin=@23478n2.bin,0 -23482d0.bin=@23482d0.bin,0 -23482d1.bin=@23482d1.bin,0 -23482d2.bin=@23482d2.bin,0 -23482n0.bin=@23482n0.bin,0 -23482n1.bin=@23482n1.bin,0 -23482n2.bin=@23482n2.bin,0 -23518d0.bin=@23518d0.bin,0 -23518d1.bin=@23518d1.bin,0 -23518d2.bin=@23518d2.bin,0 -23518n0.bin=@23518n0.bin,0 -23518n1.bin=@23518n1.bin,0 -23518n2.bin=@23518n2.bin,0 -23522d0.bin=@23522d0.bin,0 -23522d1.bin=@23522d1.bin,0 -23522d2.bin=@23522d2.bin,0 -23522n0.bin=@23522n0.bin,0 -23522n1.bin=@23522n1.bin,0 -23522n2.bin=@23522n2.bin,0 -23538d0.bin=@23538d0.bin,0 -23538d1.bin=@23538d1.bin,0 -23538d2.bin=@23538d2.bin,0 -23538n0.bin=@23538n0.bin,0 -23538n1.bin=@23538n1.bin,0 -23538n2.bin=@23538n2.bin,0 -23542d0.bin=@23542d0.bin,0 -23542d1.bin=@23542d1.bin,0 -23542d2.bin=@23542d2.bin,0 -23542n0.bin=@23542n0.bin,0 -23542n1.bin=@23542n1.bin,0 -23542n2.bin=@23542n2.bin,0 -23608d0.bin=@23608d0.bin,0 -23608d1.bin=@23608d1.bin,0 -23608d2.bin=@23608d2.bin,0 -23608n0.bin=@23608n0.bin,0 -23608n1.bin=@23608n1.bin,0 -23608n2.bin=@23608n2.bin,0 -23612d0.bin=@23612d0.bin,0 -23612d1.bin=@23612d1.bin,0 -23612d2.bin=@23612d2.bin,0 -23612n0.bin=@23612n0.bin,0 -23612n1.bin=@23612n1.bin,0 -23612n2.bin=@23612n2.bin,0 -23616d0.bin=@23616d0.bin,0 -23616d1.bin=@23616d1.bin,0 -23616d2.bin=@23616d2.bin,0 -23616n0.bin=@23616n0.bin,0 -23616n1.bin=@23616n1.bin,0 -23616n2.bin=@23616n2.bin,0 -23620d0.bin=@23620d0.bin,0 -23620d1.bin=@23620d1.bin,0 -23620d2.bin=@23620d2.bin,0 -23620n0.bin=@23620n0.bin,0 -23620n1.bin=@23620n1.bin,0 -23620n2.bin=@23620n2.bin,0 -23624d0.bin=@23624d0.bin,0 -23624d1.bin=@23624d1.bin,0 -23624d2.bin=@23624d2.bin,0 -23624n0.bin=@23624n0.bin,0 -23624n1.bin=@23624n1.bin,0 -23624n2.bin=@23624n2.bin,0 -23646d0.bin=@23646d0.bin,0 -23646d1.bin=@23646d1.bin,0 -23646d2.bin=@23646d2.bin,0 -23646n0.bin=@23646n0.bin,0 -23646n1.bin=@23646n1.bin,0 -23646n2.bin=@23646n2.bin,0 -23657d0.bin=@23657d0.bin,0 -23657d1.bin=@23657d1.bin,0 -23657d2.bin=@23657d2.bin,0 -23657n0.bin=@23657n0.bin,0 -23657n1.bin=@23657n1.bin,0 -23657n2.bin=@23657n2.bin,0 -23661d0.bin=@23661d0.bin,0 -23661d1.bin=@23661d1.bin,0 -23661d2.bin=@23661d2.bin,0 -23661n0.bin=@23661n0.bin,0 -23661n1.bin=@23661n1.bin,0 -23661n2.bin=@23661n2.bin,0 -23670d0.bin=@23670d0.bin,0 -23670d1.bin=@23670d1.bin,0 -23670d2.bin=@23670d2.bin,0 -23670n0.bin=@23670n0.bin,0 -23670n1.bin=@23670n1.bin,0 -23670n2.bin=@23670n2.bin,0 -23707d0.bin=@23707d0.bin,0 -23707d1.bin=@23707d1.bin,0 -23707d2.bin=@23707d2.bin,0 -23707n0.bin=@23707n0.bin,0 -23707n1.bin=@23707n1.bin,0 -23707n2.bin=@23707n2.bin,0 -23711d0.bin=@23711d0.bin,0 -23711d1.bin=@23711d1.bin,0 -23711d2.bin=@23711d2.bin,0 -23711n0.bin=@23711n0.bin,0 -23711n1.bin=@23711n1.bin,0 -23711n2.bin=@23711n2.bin,0 -23715d0.bin=@23715d0.bin,0 -23715d1.bin=@23715d1.bin,0 -23715d2.bin=@23715d2.bin,0 -23715n0.bin=@23715n0.bin,0 -23715n1.bin=@23715n1.bin,0 -23715n2.bin=@23715n2.bin,0 -23720d0.bin=@23720d0.bin,0 -23720d1.bin=@23720d1.bin,0 -23720d2.bin=@23720d2.bin,0 -23720n0.bin=@23720n0.bin,0 -23720n1.bin=@23720n1.bin,0 -23720n2.bin=@23720n2.bin,0 -55925d0.bin=@55925d0.bin,0 -55925d1.bin=@55925d1.bin,0 -55925d2.bin=@55925d2.bin,0 -55925n0.bin=@55925n0.bin,0 -55925n1.bin=@55925n1.bin,0 -55925n2.bin=@55925n2.bin,0 -55931d0.bin=@55931d0.bin,0 -55931d1.bin=@55931d1.bin,0 -55931d2.bin=@55931d2.bin,0 -55931n0.bin=@55931n0.bin,0 -55931n1.bin=@55931n1.bin,0 -55931n2.bin=@55931n2.bin,0 -56042d0.bin=@56042d0.bin,0 -56042d1.bin=@56042d1.bin,0 -56042d2.bin=@56042d2.bin,0 -56042n0.bin=@56042n0.bin,0 -56042n1.bin=@56042n1.bin,0 -56042n2.bin=@56042n2.bin,0 -56063d0.bin=@56063d0.bin,0 -56063d1.bin=@56063d1.bin,0 -56063d2.bin=@56063d2.bin,0 -56063n0.bin=@56063n0.bin,0 -56063n1.bin=@56063n1.bin,0 -56063n2.bin=@56063n2.bin,0 -56064d0.bin=@56064d0.bin,0 -56064d1.bin=@56064d1.bin,0 -56064d2.bin=@56064d2.bin,0 -56064n0.bin=@56064n0.bin,0 -56064n1.bin=@56064n1.bin,0 -56064n2.bin=@56064n2.bin,0 -23470d0.bin=@23470d0.bin,0 -23470d1.bin=@23470d1.bin,0 -23470d2.bin=@23470d2.bin,0 -23470n0.bin=@23470n0.bin,0 -23470n1.bin=@23470n1.bin,0 -23475d0.bin=@23475d0.bin,0 -23475d1.bin=@23475d1.bin,0 -23475d2.bin=@23475d2.bin,0 -23475n0.bin=@23475n0.bin,0 -23475n1.bin=@23475n1.bin,0 -23475n2.bin=@23475n2.bin,0 -23479d0.bin=@23479d0.bin,0 -23479d1.bin=@23479d1.bin,0 -23479d2.bin=@23479d2.bin,0 -23479n0.bin=@23479n0.bin,0 -23479n1.bin=@23479n1.bin,0 -23479n2.bin=@23479n2.bin,0 -23483d0.bin=@23483d0.bin,0 -23483d1.bin=@23483d1.bin,0 -23483d2.bin=@23483d2.bin,0 -23483n0.bin=@23483n0.bin,0 -23483n1.bin=@23483n1.bin,0 -23483n2.bin=@23483n2.bin,0 -23519d0.bin=@23519d0.bin,0 -23519d1.bin=@23519d1.bin,0 -23519d2.bin=@23519d2.bin,0 -23519n0.bin=@23519n0.bin,0 -23519n1.bin=@23519n1.bin,0 -23519n2.bin=@23519n2.bin,0 -23523d0.bin=@23523d0.bin,0 -23523d1.bin=@23523d1.bin,0 -23523d2.bin=@23523d2.bin,0 -23523n0.bin=@23523n0.bin,0 -23523n1.bin=@23523n1.bin,0 -23523n2.bin=@23523n2.bin,0 -23539d0.bin=@23539d0.bin,0 -23539d1.bin=@23539d1.bin,0 -23539d2.bin=@23539d2.bin,0 -23539n0.bin=@23539n0.bin,0 -23539n1.bin=@23539n1.bin,0 -23539n2.bin=@23539n2.bin,0 -23543d0.bin=@23543d0.bin,0 -23543d1.bin=@23543d1.bin,0 -23543d2.bin=@23543d2.bin,0 -23543n0.bin=@23543n0.bin,0 -23543n1.bin=@23543n1.bin,0 -23543n2.bin=@23543n2.bin,0 -23609d0.bin=@23609d0.bin,0 -23609d1.bin=@23609d1.bin,0 -23609d2.bin=@23609d2.bin,0 -23609n0.bin=@23609n0.bin,0 -23609n1.bin=@23609n1.bin,0 -23609n2.bin=@23609n2.bin,0 -23613d0.bin=@23613d0.bin,0 -23613d1.bin=@23613d1.bin,0 -23613d2.bin=@23613d2.bin,0 -23613n0.bin=@23613n0.bin,0 -23613n1.bin=@23613n1.bin,0 -23613n2.bin=@23613n2.bin,0 -23617d0.bin=@23617d0.bin,0 -23617d1.bin=@23617d1.bin,0 -23617d2.bin=@23617d2.bin,0 -23617n0.bin=@23617n0.bin,0 -23617n1.bin=@23617n1.bin,0 -23617n2.bin=@23617n2.bin,0 -23621d0.bin=@23621d0.bin,0 -23621d1.bin=@23621d1.bin,0 -23621d2.bin=@23621d2.bin,0 -23621n0.bin=@23621n0.bin,0 -23621n1.bin=@23621n1.bin,0 -23621n2.bin=@23621n2.bin,0 -23625d0.bin=@23625d0.bin,0 -23625d1.bin=@23625d1.bin,0 -23625d2.bin=@23625d2.bin,0 -23625n0.bin=@23625n0.bin,0 -23625n1.bin=@23625n1.bin,0 -23625n2.bin=@23625n2.bin,0 -23647d0.bin=@23647d0.bin,0 -23647d1.bin=@23647d1.bin,0 -23647d2.bin=@23647d2.bin,0 -23647n0.bin=@23647n0.bin,0 -23647n1.bin=@23647n1.bin,0 -23647n2.bin=@23647n2.bin,0 -23658d0.bin=@23658d0.bin,0 -23658d1.bin=@23658d1.bin,0 -23658d2.bin=@23658d2.bin,0 -23658n0.bin=@23658n0.bin,0 -23658n1.bin=@23658n1.bin,0 -23658n2.bin=@23658n2.bin,0 -23662d0.bin=@23662d0.bin,0 -23662d1.bin=@23662d1.bin,0 -23662d2.bin=@23662d2.bin,0 -23662n0.bin=@23662n0.bin,0 -23662n1.bin=@23662n1.bin,0 -23662n2.bin=@23662n2.bin,0 -23671d0.bin=@23671d0.bin,0 -23671d1.bin=@23671d1.bin,0 -23671d2.bin=@23671d2.bin,0 -23671n0.bin=@23671n0.bin,0 -23671n1.bin=@23671n1.bin,0 -23671n2.bin=@23671n2.bin,0 -23708d0.bin=@23708d0.bin,0 -23708d1.bin=@23708d1.bin,0 -23708d2.bin=@23708d2.bin,0 -23708n0.bin=@23708n0.bin,0 -23708n1.bin=@23708n1.bin,0 -23708n2.bin=@23708n2.bin,0 -23712d0.bin=@23712d0.bin,0 -23712d1.bin=@23712d1.bin,0 -23712d2.bin=@23712d2.bin,0 -23712n0.bin=@23712n0.bin,0 -23712n1.bin=@23712n1.bin,0 -23712n2.bin=@23712n2.bin,0 -23716d0.bin=@23716d0.bin,0 -23716d1.bin=@23716d1.bin,0 -23716d2.bin=@23716d2.bin,0 -23716n0.bin=@23716n0.bin,0 -23716n1.bin=@23716n1.bin,0 -23716n2.bin=@23716n2.bin,0 -23721d0.bin=@23721d0.bin,0 -23721d1.bin=@23721d1.bin,0 -23721d2.bin=@23721d2.bin,0 -23721n0.bin=@23721n0.bin,0 -23721n1.bin=@23721n1.bin,0 -23721n2.bin=@23721n2.bin,0 -55926d0.bin=@55926d0.bin,0 -55926d1.bin=@55926d1.bin,0 -55926d2.bin=@55926d2.bin,0 -55926n0.bin=@55926n0.bin,0 -55926n1.bin=@55926n1.bin,0 -55926n2.bin=@55926n2.bin,0 -55932d0.bin=@55932d0.bin,0 -55932d1.bin=@55932d1.bin,0 -55932d2.bin=@55932d2.bin,0 -55932n0.bin=@55932n0.bin,0 -55932n1.bin=@55932n1.bin,0 -55932n2.bin=@55932n2.bin,0 -23471d0.bin=@23471d0.bin,0 -23471d1.bin=@23471d1.bin,0 -23471d2.bin=@23471d2.bin,0 -23471n0.bin=@23471n0.bin,0 -23471n1.bin=@23471n1.bin,0 -23471n2.bin=@23471n2.bin,0 -21731n0.bin=@21731n0.bin,0 -21731n1.bin=@21731n1.bin,0 -21731n2.bin=@21731n2.bin,0 -21746d0.bin=@21746d0.bin,0 -21746d1.bin=@21746d1.bin,0 -21746d2.bin=@21746d2.bin,0 -21746n0.bin=@21746n0.bin,0 -21746n1.bin=@21746n1.bin,0 -21746n2.bin=@21746n2.bin,0 -21731d0.bin=@21731d0.bin,0 -21731d1.bin=@21731d1.bin,0 -21731d2.bin=@21731d2.bin,0 -54633d0.bin=@54633d0.bin,0 -54633d1.bin=@54633d1.bin,0 -54633d2.bin=@54633d2.bin,0 -54633n0.bin=@54633n0.bin,0 -54633n1.bin=@54633n1.bin,0 -54633n2.bin=@54633n2.bin,0 -54801d0.bin=@54801d0.bin,0 -54801d1.bin=@54801d1.bin,0 -54801d2.bin=@54801d2.bin,0 -54801n0.bin=@54801n0.bin,0 -54801n1.bin=@54801n1.bin,0 -54801n2.bin=@54801n2.bin,0 -55203d0.bin=@55203d0.bin,0 -55203d1.bin=@55203d1.bin,0 -55203d2.bin=@55203d2.bin,0 -55203n0.bin=@55203n0.bin,0 -55203n1.bin=@55203n1.bin,0 -55203n2.bin=@55203n2.bin,0 -55204d0.bin=@55204d0.bin,0 -55204d1.bin=@55204d1.bin,0 -55204d2.bin=@55204d2.bin,0 -55204n0.bin=@55204n0.bin,0 -55204n1.bin=@55204n1.bin,0 -55204n2.bin=@55204n2.bin,0 -55464d0.bin=@55464d0.bin,0 -55464d1.bin=@55464d1.bin,0 -55464d2.bin=@55464d2.bin,0 -55464n0.bin=@55464n0.bin,0 -55464n1.bin=@55464n1.bin,0 -55464n2.bin=@55464n2.bin,0 -55619d0.bin=@55619d0.bin,0 -55619d1.bin=@55619d1.bin,0 -55619d2.bin=@55619d2.bin,0 -55619n0.bin=@55619n0.bin,0 -55619n1.bin=@55619n1.bin,0 -55619n2.bin=@55619n2.bin,0 -55730d0.bin=@55730d0.bin,0 -55730d1.bin=@55730d1.bin,0 -55730d2.bin=@55730d2.bin,0 -55730n0.bin=@55730n0.bin,0 -55730n1.bin=@55730n1.bin,0 -55730n2.bin=@55730n2.bin,0 -55771d0.bin=@55771d0.bin,0 -55771d1.bin=@55771d1.bin,0 -55771d2.bin=@55771d2.bin,0 -55771n0.bin=@55771n0.bin,0 -55771n1.bin=@55771n1.bin,0 -55771n2.bin=@55771n2.bin,0 -55896d0.bin=@55896d0.bin,0 -55896d1.bin=@55896d1.bin,0 -55896d2.bin=@55896d2.bin,0 -55896n0.bin=@55896n0.bin,0 -55896n1.bin=@55896n1.bin,0 -55896n2.bin=@55896n2.bin,0 -55937d0.bin=@55937d0.bin,0 -55937d1.bin=@55937d1.bin,0 -55937d2.bin=@55937d2.bin,0 -55937n0.bin=@55937n0.bin,0 -55937n1.bin=@55937n1.bin,0 -55937n2.bin=@55937n2.bin,0 -55938d0.bin=@55938d0.bin,0 -55938d1.bin=@55938d1.bin,0 -55938d2.bin=@55938d2.bin,0 -55938n0.bin=@55938n0.bin,0 -55938n1.bin=@55938n1.bin,0 -55938n2.bin=@55938n2.bin,0 -55939d0.bin=@55939d0.bin,0 -55939d1.bin=@55939d1.bin,0 -55939d2.bin=@55939d2.bin,0 -55939n0.bin=@55939n0.bin,0 -55939n1.bin=@55939n1.bin,0 -55939n2.bin=@55939n2.bin,0 -55964d0.bin=@55964d0.bin,0 -55964d1.bin=@55964d1.bin,0 -55964d2.bin=@55964d2.bin,0 -55964n0.bin=@55964n0.bin,0 -55964n1.bin=@55964n1.bin,0 -55964n2.bin=@55964n2.bin,0 -55967d0.bin=@55967d0.bin,0 -55967d1.bin=@55967d1.bin,0 -55967d2.bin=@55967d2.bin,0 -55967n0.bin=@55967n0.bin,0 -55967n1.bin=@55967n1.bin,0 -55967n2.bin=@55967n2.bin,0 -56109d0.bin=@56109d0.bin,0 -56109d1.bin=@56109d1.bin,0 -56109d2.bin=@56109d2.bin,0 -56109n0.bin=@56109n0.bin,0 -56109n1.bin=@56109n1.bin,0 -56109n2.bin=@56109n2.bin,0 -56110d0.bin=@56110d0.bin,0 -56110d1.bin=@56110d1.bin,0 -56110d2.bin=@56110d2.bin,0 -56110n0.bin=@56110n0.bin,0 -56110n1.bin=@56110n1.bin,0 -56110n2.bin=@56110n2.bin,0 -56143d0.bin=@56143d0.bin,0 -56143d1.bin=@56143d1.bin,0 -56143d2.bin=@56143d2.bin,0 -56143n0.bin=@56143n0.bin,0 -56143n1.bin=@56143n1.bin,0 -56143n2.bin=@56143n2.bin,0 -56147d0.bin=@56147d0.bin,0 -56147d1.bin=@56147d1.bin,0 -56147d2.bin=@56147d2.bin,0 -56147n0.bin=@56147n0.bin,0 -56147n1.bin=@56147n1.bin,0 -56147n2.bin=@56147n2.bin,0 -56157d0.bin=@56157d0.bin,0 -56157d1.bin=@56157d1.bin,0 -56157d2.bin=@56157d2.bin,0 -56157n0.bin=@56157n0.bin,0 -56157n1.bin=@56157n1.bin,0 -56157n2.bin=@56157n2.bin,0 -56159d0.bin=@56159d0.bin,0 -56159d1.bin=@56159d1.bin,0 -56159d2.bin=@56159d2.bin,0 -56159n0.bin=@56159n0.bin,0 -56159n1.bin=@56159n1.bin,0 -56159n2.bin=@56159n2.bin,0 -64552d0.bin=@64552d0.bin,0 -64552d1.bin=@64552d1.bin,0 -64552d2.bin=@64552d2.bin,0 -64555d0.bin=@64555d0.bin,0 -64555d1.bin=@64555d1.bin,0 -64555d2.bin=@64555d2.bin,0 -64556d0.bin=@64556d0.bin,0 -64556d1.bin=@64556d1.bin,0 -64556d2.bin=@64556d2.bin,0 -51122d0.bin=@51122d0.bin,0 -51122d1.bin=@51122d1.bin,0 -51122d2.bin=@51122d2.bin,0 -51122n0.bin=@51122n0.bin,0 -51122n1.bin=@51122n1.bin,0 -51122n2.bin=@51122n2.bin,0 -55732d0.bin=@55732d0.bin,0 -55732d1.bin=@55732d1.bin,0 -55732d2.bin=@55732d2.bin,0 -55732n0.bin=@55732n0.bin,0 -55732n1.bin=@55732n1.bin,0 -55732n2.bin=@55732n2.bin,0 -55731d0.bin=@55731d0.bin,0 -55731d1.bin=@55731d1.bin,0 -55731d2.bin=@55731d2.bin,0 -55731n0.bin=@55731n0.bin,0 -55731n1.bin=@55731n1.bin,0 -55731n2.bin=@55731n2.bin,0 -55818d0.bin=@55818d0.bin,0 -55818d1.bin=@55818d1.bin,0 -55818d2.bin=@55818d2.bin,0 -55818n0.bin=@55818n0.bin,0 -55818n1.bin=@55818n1.bin,0 -55818n2.bin=@55818n2.bin,0 -56112d0.bin=@56112d0.bin,0 -56112d1.bin=@56112d1.bin,0 -56112d2.bin=@56112d2.bin,0 -56112n0.bin=@56112n0.bin,0 -56112n1.bin=@56112n1.bin,0 -56112n2.bin=@56112n2.bin,0 -56105d0.bin=@56105d0.bin,0 -56105d1.bin=@56105d1.bin,0 -56105d2.bin=@56105d2.bin,0 -56105n0.bin=@56105n0.bin,0 -56105n1.bin=@56105n1.bin,0 -56105n2.bin=@56105n2.bin,0 -58007d2.bin=@58007d2.bin,0 -58008d2.bin=@58008d2.bin,0 -58009d2.bin=@58009d2.bin,0 -58010d2.bin=@58010d2.bin,0 -58129d0.bin=@58129d0.bin,0 -58130d0.bin=@58130d0.bin,0 -58139d0.bin=@58139d0.bin,0 diff --git a/common/mhfcid/mhfcid.go b/common/mhfcid/mhfcid.go new file mode 100644 index 000000000..8b951fd4e --- /dev/null +++ b/common/mhfcid/mhfcid.go @@ -0,0 +1,54 @@ +package mhfcid + +import ( + "math" +) + +// ConvertCID converts a MHF Character ID String to integer +// +// Banned characters: 0, I, O, S +func ConvertCID(ID string) (r uint32) { + if len(ID) != 6 { + return + } + + m := map[rune]uint32{ + '1': 0, + '2': 1, + '3': 2, + '4': 3, + '5': 4, + '6': 5, + '7': 6, + '8': 7, + '9': 8, + 'A': 9, + 'B': 10, + 'C': 11, + 'D': 12, + 'E': 13, + 'F': 14, + 'G': 15, + 'H': 16, + 'J': 17, + 'K': 18, + 'L': 19, + 'M': 20, + 'N': 21, + 'P': 22, + 'Q': 23, + 'R': 24, + 'T': 25, + 'U': 26, + 'V': 27, + 'W': 28, + 'X': 29, + 'Y': 30, + 'Z': 31, + } + + for i, c := range ID { + r += m[c] * uint32(math.Pow(32, float64(i))) + } + return +} diff --git a/common/mhfcourse/mhfcourse.go b/common/mhfcourse/mhfcourse.go index f838e64a5..71ccc0ab7 100644 --- a/common/mhfcourse/mhfcourse.go +++ b/common/mhfcourse/mhfcourse.go @@ -1,6 +1,7 @@ package mhfcourse import ( + _config "erupe-ce/config" "math" "sort" "time" @@ -66,12 +67,15 @@ func CourseExists(ID uint16, c []Course) bool { // GetCourseStruct returns a slice of Course(s) from a rights integer func GetCourseStruct(rights uint32) ([]Course, uint32) { - resp := []Course{{ID: 1}, {ID: 23}, {ID: 24}} + var resp []Course + for _, c := range _config.ErupeConfig.DefaultCourses { + resp = append(resp, Course{ID: c}) + } s := Courses() sort.Slice(s, func(i, j int) bool { return s[i].ID > s[j].ID }) - var normalCafeCourseSet, netcafeCourseSet bool + var normalCafeCourseSet, netcafeCourseSet, hidenCourseSet bool for _, course := range s { if rights-course.Value() < 0x80000000 { switch course.ID { @@ -88,6 +92,12 @@ func GetCourseStruct(rights uint32) ([]Course, uint32) { } netcafeCourseSet = true resp = append(resp, Course{ID: 30}) + case 10: + if hidenCourseSet { + break + } + hidenCourseSet = true + resp = append(resp, Course{ID: 31}) } course.Expiry = time.Date(2030, 1, 1, 0, 0, 0, 0, time.FixedZone("UTC+9", 9*60*60)) resp = append(resp, course) diff --git a/common/stringstack/stringstack.go b/common/stringstack/stringstack.go index 9f6b646ae..a7ddef31e 100644 --- a/common/stringstack/stringstack.go +++ b/common/stringstack/stringstack.go @@ -6,8 +6,7 @@ import ( // StringStack is a basic LIFO "stack" for storing strings. type StringStack struct { - Locked bool - stack []string + stack []string } // New creates a new instance of StringStack @@ -20,20 +19,6 @@ func (s *StringStack) Set(v string) { s.stack = []string{v} } -// Lock freezes the StringStack -func (s *StringStack) Lock() { - if !s.Locked { - s.Locked = true - } -} - -// Unlock unfreezes the StringStack -func (s *StringStack) Unlock() { - if s.Locked { - s.Locked = false - } -} - // Push pushes a string onto the stack. func (s *StringStack) Push(v string) { s.stack = append(s.stack, v) diff --git a/config.json b/config.json index 5d66b9e33..48e166824 100644 --- a/config.json +++ b/config.json @@ -13,29 +13,36 @@ "DeleteOnSaveCorruption": false, "ClientMode": "ZZ", "QuestCacheExpiry": 300, - "ProxyPort": 0, "CommandPrefix": "!", - "DevMode": true, - "DevModeOptions": { - "AutoCreateAccount": true, + "AutoCreateAccount": true, + "DefaultCourses": [1, 23, 24], + "EarthStatus": 0, + "EarthID": 0, + "EarthMonsters": [0, 0, 0, 0], + "SaveDumps": { + "Enabled": true, + "RawEnabled": false, + "OutputDir": "save-backups" + }, + "DebugOptions": { "CleanDB": false, "MaxLauncherHR": false, "LogInboundMessages": false, "LogOutboundMessages": false, "LogMessageData": false, "MaxHexdumpLength": 256, - "DivaEvent": 0, - "FestaEvent": -1, - "TournamentEvent": 0, + "DivaOverride": 0, + "FestaOverride": -1, + "TournamentOverride": 0, "DisableTokenCheck": false, - "QuestDebugTools": false, - "EarthStatusOverride": 0, - "EarthIDOverride": 0, - "EarthMonsterOverride": [0, 0, 0, 0], - "SaveDumps": { - "Enabled": true, - "RawEnabled": false, - "OutputDir": "save-backups" + "QuestTools": false, + "AutoQuestBackport": true, + "ProxyPort": 0, + "CapLink": { + "Values": [51728, 20000, 51729, 1, 20000], + "Key": "", + "Host": "", + "Port": 80 } }, "GameplayOptions": { @@ -51,8 +58,6 @@ "ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]], "BonusQuestAllowance": 3, "DailyQuestAllowance": 1, - "MezfesSoloTickets": 10, - "MezfesGroupTickets": 4, "LowLatencyRaviente": false, "RegularRavienteMaxPlayers": 8, "ViolentRavienteMaxPlayers": 8, @@ -61,12 +66,29 @@ "SmallBerserkRavienteMaxPlayers": 8, "GUrgentRate": 0.10, "GCPMultiplier": 1.00, + "HRPMultiplier": 1.00, + "HRPMultiplierNC": 1.00, + "SRPMultiplier": 1.00, + "SRPMultiplierNC": 1.00, "GRPMultiplier": 1.00, + "GRPMultiplierNC": 1.00, "GSRPMultiplier": 1.00, + "GSRPMultiplierNC": 1.00, + "ZennyMultiplier": 1.00, + "ZennyMultiplierNC": 1.00, "GZennyMultiplier": 1.00, + "GZennyMultiplierNC": 1.00, "MaterialMultiplier": 1.00, + "MaterialMultiplierNC": 1.00, + "GMaterialMultiplier": 1.00, + "GMaterialMultiplierNC": 1.00, "ExtraCarves": 0, + "ExtraCarvesNC": 0, + "GExtraCarves": 0, + "GExtraCarvesNC": 0, "DisableHunterNavi": false, + "MezFesSoloTickets": 5, + "MezFesGroupTickets": 1, "MezFesDuration": 172800, "MezFesSwitchMinigame": false, "EnableKaijiEvent": false, @@ -78,7 +100,11 @@ "Discord": { "Enabled": false, "BotToken": "", - "RealtimeChannelID": "" + "RelayChannel": { + "Enabled": false, + "MaxMessageLength": 183, + "RelayChannelID": "" + } }, "Commands": [ { @@ -121,6 +147,21 @@ "Enabled": true, "Description": "Link a PlayStation Network ID to your account", "Prefix": "psn" + }, { + "Name": "Discord", + "Enabled": true, + "Description": "Generate a token to link your Discord account", + "Prefix": "discord" + }, { + "Name": "Ban", + "Enabled": false, + "Description": "Ban/Temp Ban a user", + "Prefix": "ban" + }, { + "Name": "Timer", + "Enabled": true, + "Description": "Toggle the Quest timer", + "Prefix": "timer" } ], "Courses": [ diff --git a/config/config.go b/config/config.go index 153cfedb3..b0094f628 100644 --- a/config/config.go +++ b/config/config.go @@ -80,40 +80,23 @@ type Config struct { ClientMode string RealClientMode Mode QuestCacheExpiry int // Number of seconds to keep quest data cached - ProxyPort uint16 // Forces the game to connect to a channel server proxy CommandPrefix string // The prefix for commands - DevMode bool - - DevModeOptions DevModeOptions - GameplayOptions GameplayOptions - Discord Discord - Commands []Command - Courses []Course - Database Database - Sign Sign - SignV2 SignV2 - Channel Channel - Entrance Entrance -} - -// DevModeOptions holds various debug/temporary options for use while developing Erupe. -type DevModeOptions struct { - AutoCreateAccount bool // Automatically create accounts if they don't exist - CleanDB bool // Automatically wipes the DB on server reset. - MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. - LogInboundMessages bool // Log all messages sent to the server - LogOutboundMessages bool // Log all messages sent to the clients - LogMessageData bool // Log all bytes transferred as a hexdump - MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled - DivaEvent int // Diva Defense event status - FestaEvent int // Hunter's Festa event status - TournamentEvent int // VS Tournament event status - DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) - QuestDebugTools bool // Enable various quest debug logs - EarthStatusOverride int32 - EarthIDOverride int32 - EarthMonsterOverride []int32 - SaveDumps SaveDumpOptions + AutoCreateAccount bool // Automatically create accounts if they don't exist + DefaultCourses []uint16 + EarthStatus int32 + EarthID int32 + EarthMonsters []int32 + SaveDumps SaveDumpOptions + DebugOptions DebugOptions + GameplayOptions GameplayOptions + Discord Discord + Commands []Command + Courses []Course + Database Database + Sign Sign + SignV2 SignV2 + Channel Channel + Entrance Entrance } type SaveDumpOptions struct { @@ -122,6 +105,31 @@ type SaveDumpOptions struct { OutputDir string } +// DebugOptions holds various debug/temporary options for use while developing Erupe. +type DebugOptions struct { + CleanDB bool // Automatically wipes the DB on server reset. + MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. + LogInboundMessages bool // Log all messages sent to the server + LogOutboundMessages bool // Log all messages sent to the clients + LogMessageData bool // Log all bytes transferred as a hexdump + MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled + DivaOverride int // Diva Defense event status + FestaOverride int // Hunter's Festa event status + TournamentOverride int // VS Tournament event status + DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) + QuestTools bool // Enable various quest debug logs + AutoQuestBackport bool // Automatically backport quest files + ProxyPort uint16 // Forces the game to connect to a channel server proxy + CapLink CapLinkOptions +} + +type CapLinkOptions struct { + Values []uint16 + Key string + Host string + Port int +} + // GameplayOptions has various gameplay modifiers type GameplayOptions struct { FeaturedWeapons int // Number of Active Feature weapons to generate daily @@ -137,8 +145,6 @@ type GameplayOptions struct { ClanMemberLimits [][]uint8 // Array of maximum Clan Members -> [Rank, Members] BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily DailyQuestAllowance uint32 // Number of Daily Quests to allow daily - MezfesSoloTickets uint32 // Number of solo tickets given weekly - MezfesGroupTickets uint32 // Number of group tickets given weekly LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive RegularRavienteMaxPlayers uint8 ViolentRavienteMaxPlayers uint8 @@ -147,12 +153,29 @@ type GameplayOptions struct { SmallBerserkRavienteMaxPlayers uint8 GUrgentRate float32 // Adjusts the rate of G Urgent quests spawning GCPMultiplier float32 // Adjusts the multiplier of GCP rewarded for quest completion + HRPMultiplier float32 // Adjusts the multiplier of Hunter Rank Points rewarded for quest completion + HRPMultiplierNC float32 // Adjusts the multiplier of Hunter Rank Points rewarded for quest completion in a NetCafe + SRPMultiplier float32 // Adjusts the multiplier of Skill Rank Points rewarded for quest completion + SRPMultiplierNC float32 // Adjusts the multiplier of Skill Rank Points rewarded for quest completion in a NetCafe GRPMultiplier float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion + GRPMultiplierNC float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion in a NetCafe GSRPMultiplier float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion + GSRPMultiplierNC float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion in a NetCafe + ZennyMultiplier float32 // Adjusts the multiplier of Zenny rewarded for quest completion + ZennyMultiplierNC float32 // Adjusts the multiplier of Zenny rewarded for quest completion in a NetCafe GZennyMultiplier float32 // Adjusts the multiplier of G Zenny rewarded for quest completion + GZennyMultiplierNC float32 // Adjusts the multiplier of G Zenny rewarded for quest completion in a NetCafe MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion + MaterialMultiplierNC float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion in a NetCafe + GMaterialMultiplier float32 // Adjusts the multiplier of G Rank Monster Materials rewarded for quest completion + GMaterialMultiplierNC float32 // Adjusts the multiplier of G Rank Monster Materials rewarded for quest completion in a NetCafe ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses + ExtraCarvesNC uint16 // Grant n extra chances to carve ALL carcasses in a NetCafe + GExtraCarves uint16 // Grant n extra chances to carve ALL G Rank carcasses + GExtraCarvesNC uint16 // Grant n extra chances to carve ALL G Rank carcasses in a NetCafe DisableHunterNavi bool // Disables the Hunter Navi + MezFesSoloTickets uint32 // Number of solo tickets given weekly + MezFesGroupTickets uint32 // Number of group tickets given weekly MezFesDuration int // Seconds that MezFes will last for weekly (from 12AM Mon backwards) MezFesSwitchMinigame bool // Swaps out Volpakkun Together for Tokotoko Partnya EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar @@ -164,9 +187,15 @@ type GameplayOptions struct { // Discord holds the discord integration config. type Discord struct { - Enabled bool - BotToken string - RealtimeChannelID string + Enabled bool + BotToken string + RelayChannel DiscordRelay +} + +type DiscordRelay struct { + Enabled bool + MaxMessageLength int + RelayChannelID string } // Command is a channelserver chat command diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..3f45a12c0 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,66 @@ +# Docker for erupe + +## Building the container +Run the following from the route of the soruce folder. In this example we give it the tag of dev to seperate it from any other container verions. +```bash +docker build . -t erupe:dev +``` +## Running the container in isolation +This is just running the container. You can do volume mounts into the container for the `config.json` to tell it to communicate to a database. You will need to do this also for other folders such as `bin` and `savedata` +```bash +docker run erupe:dev +``` + +## Docker compose +Docker compose allows you to run multiple containers at once. The docker compose in this folder has 3 things set up. +- postgres +- pg admin (Admin interface to make db changes) +- erupe + +We automatically populate the database to the latest version on start. If you you are updating you will need to apply the new schemas manually. + +Before we get started you should make sure the database info matches whats in the docker compose file for the environment variables `POSTGRES_PASSWORD`,`POSTGRES_USER` and `POSTGRES_DB`. You can set the host to be the service name `db`. + +Here is a example of what you would put in the config.json if you was to leave the defaults. It is strongly recommended to change the password. +```txt +"Database": { + "Host": "db", + "Port": 5432, + "User": "postgres", + "Password": "password", + "Database": "erupe" + }, +``` + +Place this file within ./docker/config.json + +You will need to do the same for your bins place these in ./docker/bin + +# Setting up the web hosted materials +Clone the Severs repo into ./docker/Severs + +Make sure your hosts are pointing to where this is hosted + + + +## Turning off the server safely +```bash +docker-compose stop +``` + +## Turning off the server destructive +```bash +docker-compose down +``` +Make sure if you want to delete your data you delete the folders that persisted +- ./docker/savedata +- ./docker/db-data +## Turning on the server again +This boots the db pgadmin and the server in a detached state +```bash +docker-compose up -d +``` +if you want all the logs and you want it to be in an attached state +```bash +docker-compose up +``` diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..a610836ee --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,71 @@ +version: "3.9" +# 1. docker-compose up db pgadmin +# 2. Use pgadmin to restore db and also apply patch-schema +# 3. Configure the config.json example. in docker you can point to the service name for the database i.e db +# 4. In seperate terminal docker-compose up server +# 5. If all went well happy hunting! +services: + db: + image: postgres + environment: + # (Make sure these match config.json) + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + - POSTGRES_DB=erupe + ports: + - "5432:5432" + volumes: + - ./db-data/:/var/lib/postgresql/data/ + - ../schemas/:/schemas/ + - ./init/setup.sh:/docker-entrypoint-initdb.d/setup.sh + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + pgadmin: + image: dpage/pgadmin4 + restart: always + environment: + PGADMIN_DEFAULT_EMAIL: user@pgadmin.com + PGADMIN_DEFAULT_PASSWORD: password + ports: + - "5050:80" + depends_on: + db: + condition: service_healthy + server: + depends_on: + db: + condition: service_healthy + # If using prebuilt container change paths and config + build: + context: ../ + volumes: + - ./config.json:/app/erupe/config.json + - ./bin:/app/erupe/bin + - ./savedata:/app/erupe/savedata + ports: + # (Make sure these match config.json) + - "53312:53312" #Sign V1 + - "8080:8080" #Sign V2 + - "53310:53310" #Entrance + # Channels + - "54001:54001" + - "54002:54002" + - "54003:54003" + - "54004:54004" + - "54005:54005" + - "54006:54006" + - "54007:54007" + - "54008:54008" + web: + image: httpd:latest + container_name: my-apache-app + ports: + - '80:80' + volumes: + - ./Servers:/usr/local/apache2/htdocs + depends_on: + db: + condition: service_healthy \ No newline at end of file diff --git a/docker/init/setup.sh b/docker/init/setup.sh new file mode 100644 index 000000000..b84f83b4d --- /dev/null +++ b/docker/init/setup.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e +echo "INIT!" +pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/initialisation-schema/9.1-init.sql + + + +echo "Updating!" + +for file in /schemas/update-schema/* +do + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -1 -f $file +done + + + +echo "Patching!" + +for file in /schemas/patch-schema/* +do + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" -1 -f $file +done \ No newline at end of file diff --git a/erupe.ico b/erupe.ico new file mode 100644 index 000000000..e1358c741 Binary files /dev/null and b/erupe.ico differ diff --git a/go.mod b/go.mod index 410eb88b0..5047c6609 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/lib/pq v1.10.9 github.com/spf13/viper v1.17.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.15.0 + golang.org/x/crypto v0.17.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/text v0.14.0 ) @@ -32,7 +32,7 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1c6fbeb53..d2cc39776 100644 --- a/go.sum +++ b/go.sum @@ -220,8 +220,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -345,8 +345,8 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index c56d90b0f..a7d368930 100644 --- a/main.go +++ b/main.go @@ -22,13 +22,10 @@ import ( ) // Temporary DB auto clean on startup for quick development & testing. -func cleanDB(db *sqlx.DB, config *_config.Config) { +func cleanDB(db *sqlx.DB) { _ = db.MustExec("DELETE FROM guild_characters") _ = db.MustExec("DELETE FROM guilds") _ = db.MustExec("DELETE FROM characters") - if config.ProxyPort == 0 { - _ = db.MustExec("DELETE FROM sign_sessions") - } _ = db.MustExec("DELETE FROM users") } @@ -48,11 +45,7 @@ func main() { var zapLogger *zap.Logger config := _config.ErupeConfig - if config.DevMode { - zapLogger, _ = zap.NewDevelopment() - } else { - zapLogger, _ = zap.NewProduction() - } + zapLogger, _ = zap.NewDevelopment() defer zapLogger.Sync() logger := zapLogger.Named("main") @@ -98,6 +91,12 @@ func main() { } discordBot = bot + + _, err = discordBot.Session.ApplicationCommandBulkOverwrite(discordBot.Session.State.User.ID, "", discordbot.Commands) + if err != nil { + preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error())) + } + logger.Info("Discord: Started successfully") } else { logger.Info("Discord: Disabled") @@ -126,16 +125,16 @@ func main() { logger.Info("Database: Started successfully") // Clear stale data - if config.ProxyPort == 0 { + if config.DebugOptions.ProxyPort == 0 { _ = db.MustExec("DELETE FROM sign_sessions") } _ = db.MustExec("DELETE FROM servers") _ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`) // Clean the DB if the option is on. - if config.DevMode && config.DevModeOptions.CleanDB { + if config.DebugOptions.CleanDB { logger.Info("Database: Started clearing...") - cleanDB(db, config) + cleanDB(db) logger.Info("Database: Finished clearing") } diff --git a/network/binpacket/msg_bin_chat.go b/network/binpacket/msg_bin_chat.go index 44bd587ca..b39a43795 100644 --- a/network/binpacket/msg_bin_chat.go +++ b/network/binpacket/msg_bin_chat.go @@ -11,7 +11,8 @@ type ChatType uint8 // Chat types const ( - ChatTypeLocal ChatType = 1 + ChatTypeWorld ChatType = 0 + ChatTypeStage = 1 ChatTypeGuild = 2 ChatTypeAlliance = 3 ChatTypeParty = 4 diff --git a/network/crypt_conn.go b/network/crypt_conn.go index 2fc302b18..de9181855 100644 --- a/network/crypt_conn.go +++ b/network/crypt_conn.go @@ -4,11 +4,10 @@ import ( "encoding/hex" "errors" _config "erupe-ce/config" + "erupe-ce/network/crypto" "fmt" "io" "net" - - "erupe-ce/network/crypto" ) // CryptConn represents a MHF encrypted two-way connection, @@ -67,7 +66,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { cc.readKeyRot = uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1) } - out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil) + out, combinedCheck, check0, check1, check2 := crypto.Crypto(encryptedPacketBody, cc.readKeyRot, false, nil) if cph.Check0 != check0 || cph.Check1 != check1 || cph.Check2 != check2 { fmt.Printf("got c0 %X, c1 %X, c2 %X\n", check0, check1, check2) fmt.Printf("want c0 %X, c1 %X, c2 %X\n", cph.Check0, cph.Check1, cph.Check2) @@ -77,7 +76,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { // Attempt to bruteforce it. fmt.Println("Crypto out of sync? Attempting bruteforce") for key := byte(0); key < 255; key++ { - out, combinedCheck, check0, check1, check2 = crypto.Decrypt(encryptedPacketBody, 0, &key) + out, combinedCheck, check0, check1, check2 = crypto.Crypto(encryptedPacketBody, 0, false, &key) //fmt.Printf("Key: 0x%X\n%s\n", key, hex.Dump(out)) if cph.Check0 == check0 && cph.Check1 == check1 && cph.Check2 == check2 { fmt.Printf("Bruceforce successful, override key: 0x%X\n", key) @@ -106,7 +105,7 @@ func (cc *CryptConn) SendPacket(data []byte) error { } // Encrypt the data - encData, combinedCheck, check0, check1, check2 := crypto.Encrypt(data, cc.sendKeyRot, nil) + encData, combinedCheck, check0, check1, check2 := crypto.Crypto(data, cc.sendKeyRot, true, nil) header := &CryptPacketHeader{} header.Pf0 = byte(((uint(len(encData)) >> 12) & 0xF3) | 3) @@ -123,9 +122,7 @@ func (cc *CryptConn) SendPacket(data []byte) error { return err } - cc.conn.Write(headerBytes) - cc.conn.Write(encData) - + cc.conn.Write(append(headerBytes, encData...)) cc.sentPackets++ cc.prevSendPacketCombinedCheck = combinedCheck diff --git a/network/crypto/crypto.go b/network/crypto/crypto.go index 87746fa90..59e631786 100644 --- a/network/crypto/crypto.go +++ b/network/crypto/crypto.go @@ -6,46 +6,30 @@ var ( _sharedCryptKey = []byte{0xDD, 0xA8, 0x5F, 0x1E, 0x57, 0xAF, 0xC0, 0xCC, 0x43, 0x35, 0x8F, 0xBB, 0x6F, 0xE6, 0xA1, 0xD6, 0x60, 0xB9, 0x1A, 0xAE, 0x20, 0x49, 0x24, 0x81, 0x21, 0xFE, 0x86, 0x2B, 0x98, 0xB7, 0xB3, 0xD2, 0x91, 0x01, 0x3A, 0x4C, 0x65, 0x92, 0x1C, 0xF4, 0xBE, 0xDD, 0xD9, 0x08, 0xE6, 0x81, 0x98, 0x1B, 0x8D, 0x60, 0xF3, 0x6F, 0xA1, 0x47, 0x24, 0xF1, 0x53, 0x45, 0xC8, 0x7B, 0x88, 0x80, 0x4E, 0x36, 0xC3, 0x0D, 0xC9, 0xD6, 0x8B, 0x08, 0x19, 0x0B, 0xA5, 0xC1, 0x11, 0x4C, 0x60, 0xF8, 0x5D, 0xFC, 0x15, 0x68, 0x7E, 0x32, 0xC0, 0x50, 0xAB, 0x64, 0x1F, 0x8A, 0xD4, 0x08, 0x39, 0x7F, 0xC2, 0xFB, 0xBA, 0x6C, 0xF0, 0xE6, 0xB0, 0x31, 0x10, 0xC1, 0xBF, 0x75, 0x43, 0xBB, 0x18, 0x04, 0x0D, 0xD1, 0x97, 0xF7, 0x23, 0x21, 0x83, 0x8B, 0xCA, 0x25, 0x2B, 0xA3, 0x03, 0x13, 0xEA, 0xAE, 0xFE, 0xF0, 0xEB, 0xFD, 0x85, 0x57, 0x53, 0x65, 0x41, 0x2A, 0x40, 0x99, 0xC0, 0x94, 0x65, 0x7E, 0x7C, 0x93, 0x82, 0xB0, 0xB3, 0xE5, 0xC0, 0x21, 0x09, 0x84, 0xD5, 0xEF, 0x9F, 0xD1, 0x7E, 0xDC, 0x4D, 0xF5, 0x7E, 0xCD, 0x45, 0x3C, 0x7F, 0xF5, 0x59, 0x98, 0xC6, 0x55, 0xFC, 0x9F, 0xA3, 0xB7, 0x74, 0xEE, 0x31, 0x98, 0xE6, 0xB7, 0xBE, 0x26, 0xF4, 0x3C, 0x76, 0xF1, 0x23, 0x7E, 0x02, 0x4E, 0x3C, 0xD1, 0xC7, 0x28, 0x23, 0x73, 0xC4, 0xD9, 0x5E, 0x0D, 0xA1, 0x80, 0xA5, 0xAA, 0x26, 0x0A, 0xA3, 0x44, 0x82, 0x74, 0xE6, 0x3C, 0x44, 0x27, 0x51, 0x0D, 0x5F, 0xC7, 0x9C, 0xD6, 0x63, 0x67, 0xA5, 0x27, 0x97, 0x38, 0xFB, 0x2D, 0xD3, 0xD6, 0x60, 0x25, 0x83, 0x4D, 0x37, 0x5B, 0x40, 0x59, 0x11, 0x77, 0x51, 0x11, 0x14, 0x18, 0x07, 0x63, 0xB1, 0x34, 0x3D, 0xB8, 0x60, 0x13, 0xC2, 0xE8, 0x13, 0x82} ) -// Encrypt encrypts the given data using MHF's custom encryption+checksum method. -// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte. -func Encrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) { - return _generalCrypt(data, key, 0, overrideByteKey) -} - -// Decrypt decrypts the given data using MHF's custom decryption+checksum method. -// if a overrideByteKey value is supplied (!= nil), it will be used to override the derived/truncated key byte. -func Decrypt(data []byte, key uint32, overrideByteKey *byte) (outputData []byte, combinedCheck uint16, check0 uint16, check1 uint16, check2 uint16) { - return _generalCrypt(data, key, 1, overrideByteKey) -} - -// _generalCrypt is a generalized MHF crypto function that can perform both encryption and decryption, +// Crypto is a generalized MHF crypto function that can perform both encryption and decryption, // these two crypto operations are combined into a single function because they shared most of their logic. -// encrypt: cryptType==0 -// decrypt: cryptType==1 -func _generalCrypt(data []byte, rotKey uint32, cryptType int, overrideByteKey *byte) ([]byte, uint16, uint16, uint16, uint16) { +func Crypto(data []byte, rotKey uint32, encrypt bool, overrideByteKey *byte) ([]byte, uint16, uint16, uint16, uint16) { cryptKeyTruncByte := byte(((rotKey >> 1) % 999983) & 0xFF) if overrideByteKey != nil { cryptKeyTruncByte = *overrideByteKey } - derivedCryptKey := int32((uint32(len(data)) * (uint32(cryptKeyTruncByte) + 1)) & 0xFFFFFFFF) + derivedCryptKey := (uint32(len(data)) * (uint32(cryptKeyTruncByte) + 1)) & 0xFFFFFFFF sharedBufIdx := byte(1) - accumulator0 := uint32(0) - accumulator1 := uint32(0) - accumulator2 := uint32(0) + var accumulator0, accumulator1, accumulator2 uint32 var outputData []byte - if cryptType == 0 { + if encrypt { for i := 0; i < len(data); i++ { // Do the encryption for this iteration - encKeyIdx := int32(((uint32(derivedCryptKey) >> 10) ^ uint32(data[i])) & 0xFF) - derivedCryptKey = (0x4FD * (derivedCryptKey + 1)) + encKeyIdx := ((derivedCryptKey >> 10) ^ uint32(data[i])) & 0xFF + derivedCryptKey = 1277*derivedCryptKey + 1277 encKeyByte := _encryptKey[encKeyIdx] // Update the checksum accumulators. - accumulator2 = uint32((accumulator2 + (uint32(sharedBufIdx) * uint32(data[i]))) & 0xFFFFFFFF) - accumulator1 = uint32((accumulator1 + uint32(encKeyIdx)) & 0xFFFFFFFF) - accumulator0 = uint32((accumulator0 + (uint32(encKeyByte)<<(i&7))&0xFFFFFFFF) & 0xFFFFFFFF) + accumulator2 = accumulator2 + (uint32(sharedBufIdx) * uint32(data[i])) + accumulator1 = accumulator1 + encKeyIdx + accumulator0 = accumulator0 + uint32(encKeyByte)<<(i&7) // Append the output. outputData = append(outputData, _sharedCryptKey[sharedBufIdx]^encKeyByte) @@ -53,32 +37,32 @@ func _generalCrypt(data []byte, rotKey uint32, cryptType int, overrideByteKey *b // Update the sharedBufIdx for the next iteration. sharedBufIdx = data[i] } - - } else if cryptType == 1 { + } else { for i := 0; i < len(data); i++ { // Do the decryption for this iteration oldSharedBufIdx := sharedBufIdx tIdx := data[i] ^ _sharedCryptKey[sharedBufIdx] decKeyByte := _decryptKey[tIdx] - sharedBufIdx = byte(((uint32(derivedCryptKey) >> 10) ^ uint32(decKeyByte)) & 0xFF) + sharedBufIdx = byte((derivedCryptKey >> 10) ^ uint32(decKeyByte)) // Update the checksum accumulators. - accumulator0 = (accumulator0 + ((uint32(tIdx) << (i & 7)) & 0xFFFFFFFF)) - accumulator1 = (accumulator1 + uint32(decKeyByte)) & 0xFFFFFFFF - accumulator2 = (accumulator2 + ((uint32(oldSharedBufIdx) * uint32(sharedBufIdx)) & 0xFFFFFFFF)) & 0xFFFFFFFF + accumulator0 = accumulator0 + uint32(tIdx)<<(i&7) + accumulator1 = accumulator1 + uint32(decKeyByte) + accumulator2 = accumulator2 + uint32(oldSharedBufIdx)*uint32(sharedBufIdx) // Append the output. outputData = append(outputData, sharedBufIdx) // Update the key pos for next iteration. - derivedCryptKey = (0x4FD * (derivedCryptKey + 1)) + derivedCryptKey = 1277*derivedCryptKey + 1277 } } - combinedCheck := uint16((accumulator1 + (accumulator0 >> 1) + (accumulator2 >> 2)) & 0xFFFF) - check0 := uint16((accumulator0 ^ ((accumulator0 & 0xFFFF0000) >> 16)) & 0xFFFF) - check1 := uint16((accumulator1 ^ ((accumulator1 & 0xFFFF0000) >> 16)) & 0xFFFF) - check2 := uint16((accumulator2 ^ ((accumulator2 & 0xFFFF0000) >> 16)) & 0xFFFF) + var check [4]uint16 + check[0] = uint16(accumulator1 + (accumulator0 >> 1) + (accumulator2 >> 2)) + check[1] = uint16(accumulator0 ^ ((accumulator0 & 0xFFFF0000) >> 16)) + check[2] = uint16(accumulator1 ^ ((accumulator1 & 0xFFFF0000) >> 16)) + check[3] = uint16(accumulator2 ^ ((accumulator2 & 0xFFFF0000) >> 16)) - return outputData, combinedCheck, check0, check1, check2 + return outputData, check[0], check[1], check[2], check[3] } diff --git a/network/crypto/crypto_test.go b/network/crypto/crypto_test.go index 32ff7ee7c..5093e429f 100644 --- a/network/crypto/crypto_test.go +++ b/network/crypto/crypto_test.go @@ -65,7 +65,7 @@ func TestEncrypt(t *testing.T) { for k, tt := range tests { testname := fmt.Sprintf("encrypt_test_%d", k) t.Run(testname, func(t *testing.T) { - out, cc, c0, c1, c2 := Encrypt(tt.decryptedData, tt.key, nil) + out, cc, c0, c1, c2 := Crypto(tt.decryptedData, tt.key, true, nil) if cc != tt.ecc { t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc) } else if c0 != tt.ec0 { @@ -86,7 +86,7 @@ func TestDecrypt(t *testing.T) { for k, tt := range tests { testname := fmt.Sprintf("decrypt_test_%d", k) t.Run(testname, func(t *testing.T) { - out, cc, c0, c1, c2 := Decrypt(tt.encryptedData, tt.key, nil) + out, cc, c0, c1, c2 := Crypto(tt.decryptedData, tt.key, false, nil) if cc != tt.ecc { t.Errorf("got cc 0x%X, want 0x%X", cc, tt.ecc) } else if c0 != tt.ec0 { diff --git a/network/mhfpacket/msg_mhf_charge_festa.go b/network/mhfpacket/msg_mhf_charge_festa.go index f5452df73..145c2a3a7 100644 --- a/network/mhfpacket/msg_mhf_charge_festa.go +++ b/network/mhfpacket/msg_mhf_charge_festa.go @@ -1,19 +1,20 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfChargeFesta represents the MSG_MHF_CHARGE_FESTA type MsgMhfChargeFesta struct { - AckHandle uint32 - FestaID uint32 - GuildID uint32 - Souls int + AckHandle uint32 + FestaID uint32 + GuildID uint32 + Souls []uint16 + Auto bool } // Opcode returns the ID associated with this packet type. @@ -23,15 +24,14 @@ func (m *MsgMhfChargeFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfChargeFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.FestaID = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - m.Souls = 0 - for i := bf.ReadUint16(); i > 0; i-- { - m.Souls += int(bf.ReadUint16()) - } - _ = bf.ReadUint8() // Unk - return nil + m.AckHandle = bf.ReadUint32() + m.FestaID = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + for i := bf.ReadUint16(); i > 0; i-- { + m.Souls = append(m.Souls, bf.ReadUint16()) + } + m.Auto = bf.ReadBool() + return nil } // Build builds a binary packet from the current data. diff --git a/rsrc_windows_amd64.syso b/rsrc_windows_amd64.syso new file mode 100644 index 000000000..012891568 Binary files /dev/null and b/rsrc_windows_amd64.syso differ diff --git a/bundled-schema/DistributionDemo.sql b/schemas/bundled-schema/DistributionDemo.sql similarity index 100% rename from bundled-schema/DistributionDemo.sql rename to schemas/bundled-schema/DistributionDemo.sql diff --git a/bundled-schema/DivaShops.sql b/schemas/bundled-schema/DivaShops.sql similarity index 100% rename from bundled-schema/DivaShops.sql rename to schemas/bundled-schema/DivaShops.sql diff --git a/bundled-schema/EventQuests.sql b/schemas/bundled-schema/EventQuests.sql similarity index 100% rename from bundled-schema/EventQuests.sql rename to schemas/bundled-schema/EventQuests.sql diff --git a/bundled-schema/FPointItems.sql b/schemas/bundled-schema/FPointItems.sql similarity index 100% rename from bundled-schema/FPointItems.sql rename to schemas/bundled-schema/FPointItems.sql diff --git a/bundled-schema/FestaDefaults.sql b/schemas/bundled-schema/FestaDefaults.sql similarity index 100% rename from bundled-schema/FestaDefaults.sql rename to schemas/bundled-schema/FestaDefaults.sql diff --git a/bundled-schema/GachaDemo.sql b/schemas/bundled-schema/GachaDemo.sql similarity index 100% rename from bundled-schema/GachaDemo.sql rename to schemas/bundled-schema/GachaDemo.sql diff --git a/bundled-schema/NetcafeDefaults.sql b/schemas/bundled-schema/NetcafeDefaults.sql similarity index 100% rename from bundled-schema/NetcafeDefaults.sql rename to schemas/bundled-schema/NetcafeDefaults.sql diff --git a/bundled-schema/OtherShops.sql b/schemas/bundled-schema/OtherShops.sql similarity index 100% rename from bundled-schema/OtherShops.sql rename to schemas/bundled-schema/OtherShops.sql diff --git a/bundled-schema/RoadShopItems.sql b/schemas/bundled-schema/RoadShopItems.sql similarity index 100% rename from bundled-schema/RoadShopItems.sql rename to schemas/bundled-schema/RoadShopItems.sql diff --git a/bundled-schema/ScenarioDefaults.sql b/schemas/bundled-schema/ScenarioDefaults.sql similarity index 100% rename from bundled-schema/ScenarioDefaults.sql rename to schemas/bundled-schema/ScenarioDefaults.sql diff --git a/schemas/initialisation-schema/9.1-init.sql b/schemas/initialisation-schema/9.1-init.sql new file mode 100644 index 000000000..3ae0ca127 Binary files /dev/null and b/schemas/initialisation-schema/9.1-init.sql differ diff --git a/patch-schema/.gitkeep b/schemas/patch-schema/.gitkeep similarity index 100% rename from patch-schema/.gitkeep rename to schemas/patch-schema/.gitkeep diff --git a/patch-schema/00-psn-id.sql b/schemas/patch-schema/00-psn-id.sql similarity index 100% rename from patch-schema/00-psn-id.sql rename to schemas/patch-schema/00-psn-id.sql diff --git a/patch-schema/01-wiiu-key.sql b/schemas/patch-schema/01-wiiu-key.sql similarity index 100% rename from patch-schema/01-wiiu-key.sql rename to schemas/patch-schema/01-wiiu-key.sql diff --git a/patch-schema/02-tower.sql b/schemas/patch-schema/02-tower.sql similarity index 75% rename from patch-schema/02-tower.sql rename to schemas/patch-schema/02-tower.sql index 0697fc2be..732f46c5e 100644 --- a/patch-schema/02-tower.sql +++ b/schemas/patch-schema/02-tower.sql @@ -7,8 +7,8 @@ CREATE TABLE IF NOT EXISTS tower ( tsp INT, block1 INT, block2 INT, - skills TEXT DEFAULT '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0', - gems TEXT DEFAULT '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0' + skills TEXT, + gems TEXT ); ALTER TABLE IF EXISTS guild_characters diff --git a/patch-schema/03-event_quests.sql b/schemas/patch-schema/03-event_quests.sql similarity index 100% rename from patch-schema/03-event_quests.sql rename to schemas/patch-schema/03-event_quests.sql diff --git a/patch-schema/04-trend-weapons.sql b/schemas/patch-schema/04-trend-weapons.sql similarity index 100% rename from patch-schema/04-trend-weapons.sql rename to schemas/patch-schema/04-trend-weapons.sql diff --git a/patch-schema/05-gacha-roll-name.sql b/schemas/patch-schema/05-gacha-roll-name.sql similarity index 100% rename from patch-schema/05-gacha-roll-name.sql rename to schemas/patch-schema/05-gacha-roll-name.sql diff --git a/patch-schema/06-goocoo-rename.sql b/schemas/patch-schema/06-goocoo-rename.sql similarity index 100% rename from patch-schema/06-goocoo-rename.sql rename to schemas/patch-schema/06-goocoo-rename.sql diff --git a/patch-schema/07-scenarios-counter.sql b/schemas/patch-schema/07-scenarios-counter.sql similarity index 100% rename from patch-schema/07-scenarios-counter.sql rename to schemas/patch-schema/07-scenarios-counter.sql diff --git a/patch-schema/08-kill-counts.sql b/schemas/patch-schema/08-kill-counts.sql similarity index 100% rename from patch-schema/08-kill-counts.sql rename to schemas/patch-schema/08-kill-counts.sql diff --git a/patch-schema/09-fix-guild-treasure.sql b/schemas/patch-schema/09-fix-guild-treasure.sql similarity index 100% rename from patch-schema/09-fix-guild-treasure.sql rename to schemas/patch-schema/09-fix-guild-treasure.sql diff --git a/patch-schema/10-rework-distributions.sql b/schemas/patch-schema/10-rework-distributions.sql similarity index 100% rename from patch-schema/10-rework-distributions.sql rename to schemas/patch-schema/10-rework-distributions.sql diff --git a/patch-schema/11-event-quest-flags.sql b/schemas/patch-schema/11-event-quest-flags.sql similarity index 100% rename from patch-schema/11-event-quest-flags.sql rename to schemas/patch-schema/11-event-quest-flags.sql diff --git a/patch-schema/12-event_quest_cycling.sql b/schemas/patch-schema/12-event_quest_cycling.sql similarity index 100% rename from patch-schema/12-event_quest_cycling.sql rename to schemas/patch-schema/12-event_quest_cycling.sql diff --git a/patch-schema/13-festa-trial-votes.sql b/schemas/patch-schema/13-festa-trial-votes.sql similarity index 100% rename from patch-schema/13-festa-trial-votes.sql rename to schemas/patch-schema/13-festa-trial-votes.sql diff --git a/patch-schema/14-fix-fpoint-trades.sql b/schemas/patch-schema/14-fix-fpoint-trades.sql similarity index 100% rename from patch-schema/14-fix-fpoint-trades.sql rename to schemas/patch-schema/14-fix-fpoint-trades.sql diff --git a/patch-schema/15-reset-goocoos.sql b/schemas/patch-schema/15-reset-goocoos.sql similarity index 100% rename from patch-schema/15-reset-goocoos.sql rename to schemas/patch-schema/15-reset-goocoos.sql diff --git a/schemas/patch-schema/16-discord-password-resets.sql b/schemas/patch-schema/16-discord-password-resets.sql new file mode 100644 index 000000000..bd2e83fea --- /dev/null +++ b/schemas/patch-schema/16-discord-password-resets.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.users ADD COLUMN discord_token text; +ALTER TABLE IF EXISTS public.users ADD COLUMN discord_id text; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/17-op-accounts.sql b/schemas/patch-schema/17-op-accounts.sql new file mode 100644 index 000000000..bdf5dccd8 --- /dev/null +++ b/schemas/patch-schema/17-op-accounts.sql @@ -0,0 +1,12 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.users ADD COLUMN op boolean; + +CREATE TABLE public.bans +( + user_id integer NOT NULL, + expires timestamp with time zone, + PRIMARY KEY (user_id) +); + +END; \ No newline at end of file diff --git a/schemas/patch-schema/18-timer-toggle.sql b/schemas/patch-schema/18-timer-toggle.sql new file mode 100644 index 000000000..c2bff008f --- /dev/null +++ b/schemas/patch-schema/18-timer-toggle.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE users ADD COLUMN IF NOT EXISTS timer bool; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/19-festa-submissions.sql b/schemas/patch-schema/19-festa-submissions.sql new file mode 100644 index 000000000..d720c587f --- /dev/null +++ b/schemas/patch-schema/19-festa-submissions.sql @@ -0,0 +1,15 @@ +BEGIN; + +CREATE TABLE festa_submissions ( + character_id int NOT NULL, + guild_id int NOT NULL, + trial_type int NOT NULL, + souls int NOT NULL, + timestamp timestamp with time zone NOT NULL +); + +ALTER TABLE guild_characters DROP COLUMN souls; + +ALTER TYPE festival_colour RENAME TO festival_color; + +END; \ No newline at end of file diff --git a/patch-schema/psn-link.sql b/schemas/patch-schema/psn-link.sql similarity index 100% rename from patch-schema/psn-link.sql rename to schemas/patch-schema/psn-link.sql diff --git a/schemas/update-schema/9.2-update.sql b/schemas/update-schema/9.2-update.sql new file mode 100644 index 000000000..e7dbf699b --- /dev/null +++ b/schemas/update-schema/9.2-update.sql @@ -0,0 +1,241 @@ +BEGIN; + +DROP TABLE IF EXISTS public.fpoint_items; + +CREATE TABLE IF NOT EXISTS public.fpoint_items ( + id serial PRIMARY KEY, + item_type integer, + item_id integer, + quantity integer, + fpoints integer, + trade_type integer +); + +ALTER TABLE IF EXISTS public.characters ADD bonus_quests INT NOT NULL DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters ADD daily_quests INT NOT NULL DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters ADD promo_points INT NOT NULL DEFAULT 0; + +ALTER TABLE IF EXISTS public.guild_characters ADD rp_today INT DEFAULT 0; + +ALTER TABLE IF EXISTS public.guild_characters ADD rp_yesterday INT DEFAULT 0; + +UPDATE public.characters SET savemercenary = NULL; + +ALTER TABLE IF EXISTS public.characters ADD rasta_id INT; + +ALTER TABLE IF EXISTS public.characters ADD pact_id INT; + +ALTER TABLE IF EXISTS public.characters ADD stampcard INT NOT NULL DEFAULT 0; + +ALTER TABLE IF EXISTS public.characters DROP COLUMN IF EXISTS gacha_prem; + +ALTER TABLE IF EXISTS public.characters DROP COLUMN IF EXISTS gacha_trial; + +ALTER TABLE IF EXISTS public.characters DROP COLUMN IF EXISTS frontier_points; + +ALTER TABLE IF EXISTS public.users ADD IF NOT EXISTS gacha_premium INT; + +ALTER TABLE IF EXISTS public.users ADD IF NOT EXISTS gacha_trial INT; + +ALTER TABLE IF EXISTS public.users ADD IF NOT EXISTS frontier_points INT; + +DROP TABLE IF EXISTS public.gacha_shop; + +CREATE TABLE IF NOT EXISTS public.gacha_shop ( + id SERIAL PRIMARY KEY, + min_gr INTEGER, + min_hr INTEGER, + name TEXT, + url_banner TEXT, + url_feature TEXT, + url_thumbnail TEXT, + wide BOOLEAN, + recommended BOOLEAN, + gacha_type INTEGER, + hidden BOOLEAN +); + +DROP TABLE IF EXISTS public.gacha_shop_items; + +CREATE TABLE IF NOT EXISTS public.gacha_entries ( + id SERIAL PRIMARY KEY, + gacha_id INTEGER, + entry_type INTEGER, + item_type INTEGER, + item_number INTEGER, + item_quantity INTEGER, + weight INTEGER, + rarity INTEGER, + rolls INTEGER, + frontier_points INTEGER, + daily_limit INTEGER +); + +CREATE TABLE IF NOT EXISTS public.gacha_items ( + id SERIAL PRIMARY KEY, + entry_id INTEGER, + item_type INTEGER, + item_id INTEGER, + quantity INTEGER +); + +DROP TABLE IF EXISTS public.stepup_state; + +CREATE TABLE IF NOT EXISTS public.gacha_stepup ( + gacha_id INTEGER, + step INTEGER, + character_id INTEGER +); + +DROP TABLE IF EXISTS public.lucky_box_state; + +CREATE TABLE IF NOT EXISTS public.gacha_box ( + gacha_id INTEGER, + entry_id INTEGER, + character_id INTEGER +); + +DROP TABLE IF EXISTS public.login_boost_state; + +CREATE TABLE IF NOT EXISTS public.login_boost ( + char_id INTEGER, + week_req INTEGER, + expiration TIMESTAMP WITH TIME ZONE, + reset TIMESTAMP WITH TIME ZONE +); + +ALTER TABLE IF EXISTS public.characters ADD COLUMN mezfes BYTEA; + +ALTER TABLE IF EXISTS public.characters ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters ALTER COLUMN guild_post_checked TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters ALTER COLUMN boost_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters ADD COLUMN IF NOT EXISTS cafe_reset TIMESTAMP WITHOUT TIME ZONE; + +ALTER TABLE IF EXISTS public.characters ALTER COLUMN cafe_reset TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN deadline TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.events ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.feature_weapon ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE; + +CREATE TABLE IF NOT EXISTS public.feature_weapon +( + start_time TIMESTAMP WITH TIME ZONE NOT NULL, + featured INTEGER NOT NULL +); + +ALTER TABLE IF EXISTS public.guild_alliances ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_applications ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_characters ALTER COLUMN joined_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_posts ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guilds ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.mail ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.stamps ALTER COLUMN hl_next TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.stamps ALTER COLUMN ex_next TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.titles ALTER COLUMN unlocked_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.titles ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.users ALTER COLUMN last_login TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.users ALTER COLUMN return_expires TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_meals DROP COLUMN IF EXISTS expires; + +ALTER TABLE IF EXISTS public.guild_meals ADD COLUMN IF NOT EXISTS created_at TIMESTAMP WITH TIME ZONE; + +DROP TABLE IF EXISTS public.account_ban; + +DROP TABLE IF EXISTS public.account_history; + +DROP TABLE IF EXISTS public.account_moderation; + +DROP TABLE IF EXISTS public.account_sub; + +DROP TABLE IF EXISTS public.history; + +DROP TABLE IF EXISTS public.questlists; + +DROP TABLE IF EXISTS public.schema_migrations; + +DROP TABLE IF EXISTS public.user_binaries; + +DROP PROCEDURE IF EXISTS raviinit; + +DROP PROCEDURE IF EXISTS ravireset; + +ALTER TABLE IF EXISTS public.normal_shop_items RENAME TO shop_items; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN shoptype TO shop_type; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN shopid TO shop_id; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN itemhash TO id; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN itemid TO item_id; + +ALTER TABLE IF EXISTS public.shop_items ALTER COLUMN points TYPE integer; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN points TO cost; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN tradequantity TO quantity; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqlow TO min_hr; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqhigh TO min_sr; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN rankreqg TO min_gr; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN storelevelreq TO store_level; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN maximumquantity TO max_quantity; + +ALTER TABLE IF EXISTS public.shop_items DROP COLUMN IF EXISTS boughtquantity; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN roadfloorsrequired TO road_floors; + +ALTER TABLE IF EXISTS public.shop_items RENAME COLUMN weeklyfataliskills TO road_fatalis; + +ALTER TABLE public.shop_items RENAME CONSTRAINT normal_shop_items_pkey TO shop_items_pkey; + +ALTER TABLE IF EXISTS public.shop_items DROP CONSTRAINT IF EXISTS normal_shop_items_itemhash_key; + +CREATE SEQUENCE IF NOT EXISTS public.shop_items_id_seq; + +ALTER SEQUENCE IF EXISTS public.shop_items_id_seq OWNER TO postgres; + +ALTER TABLE IF EXISTS public.shop_items ALTER COLUMN id SET DEFAULT nextval('shop_items_id_seq'::regclass); + +ALTER SEQUENCE IF EXISTS public.shop_items_id_seq OWNED BY shop_items.id; + +SELECT setval('shop_items_id_seq', (SELECT MAX(id) FROM public.shop_items)); + +DROP TABLE IF EXISTS public.shop_item_state; + +CREATE TABLE IF NOT EXISTS public.shop_items_bought ( + character_id INTEGER, + shop_item_id INTEGER, + bought INTEGER +); + +UPDATE users SET rights = rights-2; + +ALTER TABLE IF EXISTS public.users ALTER COLUMN rights SET DEFAULT 12; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 2abb4633a..ba3161f8b 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -32,7 +32,7 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) { func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) { bf := byteframe.NewByteFrame() - bf.WriteUint32(uint32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)) + bf.WriteUint32(uint32(s.server.erupeConfig.EarthID)) bf.WriteUint32(0) bf.WriteUint32(0) bf.WriteUint32(uint32(len(data))) @@ -128,7 +128,7 @@ func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysLogin) - if !s.server.erupeConfig.DevModeOptions.DisableTokenCheck { + if !s.server.erupeConfig.DebugOptions.DisableTokenCheck { var token string err := s.server.db.QueryRow("SELECT token FROM sign_sessions ss INNER JOIN public.users u on ss.user_id = u.id WHERE token=$1 AND ss.id=$2 AND u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.LoginTokenString, pkt.LoginTokenNumber, pkt.CharID0).Scan(&token) if err != nil { @@ -1148,9 +1148,9 @@ func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride) - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride) - for i, m := range s.server.erupeConfig.DevModeOptions.EarthMonsterOverride { + bf.WriteInt32(s.server.erupeConfig.EarthStatus) + bf.WriteInt32(s.server.erupeConfig.EarthID) + for i, m := range s.server.erupeConfig.EarthMonsters { if _config.ErupeConfig.RealClientMode <= _config.G9 { if i == 3 { break diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 9c4b0b732..b87bac5ff 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -94,7 +94,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(cafeTime) // Total cafe time bf.WriteUint16(0) - ps.Uint16(bf, fmt.Sprintf(s.server.dict["cafeReset"], int(cafeReset.Month()), cafeReset.Day()), true) + ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 71a8a6f07..67e6e3a3a 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -1,8 +1,10 @@ package channelserver import ( + "crypto/rand" "encoding/hex" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfcid" "erupe-ce/common/mhfcourse" "erupe-ce/common/token" "erupe-ce/config" @@ -58,7 +60,7 @@ func init() { } func sendDisabledCommandMessage(s *Session, cmd _config.Command) { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDisabled"], cmd.Name)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.disabled, cmd.Name)) } func sendServerChatMessage(s *Session, message string) { @@ -86,28 +88,97 @@ func sendServerChatMessage(s *Session, message string) { func parseChatCommand(s *Session, command string) { args := strings.Split(command[len(s.server.erupeConfig.CommandPrefix):], " ") switch args[0] { + case commands["Ban"].Prefix: + if s.isOp() { + if len(args) > 1 { + var expiry time.Time + if len(args) > 2 { + var length int + var unit string + n, err := fmt.Sscanf(args[2], `%d%s`, &length, &unit) + if err == nil && n == 2 { + switch unit { + case "s", "second", "seconds": + expiry = time.Now().Add(time.Duration(length) * time.Second) + case "m", "mi", "minute", "minutes": + expiry = time.Now().Add(time.Duration(length) * time.Minute) + case "h", "hour", "hours": + expiry = time.Now().Add(time.Duration(length) * time.Hour) + case "d", "day", "days": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24) + case "mo", "month", "months": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 30) + case "y", "year", "years": + expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 365) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.error) + return + } + } + cid := mhfcid.ConvertCID(args[1]) + if cid > 0 { + var uid uint32 + var uname string + err := s.server.db.QueryRow(`SELECT id, username FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, cid).Scan(&uid, &uname) + if err == nil { + if expiry.IsZero() { + s.server.db.Exec(`INSERT INTO bans VALUES ($1) + ON CONFLICT (user_id) DO UPDATE SET expires=NULL`, uid) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)) + } else { + s.server.db.Exec(`INSERT INTO bans VALUES ($1, $2) + ON CONFLICT (user_id) DO UPDATE SET expires=$2`, uid, expiry) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)+fmt.Sprintf(s.server.i18n.commands.ban.length, expiry.Format(time.DateTime))) + } + s.server.DisconnectUser(uid) + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.noUser) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.invalid) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.ban.error) + } + } else { + sendServerChatMessage(s, s.server.i18n.commands.noOp) + } + case commands["Timer"].Prefix: + if commands["Timer"].Enabled || s.isOp() { + var state bool + s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&state) + s.server.db.Exec(`UPDATE users u SET timer=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, !state, s.charID) + if state { + sendServerChatMessage(s, s.server.i18n.commands.timer.disabled) + } else { + sendServerChatMessage(s, s.server.i18n.commands.timer.enabled) + } + } else { + sendDisabledCommandMessage(s, commands["Timer"]) + } case commands["PSN"].Prefix: - if commands["PSN"].Enabled { + if commands["PSN"].Enabled || s.isOp() { if len(args) > 1 { var exists int s.server.db.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, args[1]).Scan(&exists) if exists == 0 { _, err := s.server.db.Exec(`UPDATE users u SET psn_id=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, args[1], s.charID) if err == nil { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandPSNSuccess"], args[1])) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.success, args[1])) } } else { - sendServerChatMessage(s, s.server.dict["commandPSNExists"]) + sendServerChatMessage(s, s.server.i18n.commands.psn.exists) } } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandPSNError"], commands["PSN"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.error, commands["PSN"].Prefix)) } } else { sendDisabledCommandMessage(s, commands["PSN"]) } case commands["Reload"].Prefix: - if commands["Reload"].Enabled { - sendServerChatMessage(s, s.server.dict["commandReload"]) + if commands["Reload"].Enabled || s.isOp() { + sendServerChatMessage(s, s.server.i18n.commands.reload) var temp mhfpacket.MHFPacket deleteNotif := byteframe.NewByteFrame() for _, object := range s.stage.objects { @@ -167,21 +238,21 @@ func parseChatCommand(s *Session, command string) { sendDisabledCommandMessage(s, commands["Reload"]) } case commands["KeyQuest"].Prefix: - if commands["KeyQuest"].Enabled { + if commands["KeyQuest"].Enabled || s.isOp() { if s.server.erupeConfig.RealClientMode < _config.G10 { - sendServerChatMessage(s, s.server.dict["commandKqfVersion"]) + sendServerChatMessage(s, s.server.i18n.commands.kqf.version) } else { if len(args) > 1 { if args[1] == "get" { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandKqfGet"], s.kqf)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.get, s.kqf)) } else if args[1] == "set" { if len(args) > 2 && len(args[2]) == 16 { hexd, _ := hex.DecodeString(args[2]) s.kqf = hexd s.kqfOverride = true - sendServerChatMessage(s, s.server.dict["commandKqfSetSuccess"]) + sendServerChatMessage(s, s.server.i18n.commands.kqf.set.success) } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandKqfSetError"], commands["KeyQuest"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.set.error, commands["KeyQuest"].Prefix)) } } } @@ -190,23 +261,23 @@ func parseChatCommand(s *Session, command string) { sendDisabledCommandMessage(s, commands["KeyQuest"]) } case commands["Rights"].Prefix: - if commands["Rights"].Enabled { + if commands["Rights"].Enabled || s.isOp() { if len(args) > 1 { v, _ := strconv.Atoi(args[1]) _, err := s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID) if err == nil { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandRightsSuccess"], v)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.success, v)) } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandRightsError"], commands["Rights"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix)) } } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandRightsError"], commands["Rights"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix)) } } else { sendDisabledCommandMessage(s, commands["Rights"]) } case commands["Course"].Prefix: - if commands["Course"].Enabled { + if commands["Course"].Enabled || s.isOp() { if len(args) > 1 { for _, course := range mhfcourse.Courses() { for _, alias := range course.Aliases() { @@ -224,11 +295,11 @@ func parseChatCommand(s *Session, command string) { }) if ei != -1 { delta = uint32(-1 * math.Pow(2, float64(course.ID))) - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseDisabled"], course.Aliases()[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.disabled, course.Aliases()[0])) } } else { delta = uint32(math.Pow(2, float64(course.ID))) - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseEnabled"], course.Aliases()[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.enabled, course.Aliases()[0])) } err := s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt) if err == nil { @@ -236,71 +307,71 @@ func parseChatCommand(s *Session, command string) { } updateRights(s) } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseLocked"], course.Aliases()[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.locked, course.Aliases()[0])) } return } } } } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseError"], commands["Course"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.error, commands["Course"].Prefix)) } } else { sendDisabledCommandMessage(s, commands["Course"]) } case commands["Raviente"].Prefix: - if commands["Raviente"].Enabled { + if commands["Raviente"].Enabled || s.isOp() { if len(args) > 1 { if s.server.getRaviSemaphore() != nil { switch args[1] { case "start": if s.server.raviente.register[1] == 0 { s.server.raviente.register[1] = s.server.raviente.register[3] - sendServerChatMessage(s, s.server.dict["commandRaviStartSuccess"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.start.success) s.notifyRavi() } else { - sendServerChatMessage(s, s.server.dict["commandRaviStartError"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.start.error) } case "cm", "check", "checkmultiplier", "multiplier": - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandRaviMultiplier"], s.server.GetRaviMultiplier())) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ravi.multiplier, s.server.GetRaviMultiplier())) case "sr", "sendres", "resurrection", "ss", "sendsed", "rs", "reqsed": if s.server.erupeConfig.RealClientMode == _config.ZZ { switch args[1] { case "sr", "sendres", "resurrection": if s.server.raviente.state[28] > 0 { - sendServerChatMessage(s, s.server.dict["commandRaviResSuccess"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.res.success) s.server.raviente.state[28] = 0 } else { - sendServerChatMessage(s, s.server.dict["commandRaviResError"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.res.error) } case "ss", "sendsed": - sendServerChatMessage(s, s.server.dict["commandRaviSedSuccess"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.sed.success) // Total BerRavi HP HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4] s.server.raviente.support[1] = HP case "rs", "reqsed": - sendServerChatMessage(s, s.server.dict["commandRaviRequest"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.request) // Total BerRavi HP HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4] s.server.raviente.support[1] = HP + 1 } } else { - sendServerChatMessage(s, s.server.dict["commandRaviVersion"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.version) } default: - sendServerChatMessage(s, s.server.dict["commandRaviError"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.error) } } else { - sendServerChatMessage(s, s.server.dict["commandRaviNoPlayers"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.noPlayers) } } else { - sendServerChatMessage(s, s.server.dict["commandRaviError"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.error) } } else { sendDisabledCommandMessage(s, commands["Raviente"]) } case commands["Teleport"].Prefix: - if commands["Teleport"].Enabled { + if commands["Teleport"].Enabled || s.isOp() { if len(args) > 2 { x, _ := strconv.ParseInt(args[1], 10, 16) y, _ := strconv.ParseInt(args[2], 10, 16) @@ -315,17 +386,31 @@ func parseChatCommand(s *Session, command string) { MessageType: BinaryMessageTypeState, RawDataPayload: payloadBytes, }) - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandTeleportSuccess"], x, y)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.success, x, y)) } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandTeleportError"], commands["Teleport"].Prefix)) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.error, commands["Teleport"].Prefix)) } } else { sendDisabledCommandMessage(s, commands["Teleport"]) } + case commands["Discord"].Prefix: + if commands["Discord"].Enabled || s.isOp() { + var _token string + err := s.server.db.QueryRow(`SELECT discord_token FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&_token) + if err != nil { + randToken := make([]byte, 4) + rand.Read(randToken) + _token = fmt.Sprintf("%x-%x", randToken[:2], randToken[2:]) + s.server.db.Exec(`UPDATE users u SET discord_token = $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, _token, s.charID) + } + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.discord.success, _token)) + } else { + sendDisabledCommandMessage(s, commands["Discord"]) + } case commands["Help"].Prefix: - if commands["Help"].Enabled { + if commands["Help"].Enabled || s.isOp() { for _, command := range commands { - if command.Enabled { + if command.Enabled || s.isOp() { sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", s.server.erupeConfig.CommandPrefix, command.Prefix, command.Description)) } } @@ -341,14 +426,18 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 { if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 { - _ = tmp.ReadBytes(9) - tmp.SetLE() - frame := tmp.ReadUint32() - sendServerChatMessage(s, fmt.Sprintf("TIME : %d'%d.%03d (%dframe)", frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame)) + var timer bool + s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&timer) + if timer { + _ = tmp.ReadBytes(9) + tmp.SetLE() + frame := tmp.ReadUint32() + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.timer, frame/30/60/60, frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame)) + } } } - if s.server.erupeConfig.DevModeOptions.QuestDebugTools == true && s.server.erupeConfig.DevMode { + if s.server.erupeConfig.DebugOptions.QuestTools { if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 { // This is only correct most of the time tmp.ReadBytes(20) @@ -362,24 +451,20 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { // Parse out the real casted binary payload var msgBinTargeted *binpacket.MsgBinTargeted - var authorLen, msgLen uint16 - var msg []byte - - isDiceCommand := false + var message, author string + var returnToSender bool if pkt.MessageType == BinaryMessageTypeChat { tmp.SetLE() - tmp.Seek(int64(0), 0) - _ = tmp.ReadUint32() - authorLen = tmp.ReadUint16() - msgLen = tmp.ReadUint16() - msg = tmp.ReadNullTerminatedBytes() + tmp.Seek(8, 0) + message = string(tmp.ReadNullTerminatedBytes()) + author = string(tmp.ReadNullTerminatedBytes()) } // Customise payload realPayload := pkt.RawDataPayload if pkt.BroadcastType == BroadcastTypeTargeted { tmp.SetBE() - tmp.Seek(int64(0), 0) + tmp.Seek(0, 0) msgBinTargeted = &binpacket.MsgBinTargeted{} err := msgBinTargeted.Parse(tmp) if err != nil { @@ -388,18 +473,18 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } realPayload = msgBinTargeted.RawDataPayload } else if pkt.MessageType == BinaryMessageTypeChat { - if msgLen == 6 && string(msg) == "@dice" { - isDiceCommand = true - roll := byteframe.NewByteFrame() - roll.WriteInt16(1) // Unk - roll.SetLE() - roll.WriteUint16(4) // Unk - roll.WriteUint16(authorLen) - dice := fmt.Sprintf("%d", token.RNG().Intn(100)+1) - roll.WriteUint16(uint16(len(dice) + 1)) - roll.WriteNullTerminatedBytes([]byte(dice)) - roll.WriteNullTerminatedBytes(tmp.ReadNullTerminatedBytes()) - realPayload = roll.Data() + if message == "@dice" { + returnToSender = true + m := binpacket.MsgBinChat{ + Type: BinaryMessageTypeChat, + Flags: 4, + Message: fmt.Sprintf(`%d`, token.RNG().Intn(100)+1), + SenderName: author, + } + bf := byteframe.NewByteFrame() + bf.SetLE() + m.Build(bf) + realPayload = bf.Data() } else { bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) bf.SetLE() @@ -428,8 +513,8 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { case BroadcastTypeWorld: s.server.WorldcastMHF(resp, s, nil) case BroadcastTypeStage: - if isDiceCommand { - s.stage.BroadcastMHF(resp, nil) // send dice result back to caller + if returnToSender { + s.stage.BroadcastMHF(resp, nil) } else { s.stage.BroadcastMHF(resp, s) } diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 9d90cc898..310fa0221 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -97,6 +97,17 @@ func getPointers() map[SavePointer]int { pointers[pGalleryData] = 72064 pointers[pGardenData] = 74424 pointers[pRP] = 74614 + case _config.S6: + pointers[pWeaponID] = 12522 + pointers[pWeaponType] = 12789 + pointers[pHouseTier] = 13900 + pointers[pToreData] = 14228 + pointers[pHRP] = 14550 + pointers[pHouseData] = 14561 + pointers[pBookshelfData] = 9118 // Probably same here + pointers[pGalleryData] = 24064 + pointers[pGardenData] = 26424 + pointers[pRP] = 26614 } if _config.ErupeConfig.RealClientMode == _config.G5 { pointers[lBookshelfData] = 5548 @@ -212,7 +223,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.Gender = false } if !save.IsNewCharacter { - if _config.ErupeConfig.RealClientMode >= _config.F4 { + if _config.ErupeConfig.RealClientMode >= _config.S6 { save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2]) save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5] save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195] diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 805fa59f5..fd41e1366 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -1,6 +1,7 @@ package channelserver import ( + "erupe-ce/common/mhfmon" "erupe-ce/common/stringsupport" _config "erupe-ce/config" "fmt" @@ -45,7 +46,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - if s.server.erupeConfig.DevModeOptions.SaveDumps.RawEnabled { + if s.server.erupeConfig.SaveDumps.RawEnabled { dumpSaveData(s, saveData, "raw-savedata") } s.logger.Info("Updating save with blob") @@ -112,11 +113,11 @@ func grpToGR(n int) uint16 { } func dumpSaveData(s *Session, data []byte, suffix string) { - if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled { + if !s.server.erupeConfig.SaveDumps.Enabled { return } else { - dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID)) - path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix)) + dir := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID)) + path := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix)) _, err := os.Stat(dir) if err != nil { if os.IsNotExist(err) { @@ -1042,34 +1043,34 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) { {1105, 1, 10, 500, 0, 0, 0}, {1105, 2, 10, 500, 0, 0, 0}, // setServerBoss - {2001, 1, 17, 58, 0, 6, 700}, - {2001, 1, 20, 58, 0, 3, 200}, - {2001, 1, 22, 58, 0, 7, 250}, - {2001, 1, 27, 58, 0, 1, 100}, - {2001, 1, 53, 58, 0, 8, 1000}, - {2001, 1, 67, 58, 0, 9, 500}, - {2001, 1, 68, 58, 0, 2, 150}, - {2001, 1, 74, 58, 0, 4, 200}, - {2001, 1, 75, 58, 0, 5, 500}, - {2001, 1, 76, 58, 0, 10, 800}, - {2001, 1, 80, 58, 0, 11, 900}, - {2001, 1, 89, 58, 0, 12, 600}, - {2001, 2, 17, 60, 0, 6, 700}, - {2001, 2, 20, 60, 0, 3, 200}, - {2001, 2, 22, 60, 0, 7, 350}, - {2001, 2, 27, 60, 0, 1, 100}, - {2001, 2, 39, 60, 0, 13, 200}, - {2001, 2, 40, 60, 0, 15, 600}, - {2001, 2, 53, 60, 0, 8, 1000}, - {2001, 2, 67, 60, 0, 2, 500}, - {2001, 2, 68, 60, 0, 9, 150}, - {2001, 2, 74, 60, 0, 4, 200}, - {2001, 2, 75, 60, 0, 5, 500}, - {2001, 2, 76, 60, 0, 10, 800}, - {2001, 2, 80, 60, 0, 11, 900}, - {2001, 2, 81, 60, 0, 14, 900}, - {2001, 2, 89, 60, 0, 12, 600}, - {2001, 2, 94, 60, 0, 16, 1000}, + {2001, 1, mhfmon.Gravios, 58, 0, 6, 700}, + {2001, 1, mhfmon.Gypceros, 58, 0, 3, 200}, + {2001, 1, mhfmon.Basarios, 58, 0, 7, 250}, + {2001, 1, mhfmon.Velocidrome, 58, 0, 1, 100}, + {2001, 1, mhfmon.Rajang, 58, 0, 8, 1000}, + {2001, 1, mhfmon.ShogunCeanataur, 58, 0, 9, 500}, + {2001, 1, mhfmon.Bulldrome, 58, 0, 2, 150}, + {2001, 1, mhfmon.Hypnocatrice, 58, 0, 4, 200}, + {2001, 1, mhfmon.Lavasioth, 58, 0, 5, 500}, + {2001, 1, mhfmon.Tigrex, 58, 0, 10, 800}, + {2001, 1, mhfmon.Espinas, 58, 0, 11, 900}, + {2001, 1, mhfmon.Pariapuria, 58, 0, 12, 600}, + {2001, 2, mhfmon.Gravios, 60, 0, 6, 700}, + {2001, 2, mhfmon.Gypceros, 60, 0, 3, 200}, + {2001, 2, mhfmon.Basarios, 60, 0, 7, 350}, + {2001, 2, mhfmon.Velocidrome, 60, 0, 1, 100}, + {2001, 2, mhfmon.PurpleGypceros, 60, 0, 13, 200}, + {2001, 2, mhfmon.YianGaruga, 60, 0, 15, 600}, + {2001, 2, mhfmon.Rajang, 60, 0, 8, 1000}, + {2001, 2, mhfmon.ShogunCeanataur, 60, 0, 2, 500}, + {2001, 2, mhfmon.Bulldrome, 60, 0, 9, 150}, + {2001, 2, mhfmon.Hypnocatrice, 60, 0, 4, 200}, + {2001, 2, mhfmon.Lavasioth, 60, 0, 5, 500}, + {2001, 2, mhfmon.Tigrex, 60, 0, 10, 800}, + {2001, 2, mhfmon.Espinas, 60, 0, 11, 900}, + {2001, 2, mhfmon.BurningEspinas, 60, 0, 14, 900}, + {2001, 2, mhfmon.Pariapuria, 60, 0, 12, 600}, + {2001, 2, mhfmon.Dyuragaua, 60, 0, 16, 1000}, } case 6: paperData = []PaperData{ diff --git a/server/channelserver/handlers_discord.go b/server/channelserver/handlers_discord.go index 1f3398d18..3144b5e7b 100644 --- a/server/channelserver/handlers_discord.go +++ b/server/channelserver/handlers_discord.go @@ -3,6 +3,7 @@ package channelserver import ( "fmt" "github.com/bwmarrin/discordgo" + "golang.org/x/crypto/bcrypt" "sort" "strings" "unicode" @@ -66,10 +67,56 @@ func getCharacterList(s *Server) string { return message } +// onInteraction handles slash commands +func (s *Server) onInteraction(ds *discordgo.Session, i *discordgo.InteractionCreate) { + switch i.Interaction.ApplicationCommandData().Name { + case "link": + var temp string + err := s.db.QueryRow(`UPDATE users SET discord_id = $1 WHERE discord_token = $2 RETURNING discord_id`, i.Member.User.ID, i.ApplicationCommandData().Options[0].StringValue()).Scan(&temp) + if err == nil { + ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Your Erupe account was linked successfully.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + } else { + ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Failed to link Erupe account.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + } + case "password": + password, _ := bcrypt.GenerateFromPassword([]byte(i.ApplicationCommandData().Options[0].StringValue()), 10) + _, err := s.db.Exec(`UPDATE users SET password = $1 WHERE discord_id = $2`, password, i.Member.User.ID) + if err == nil { + ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Your Erupe account password has been updated.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + } else { + ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Failed to update Erupe account password.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + } + } +} + // onDiscordMessage handles receiving messages from discord and forwarding them ingame. func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) { - // Ignore messages from our bot, or ones that are not in the correct channel. - if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID { + // Ignore messages from bots, or messages that are not in the correct channel. + if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RelayChannel.RelayChannelID { return } @@ -79,11 +126,24 @@ func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCre } return r }, m.Author.Username)) - for i := 0; i < 8-len(m.Author.Username); i++ { paddedName += " " } + message := s.discordBot.NormalizeDiscordMessage(fmt.Sprintf("[D] %s > %s", paddedName, m.Content)) + if len(message) > s.erupeConfig.Discord.RelayChannel.MaxMessageLength { + return + } - message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content) - s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message)) + var messages []string + lineLength := 61 + for i := 0; i < len(message); i += lineLength { + end := i + lineLength + if end > len(message) { + end = len(message) + } + messages = append(messages, message[i:end]) + } + for i := range messages { + s.BroadcastChatMessage(messages[i]) + } } diff --git a/server/channelserver/handlers_diva.go b/server/channelserver/handlers_diva.go index 42b70f064..7f5b33992 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -70,8 +70,8 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { } var timestamps []uint32 - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 { - if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 { + if s.server.erupeConfig.DebugOptions.DivaOverride >= 0 { + if s.server.erupeConfig.DebugOptions.DivaOverride == 0 { if s.server.erupeConfig.RealClientMode >= _config.Z2 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36)) } else { @@ -79,7 +79,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { } return } - timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true) + timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.DivaOverride), true) } else { timestamps = generateDivaTimestamps(s, start, false) } diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 0ea67fee2..11a0670ef 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -36,7 +36,7 @@ func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRanking) bf := byteframe.NewByteFrame() - state := s.server.erupeConfig.DevModeOptions.TournamentEvent + state := s.server.erupeConfig.DebugOptions.TournamentOverride // Unk // Unk // Start? @@ -95,8 +95,9 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { func cleanupFesta(s *Session) { s.server.db.Exec("DELETE FROM events WHERE event_type='festa'") s.server.db.Exec("DELETE FROM festa_registrations") + s.server.db.Exec("DELETE FROM festa_submissions") s.server.db.Exec("DELETE FROM festa_prizes_accepted") - s.server.db.Exec("UPDATE guild_characters SET souls=0, trial_vote=NULL") + s.server.db.Exec("UPDATE guild_characters SET trial_vote=NULL") } func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { @@ -141,13 +142,13 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { } type FestaTrial struct { - ID uint32 `db:"id"` - Objective uint16 `db:"objective"` - GoalID uint32 `db:"goal_id"` - TimesReq uint16 `db:"times_req"` - Locale uint16 `db:"locale_req"` - Reward uint16 `db:"reward"` - Monopoly FestivalColour `db:"monopoly"` + ID uint32 `db:"id"` + Objective uint16 `db:"objective"` + GoalID uint32 `db:"goal_id"` + TimesReq uint16 `db:"times_req"` + Locale uint16 `db:"locale_req"` + Reward uint16 `db:"reward"` + Monopoly FestivalColor `db:"monopoly"` Unk uint16 } @@ -173,12 +174,12 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { } var timestamps []uint32 - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.FestaEvent >= 0 { - if s.server.erupeConfig.DevModeOptions.FestaEvent == 0 { + if s.server.erupeConfig.DebugOptions.FestaOverride >= 0 { + if s.server.erupeConfig.DebugOptions.FestaOverride == 0 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.FestaEvent), true) + timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.FestaOverride), true) } else { timestamps = generateFestaTimestamps(s, start, false) } @@ -233,8 +234,10 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(trial.TimesReq) bf.WriteUint16(trial.Locale) bf.WriteUint16(trial.Reward) - bf.WriteInt16(int16(FestivalColourCodes[trial.Monopoly])) - bf.WriteUint16(trial.Unk) + bf.WriteInt16(FestivalColorCodes[trial.Monopoly]) + if _config.ErupeConfig.RealClientMode >= _config.F4 { // Not in S6.0 + bf.WriteUint16(trial.Unk) + } } // The Winner and Loser Armor IDs are missing @@ -291,26 +294,49 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint16(500) - categoryWinners := uint16(0) // NYI - bf.WriteUint16(categoryWinners) - for i := uint16(0); i < categoryWinners; i++ { - bf.WriteUint32(0) // Guild ID - bf.WriteUint16(i + 1) // Category ID - bf.WriteUint16(0) // Festa Team - ps.Uint8(bf, "", true) // Guild Name - } - - dailyWinners := uint16(0) // NYI - bf.WriteUint16(dailyWinners) - for i := uint16(0); i < dailyWinners; i++ { - bf.WriteUint32(0) // Guild ID - bf.WriteUint16(i + 1) // Category ID - bf.WriteUint16(0) // Festa Team - ps.Uint8(bf, "", true) // Guild Name + var temp uint32 + bf.WriteUint16(4) + for i := uint16(0); i < 4; i++ { + var guildID uint32 + var guildName string + var guildTeam = FestivalColorNone + s.server.db.QueryRow(` + SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _ + FROM festa_submissions fs + LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id + LEFT JOIN guilds g ON fs.guild_id = g.id + WHERE fs.trial_type = $1 + GROUP BY fs.guild_id, g.name, fr.team + ORDER BY _ DESC LIMIT 1 + `, i+1).Scan(&guildID, &guildName, &guildTeam, &temp) + bf.WriteUint32(guildID) + bf.WriteUint16(i + 1) + bf.WriteInt16(FestivalColorCodes[guildTeam]) + ps.Uint8(bf, guildName, true) + } + bf.WriteUint16(7) + for i := uint16(0); i < 7; i++ { + var guildID uint32 + var guildName string + var guildTeam = FestivalColorNone + offset := 86400 * uint32(i) + s.server.db.QueryRow(` + SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _ + FROM festa_submissions fs + LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id + LEFT JOIN guilds g ON fs.guild_id = g.id + WHERE EXTRACT(EPOCH FROM fs.timestamp)::int > $1 AND EXTRACT(EPOCH FROM fs.timestamp)::int < $2 + GROUP BY fs.guild_id, g.name, fr.team + ORDER BY _ DESC LIMIT 1 + `, timestamps[1]+offset, timestamps[1]+offset+86400).Scan(&guildID, &guildName, &guildTeam, &temp) + bf.WriteUint32(guildID) + bf.WriteUint16(i + 1) + bf.WriteInt16(FestivalColorCodes[guildTeam]) + ps.Uint8(bf, guildName, true) } + bf.WriteUint32(0) // Clan goal // Final bonus rates - bf.WriteUint32(1) // 5000-Infinity? bf.WriteUint32(5000) // 5000+ souls bf.WriteUint32(2000) // 2000-4999 souls bf.WriteUint32(1000) // 1000-1999 souls @@ -321,7 +347,9 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(100) // Normal rate bf.WriteUint16(50) // 50% penalty - ps.Uint16(bf, "", false) + if _config.ErupeConfig.RealClientMode >= _config.G52 { + ps.Uint16(bf, "", false) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -349,7 +377,6 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(false) bf.WriteBool(true) } - bf.WriteUint16(0) // Unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -364,18 +391,18 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { resp := byteframe.NewByteFrame() if err != nil || guild == nil || applicant { resp.WriteUint32(0) - resp.WriteUint32(0) - resp.WriteUint32(0xFFFFFFFF) - resp.WriteUint32(0) - resp.WriteUint32(0) + resp.WriteInt32(0) + resp.WriteInt32(-1) + resp.WriteInt32(0) + resp.WriteInt32(0) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) return } resp.WriteUint32(guild.Souls) - resp.WriteUint32(1) // unk - resp.WriteUint32(1) // unk - resp.WriteUint32(1) // unk, rank? - resp.WriteUint32(1) // unk + resp.WriteInt32(1) // unk + resp.WriteInt32(1) // unk, rank? + resp.WriteInt32(1) // unk + resp.WriteInt32(1) // unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } @@ -391,15 +418,26 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(len(members))) - bf.WriteUint16(0) // Unk sort.Slice(members, func(i, j int) bool { return members[i].Souls > members[j].Souls }) + var validMembers []*GuildMember for _, member := range members { + if member.Souls > 0 { + validMembers = append(validMembers, member) + } + } + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(len(validMembers))) + bf.WriteUint16(0) // Unk + for _, member := range validMembers { bf.WriteUint32(member.CharID) - bf.WriteUint32(member.Souls) + if _config.ErupeConfig.RealClientMode <= _config.Z1 { + bf.WriteUint16(uint16(member.Souls)) + bf.WriteUint16(0) + } else { + bf.WriteUint32(member.Souls) + } } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -431,7 +469,14 @@ func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfChargeFesta) - s.server.db.Exec("UPDATE guild_characters SET souls=souls+$1 WHERE character_id=$2", pkt.Souls, s.charID) + tx, _ := s.server.db.Begin() + for i := range pkt.Souls { + if pkt.Souls[i] == 0 { + continue + } + _, _ = tx.Exec(`INSERT INTO festa_submissions VALUES ($1, $2, $3, $4, now())`, s.charID, pkt.GuildID, i, pkt.Souls[i]) + } + _ = tx.Commit() doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index ad5221281..7fc90cdf1 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -21,18 +21,18 @@ import ( "go.uber.org/zap" ) -type FestivalColour string +type FestivalColor string const ( - FestivalColourNone FestivalColour = "none" - FestivalColourBlue FestivalColour = "blue" - FestivalColourRed FestivalColour = "red" + FestivalColorNone FestivalColor = "none" + FestivalColorBlue FestivalColor = "blue" + FestivalColorRed FestivalColor = "red" ) -var FestivalColourCodes = map[FestivalColour]int8{ - FestivalColourNone: -1, - FestivalColourBlue: 0, - FestivalColourRed: 1, +var FestivalColorCodes = map[FestivalColor]int16{ + FestivalColorNone: -1, + FestivalColorBlue: 0, + FestivalColorRed: 1, } type GuildApplicationType string @@ -43,27 +43,27 @@ const ( ) type Guild struct { - ID uint32 `db:"id"` - Name string `db:"name"` - MainMotto uint8 `db:"main_motto"` - SubMotto uint8 `db:"sub_motto"` - CreatedAt time.Time `db:"created_at"` - MemberCount uint16 `db:"member_count"` - RankRP uint32 `db:"rank_rp"` - EventRP uint32 `db:"event_rp"` - Comment string `db:"comment"` - PugiName1 string `db:"pugi_name_1"` - PugiName2 string `db:"pugi_name_2"` - PugiName3 string `db:"pugi_name_3"` - PugiOutfit1 uint8 `db:"pugi_outfit_1"` - PugiOutfit2 uint8 `db:"pugi_outfit_2"` - PugiOutfit3 uint8 `db:"pugi_outfit_3"` - PugiOutfits uint32 `db:"pugi_outfits"` - Recruiting bool `db:"recruiting"` - FestivalColour FestivalColour `db:"festival_colour"` - Souls uint32 `db:"souls"` - AllianceID uint32 `db:"alliance_id"` - Icon *GuildIcon `db:"icon"` + ID uint32 `db:"id"` + Name string `db:"name"` + MainMotto uint8 `db:"main_motto"` + SubMotto uint8 `db:"sub_motto"` + CreatedAt time.Time `db:"created_at"` + MemberCount uint16 `db:"member_count"` + RankRP uint32 `db:"rank_rp"` + EventRP uint32 `db:"event_rp"` + Comment string `db:"comment"` + PugiName1 string `db:"pugi_name_1"` + PugiName2 string `db:"pugi_name_2"` + PugiName3 string `db:"pugi_name_3"` + PugiOutfit1 uint8 `db:"pugi_outfit_1"` + PugiOutfit2 uint8 `db:"pugi_outfit_2"` + PugiOutfit3 uint8 `db:"pugi_outfit_3"` + PugiOutfits uint32 `db:"pugi_outfits"` + Recruiting bool `db:"recruiting"` + FestivalColor FestivalColor `db:"festival_colour"` + Souls uint32 `db:"souls"` + AllianceID uint32 `db:"alliance_id"` + Icon *GuildIcon `db:"icon"` GuildLeader } @@ -967,7 +967,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(uint8(len(guildLeaderName))) bf.WriteBytes(guildName) bf.WriteBytes(guildComment) - bf.WriteInt8(FestivalColourCodes[guild.FestivalColour]) + bf.WriteInt8(int8(FestivalColorCodes[guild.FestivalColor])) bf.WriteUint32(guild.RankRP) bf.WriteBytes(guildLeaderName) bf.WriteUint32(0) // Unk @@ -1427,7 +1427,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() - bf.WriteUint16(guild.MemberCount) + bf.WriteUint16(uint16(len(guildMembers))) sort.Slice(guildMembers[:], func(i, j int) bool { return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex @@ -1460,7 +1460,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { } if guild.AllianceID > 0 { - bf.WriteUint16(alliance.TotalMembers - guild.MemberCount) + bf.WriteUint16(alliance.TotalMembers - uint16(len(guildMembers))) if guild.ID != alliance.ParentGuildID { mems, err := GetGuildMembers(s, alliance.ParentGuildID, false) if err != nil { diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index e053e2498..ac64e892a 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -61,41 +61,35 @@ func (gm *GuildMember) Save(s *Session) error { } const guildMembersSelectSQL = ` -SELECT - g.id as guild_id, - joined_at, - coalesce(souls, 0) as souls, - COALESCE(rp_today, 0) AS rp_today, - COALESCE(rp_yesterday, 0) AS rp_yesterday, - c.name, - character.character_id, - coalesce(gc.order_index, 0) as order_index, - c.last_login, - coalesce(gc.recruiter, false) as recruiter, - coalesce(gc.avoid_leadership, false) as avoid_leadership, - c.hrp, - c.gr, - c.weapon_id, - c.weapon_type, - character.is_applicant, - CASE WHEN g.leader_id = c.id THEN 1 ELSE 0 END as is_leader - FROM ( - SELECT character_id, true as is_applicant, guild_id - FROM guild_applications ga - WHERE ga.application_type = 'applied' - UNION - SELECT character_id, false as is_applicant, guild_id +SELECT * FROM ( + SELECT + g.id AS guild_id, + joined_at, + COALESCE((SELECT SUM(souls) FROM festa_submissions fs WHERE fs.character_id=c.id), 0) AS souls, + COALESCE(rp_today, 0) AS rp_today, + COALESCE(rp_yesterday, 0) AS rp_yesterday, + c.name, + c.id AS character_id, + COALESCE(order_index, 0) AS order_index, + c.last_login, + COALESCE(recruiter, false) AS recruiter, + COALESCE(avoid_leadership, false) AS avoid_leadership, + c.hrp, + c.gr, + c.weapon_id, + c.weapon_type, + EXISTS(SELECT 1 FROM guild_applications ga WHERE ga.character_id=c.id AND application_type='applied') AS is_applicant, + CASE WHEN g.leader_id = c.id THEN true ELSE false END AS is_leader FROM guild_characters gc - ) character - JOIN characters c on character.character_id = c.id - LEFT JOIN guild_characters gc ON gc.character_id = character.character_id - JOIN guilds g ON g.id = character.guild_id + LEFT JOIN characters c ON c.id = gc.character_id + LEFT JOIN guilds g ON g.id = gc.guild_id +) AS subquery ` func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s - WHERE character.guild_id = $1 AND is_applicant = $2 + WHERE guild_id = $1 AND is_applicant = $2 `, guildMembersSelectSQL), guildID, applicants) if err != nil { @@ -121,7 +115,7 @@ func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMembe } func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character.character_id=$1", guildMembersSelectSQL), charID) + rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID) if err != nil { s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID)) diff --git a/server/channelserver/handlers_guild_scout.go b/server/channelserver/handlers_guild_scout.go index fceeb037d..3c73b5bec 100644 --- a/server/channelserver/handlers_guild_scout.go +++ b/server/channelserver/handlers_guild_scout.go @@ -60,9 +60,9 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) { mail := &Mail{ SenderID: s.charID, RecipientID: pkt.CharID, - Subject: s.server.dict["guildInviteName"], + Subject: s.server.i18n.guild.invite.title, Body: fmt.Sprintf( - s.server.dict["guildInvite"], + s.server.i18n.guild.invite.body, guildInfo.Name, ), IsGuildInvite: true, @@ -146,30 +146,30 @@ func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) { err = guild.AcceptApplication(s, s.charID) mail = append(mail, Mail{ RecipientID: s.charID, - Subject: s.server.dict["guildInviteSuccessName"], - Body: fmt.Sprintf(s.server.dict["guildInviteSuccess"], guild.Name), + Subject: s.server.i18n.guild.invite.success.title, + Body: fmt.Sprintf(s.server.i18n.guild.invite.success.body, guild.Name), IsSystemMessage: true, }) mail = append(mail, Mail{ SenderID: s.charID, RecipientID: pkt.LeaderID, - Subject: s.server.dict["guildInviteAcceptedName"], - Body: fmt.Sprintf(s.server.dict["guildInviteAccepted"], guild.Name), + Subject: s.server.i18n.guild.invite.accepted.title, + Body: fmt.Sprintf(s.server.i18n.guild.invite.accepted.body, guild.Name), IsSystemMessage: true, }) } else { err = guild.RejectApplication(s, s.charID) mail = append(mail, Mail{ RecipientID: s.charID, - Subject: s.server.dict["guildInviteRejectName"], - Body: fmt.Sprintf(s.server.dict["guildInviteReject"], guild.Name), + Subject: s.server.i18n.guild.invite.rejected.title, + Body: fmt.Sprintf(s.server.i18n.guild.invite.rejected.body, guild.Name), IsSystemMessage: true, }) mail = append(mail, Mail{ SenderID: s.charID, RecipientID: pkt.LeaderID, - Subject: s.server.dict["guildInviteDeclined"], - Body: fmt.Sprintf(s.server.dict["guildInviteDeclined"], guild.Name), + Subject: s.server.i18n.guild.invite.declined.title, + Body: fmt.Sprintf(s.server.i18n.guild.invite.declined.body, guild.Name), IsSystemMessage: true, }) } diff --git a/server/channelserver/handlers_object.go b/server/channelserver/handlers_object.go index 241505c0d..41f28e5d3 100644 --- a/server/channelserver/handlers_object.go +++ b/server/channelserver/handlers_object.go @@ -42,7 +42,7 @@ func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysPositionObject) - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages { + if s.server.erupeConfig.DebugOptions.LogInboundMessages { fmt.Printf("[%s] with objectID [%d] move to (%f,%f,%f)\n\n", s.Name, pkt.ObjID, pkt.X, pkt.Y, pkt.Z) } s.stage.Lock() diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index 98226c777..753e59040 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -2,6 +2,7 @@ package channelserver import ( "database/sql" + "encoding/binary" "erupe-ce/common/byteframe" "erupe-ce/common/decryption" ps "erupe-ce/common/pascalstring" @@ -16,11 +17,82 @@ import ( "go.uber.org/zap" ) +type tuneValue struct { + ID uint16 + Value uint16 +} + +func findSubSliceIndices(data []byte, sub []byte) []int { + var indices []int + lenSub := len(sub) + for i := 0; i < len(data); i++ { + if i+lenSub > len(data) { + break + } + if equal(data[i:i+lenSub], sub) { + indices = append(indices, i) + } + } + return indices +} + +func equal(a, b []byte) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +} + +func BackportQuest(data []byte) []byte { + wp := binary.LittleEndian.Uint32(data[0:4]) + 96 + rp := wp + 4 + for i := uint32(0); i < 6; i++ { + if i != 0 { + wp += 4 + rp += 8 + } + copy(data[wp:wp+4], data[rp:rp+4]) + } + + fillLength := uint32(108) + if _config.ErupeConfig.RealClientMode <= _config.S6 { + fillLength = 44 + } else if _config.ErupeConfig.RealClientMode <= _config.F5 { + fillLength = 52 + } else if _config.ErupeConfig.RealClientMode <= _config.G101 { + fillLength = 76 + } + + copy(data[wp:wp+fillLength], data[rp:rp+fillLength]) + if _config.ErupeConfig.RealClientMode <= _config.G91 { + patterns := [][]byte{ + {0x0A, 0x00, 0x01, 0x33, 0xD7, 0x00}, // 10% Armor Sphere -> Stone + {0x06, 0x00, 0x02, 0x33, 0xD8, 0x00}, // 6% Armor Sphere+ -> Iron Ore + {0x0A, 0x00, 0x03, 0x33, 0xD7, 0x00}, // 10% Adv Armor Sphere -> Stone + {0x06, 0x00, 0x04, 0x33, 0xDB, 0x00}, // 6% Hard Armor Sphere -> Dragonite Ore + {0x0A, 0x00, 0x05, 0x33, 0xD9, 0x00}, // 10% Heaven Armor Sphere -> Earth Crystal + {0x06, 0x00, 0x06, 0x33, 0xDB, 0x00}, // 6% True Armor Sphere -> Dragonite Ore + } + for i := range patterns { + j := findSubSliceIndices(data, patterns[i][0:4]) + for k := range j { + copy(data[j[k]+2:j[k]+4], patterns[i][4:6]) + } + } + } + return data +} + func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetFile) if pkt.IsScenario { - if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode { + if s.server.erupeConfig.DebugOptions.QuestTools { s.logger.Debug( "Scenario", zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID), @@ -40,7 +112,7 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { } doAckBufSucceed(s, pkt.AckHandle, data) } else { - if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode { + if s.server.erupeConfig.DebugOptions.QuestTools { s.logger.Debug( "Quest", zap.String("Filename", pkt.Filename), @@ -58,6 +130,9 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, data) return } + if _config.ErupeConfig.RealClientMode <= _config.Z1 && s.server.erupeConfig.DebugOptions.AutoQuestBackport { + data = BackportQuest(decryption.UnpackSimple(data)) + } doAckBufSucceed(s, pkt.AckHandle, data) } } @@ -119,25 +194,39 @@ func loadQuestFile(s *Session, questId int) []byte { } decrypted := decryption.UnpackSimple(file) + if _config.ErupeConfig.RealClientMode <= _config.Z1 && s.server.erupeConfig.DebugOptions.AutoQuestBackport { + decrypted = BackportQuest(decrypted) + } fileBytes := byteframe.NewByteFrameFromBytes(decrypted) fileBytes.SetLE() fileBytes.Seek(int64(fileBytes.ReadUint32()), 0) - // The 320 bytes directly following the data pointer must go directly into the event's body, after the header and before the string pointers. - questBody := byteframe.NewByteFrameFromBytes(fileBytes.ReadBytes(320)) + bodyLength := 320 + if _config.ErupeConfig.RealClientMode <= _config.S6 { + bodyLength = 160 + } else if _config.ErupeConfig.RealClientMode <= _config.F5 { + bodyLength = 168 + } else if _config.ErupeConfig.RealClientMode <= _config.G101 { + bodyLength = 192 + } else if _config.ErupeConfig.RealClientMode <= _config.Z1 { + bodyLength = 224 + } + + // The n bytes directly following the data pointer must go directly into the event's body, after the header and before the string pointers. + questBody := byteframe.NewByteFrameFromBytes(fileBytes.ReadBytes(uint(bodyLength))) questBody.SetLE() // Find the master quest string pointer questBody.Seek(40, 0) fileBytes.Seek(int64(questBody.ReadUint32()), 0) questBody.Seek(40, 0) // Overwrite it - questBody.WriteUint32(320) + questBody.WriteUint32(uint32(bodyLength)) questBody.Seek(0, 2) // Rewrite the quest strings and their pointers var tempString []byte newStrings := byteframe.NewByteFrame() - tempPointer := 352 + tempPointer := bodyLength + 32 for i := 0; i < 8; i++ { questBody.WriteUint32(uint32(tempPointer)) temp := int64(fileBytes.Index()) @@ -163,7 +252,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) { data := loadQuestFile(s, questId) if data == nil { - return nil, fmt.Errorf("failed to load quest file") + return nil, fmt.Errorf(fmt.Sprintf("failed to load quest file (%d)", questId)) } bf := byteframe.NewByteFrame() @@ -249,6 +338,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { err = rows.Scan(&id, &maxPlayers, &questType, &questId, &mark, &flags, &startTime, &activeDays, &inactiveDays) if err != nil { + s.logger.Error("Failed to scan event quest row", zap.Error(err)) continue } @@ -277,15 +367,17 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { // Check if the quest is currently active if currentTime.Before(startTime) || currentTime.After(startTime.Add(time.Duration(activeDays)*24*time.Hour)) { - break + continue } } data, err := makeEventQuest(s, rows) if err != nil { + s.logger.Error("Failed to make event quest", zap.Error(err)) continue } else { if len(data) > 896 || len(data) < 352 { + s.logger.Error("Invalid quest data length", zap.Int("len", len(data))) continue } else { totalCount++ @@ -302,11 +394,6 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { tx.Commit() } - type tuneValue struct { - ID uint16 - Value uint16 - } - tuneValues := []tuneValue{ {ID: 20, Value: 1}, {ID: 26, Value: 1}, @@ -319,43 +406,46 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { {ID: 67, Value: 1}, {ID: 80, Value: 1}, {ID: 94, Value: 1}, - {ID: 1010, Value: 300}, - {ID: 1011, Value: 300}, - {ID: 1012, Value: 300}, - {ID: 1013, Value: 300}, - {ID: 1014, Value: 200}, - {ID: 1015, Value: 200}, - {ID: 1021, Value: 400}, - {ID: 1023, Value: 8}, - {ID: 1024, Value: 150}, - {ID: 1025, Value: 1}, - {ID: 1026, Value: 999}, // get_grank_cap - {ID: 1027, Value: 100}, - {ID: 1028, Value: 100}, - {ID: 1030, Value: 8}, - {ID: 1031, Value: 100}, - {ID: 1032, Value: 0}, // isValid_partner - {ID: 1044, Value: 200}, // get_rate_tload_time_out - {ID: 1045, Value: 0}, // get_rate_tower_treasure_preset - {ID: 1046, Value: 99}, - {ID: 1048, Value: 0}, // get_rate_tower_log_disable - {ID: 1049, Value: 10}, // get_rate_tower_gem_max - {ID: 1050, Value: 1}, // get_rate_tower_gem_set - {ID: 1051, Value: 200}, - {ID: 1052, Value: 200}, - {ID: 1063, Value: 50000}, - {ID: 1064, Value: 50000}, - {ID: 1065, Value: 25000}, - {ID: 1066, Value: 25000}, - {ID: 1067, Value: 90}, // get_lobby_member_upper_for_making_room Lv1? - {ID: 1068, Value: 80}, // get_lobby_member_upper_for_making_room Lv2? - {ID: 1069, Value: 70}, // get_lobby_member_upper_for_making_room Lv3? - {ID: 1072, Value: 300}, // get_rate_premium_ravi_tama - {ID: 1073, Value: 300}, // get_rate_premium_ravi_ax_tama - {ID: 1074, Value: 300}, // get_rate_premium_ravi_g_tama - {ID: 1078, Value: 0}, - {ID: 1079, Value: 1}, - {ID: 1080, Value: 1}, + {ID: 1001, Value: 100}, // get_hrp_rate + {ID: 1010, Value: 300}, // get_hrp_rate_netcafe + {ID: 1011, Value: 300}, // get_zeny_rate_netcafe + {ID: 1012, Value: 300}, // get_hrp_rate_ncource + {ID: 1013, Value: 300}, // get_zeny_rate_ncource + {ID: 1014, Value: 200}, // get_hrp_rate_premium + {ID: 1015, Value: 200}, // get_zeny_rate_premium + {ID: 1021, Value: 400}, // get_gcp_rate_assist + {ID: 1023, Value: 8}, // unused? + {ID: 1024, Value: 150}, // get_hrp_rate_ptbonus + {ID: 1025, Value: 1}, // isValid_stampcard + {ID: 1026, Value: 999}, // get_grank_cap + {ID: 1027, Value: 100}, // get_exchange_rate_festa + {ID: 1028, Value: 100}, // get_exchange_rate_cafe + {ID: 1030, Value: 8}, // get_gquest_cap + {ID: 1031, Value: 100}, // get_exchange_rate_guild (GCP) + {ID: 1032, Value: 0}, // isValid_partner + {ID: 1044, Value: 200}, // get_rate_tload_time_out + {ID: 1045, Value: 0}, // get_rate_tower_treasure_preset + {ID: 1046, Value: 99}, // get_hunter_life_cap + {ID: 1048, Value: 0}, // get_rate_tower_hint_sec + {ID: 1049, Value: 10}, // get_rate_tower_gem_max + {ID: 1050, Value: 1}, // get_rate_tower_gem_set + {ID: 1051, Value: 200}, // get_pallone_score_rate_premium + {ID: 1052, Value: 200}, // get_trp_rate_premium + {ID: 1063, Value: 50000}, // get_nboost_quest_point_from_hrank + {ID: 1064, Value: 50000}, // get_nboost_quest_point_from_srank + {ID: 1065, Value: 25000}, // get_nboost_quest_point_from_grank + {ID: 1066, Value: 25000}, // get_nboost_quest_point_from_gsrank + {ID: 1067, Value: 90}, // get_lobby_member_upper_for_making_room Lv1? + {ID: 1068, Value: 80}, // get_lobby_member_upper_for_making_room Lv2? + {ID: 1069, Value: 70}, // get_lobby_member_upper_for_making_room Lv3? + {ID: 1072, Value: 300}, // get_rate_premium_ravi_tama + {ID: 1073, Value: 300}, // get_rate_premium_ravi_ax_tama + {ID: 1074, Value: 300}, // get_rate_premium_ravi_g_tama + {ID: 1078, Value: 0}, // isCapped_tenrou_irai + {ID: 1079, Value: 1}, // get_add_tower_level_assist + {ID: 1080, Value: 1}, // get_tune_add_tower_level_w_assist_nboost + + // get_tune_secret_book_item {ID: 1081, Value: 1}, {ID: 1082, Value: 4}, {ID: 1083, Value: 2}, @@ -380,14 +470,17 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { {ID: 1102, Value: 5}, {ID: 1103, Value: 2}, {ID: 1104, Value: 10}, - {ID: 1145, Value: 200}, - {ID: 1146, Value: 0}, // isTower_invisible - {ID: 1147, Value: 0}, // isVenom_playable - {ID: 1149, Value: 20}, - {ID: 1152, Value: 1130}, - {ID: 1154, Value: 0}, // isDisabled_object_season - {ID: 1158, Value: 1}, - {ID: 1160, Value: 300}, + + {ID: 1145, Value: 200}, // get_ud_point_rate_premium + {ID: 1146, Value: 0}, // isTower_invisible + {ID: 1147, Value: 0}, // isVenom_playable + {ID: 1149, Value: 20}, // get_ud_break_parts_point + {ID: 1152, Value: 1130}, // unused? + {ID: 1154, Value: 0}, // isDisabled_object_season + {ID: 1158, Value: 1}, // isDelivery_venom_ult_quest + {ID: 1160, Value: 300}, // get_rate_premium_ravi_g_enhance_tama + + // unknown {ID: 1162, Value: 1}, {ID: 1163, Value: 3}, {ID: 1164, Value: 5}, @@ -407,240 +500,6 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { {ID: 1178, Value: 10}, {ID: 1179, Value: 2}, {ID: 1180, Value: 5}, - {ID: 3000, Value: 100}, - {ID: 3001, Value: 100}, - {ID: 3002, Value: 100}, - {ID: 3003, Value: 100}, - {ID: 3004, Value: 100}, - {ID: 3005, Value: 100}, - {ID: 3006, Value: 100}, - {ID: 3007, Value: 100}, - {ID: 3008, Value: 100}, - {ID: 3009, Value: 100}, - {ID: 3010, Value: 100}, - {ID: 3011, Value: 100}, - {ID: 3012, Value: 100}, - {ID: 3013, Value: 100}, - {ID: 3014, Value: 100}, - {ID: 3015, Value: 100}, - {ID: 3016, Value: 100}, - {ID: 3017, Value: 100}, - {ID: 3018, Value: 100}, - {ID: 3019, Value: 100}, - {ID: 3020, Value: 100}, - {ID: 3021, Value: 100}, - {ID: 3022, Value: 100}, - {ID: 3023, Value: 100}, - {ID: 3024, Value: 100}, - {ID: 3025, Value: 100}, - {ID: 3286, Value: 200}, - {ID: 3287, Value: 200}, - {ID: 3288, Value: 200}, - {ID: 3289, Value: 200}, - {ID: 3290, Value: 200}, - {ID: 3291, Value: 200}, - {ID: 3292, Value: 200}, - {ID: 3293, Value: 200}, - {ID: 3294, Value: 200}, - {ID: 3295, Value: 200}, - {ID: 3296, Value: 200}, - {ID: 3297, Value: 200}, - {ID: 3298, Value: 200}, - {ID: 3299, Value: 200}, - {ID: 3300, Value: 200}, - {ID: 3301, Value: 200}, - {ID: 3302, Value: 200}, - {ID: 3303, Value: 200}, - {ID: 3304, Value: 200}, - {ID: 3305, Value: 200}, - {ID: 3306, Value: 200}, - {ID: 3307, Value: 200}, - {ID: 3308, Value: 200}, - {ID: 3309, Value: 200}, - {ID: 3310, Value: 200}, - {ID: 3311, Value: 200}, - {ID: 3312, Value: 300}, - {ID: 3313, Value: 300}, - {ID: 3314, Value: 300}, - {ID: 3315, Value: 300}, - {ID: 3316, Value: 300}, - {ID: 3317, Value: 300}, - {ID: 3318, Value: 300}, - {ID: 3319, Value: 300}, - {ID: 3320, Value: 300}, - {ID: 3321, Value: 300}, - {ID: 3322, Value: 300}, - {ID: 3323, Value: 300}, - {ID: 3324, Value: 300}, - {ID: 3325, Value: 300}, - {ID: 3326, Value: 300}, - {ID: 3327, Value: 300}, - {ID: 3328, Value: 300}, - {ID: 3329, Value: 300}, - {ID: 3330, Value: 300}, - {ID: 3331, Value: 300}, - {ID: 3332, Value: 300}, - {ID: 3333, Value: 300}, - {ID: 3334, Value: 300}, - {ID: 3335, Value: 300}, - {ID: 3336, Value: 300}, - {ID: 3337, Value: 300}, - {ID: 3338, Value: 100}, - {ID: 3339, Value: 100}, - {ID: 3340, Value: 100}, - {ID: 3341, Value: 100}, - {ID: 3342, Value: 100}, - {ID: 3343, Value: 100}, - {ID: 3344, Value: 100}, - {ID: 3345, Value: 100}, - {ID: 3346, Value: 100}, - {ID: 3347, Value: 100}, - {ID: 3348, Value: 100}, - {ID: 3349, Value: 100}, - {ID: 3350, Value: 100}, - {ID: 3351, Value: 100}, - {ID: 3352, Value: 100}, - {ID: 3353, Value: 100}, - {ID: 3354, Value: 100}, - {ID: 3355, Value: 100}, - {ID: 3356, Value: 100}, - {ID: 3357, Value: 100}, - {ID: 3358, Value: 100}, - {ID: 3359, Value: 100}, - {ID: 3360, Value: 100}, - {ID: 3361, Value: 100}, - {ID: 3362, Value: 100}, - {ID: 3363, Value: 100}, - {ID: 3364, Value: 100}, - {ID: 3365, Value: 100}, - {ID: 3366, Value: 100}, - {ID: 3367, Value: 100}, - {ID: 3368, Value: 100}, - {ID: 3369, Value: 100}, - {ID: 3370, Value: 100}, - {ID: 3371, Value: 100}, - {ID: 3372, Value: 100}, - {ID: 3373, Value: 100}, - {ID: 3374, Value: 100}, - {ID: 3375, Value: 100}, - {ID: 3376, Value: 100}, - {ID: 3377, Value: 100}, - {ID: 3378, Value: 100}, - {ID: 3379, Value: 100}, - {ID: 3380, Value: 100}, - {ID: 3381, Value: 100}, - {ID: 3382, Value: 100}, - {ID: 3383, Value: 100}, - {ID: 3384, Value: 100}, - {ID: 3385, Value: 100}, - {ID: 3386, Value: 100}, - {ID: 3387, Value: 100}, - {ID: 3388, Value: 100}, - {ID: 3389, Value: 100}, - {ID: 3390, Value: 100}, - {ID: 3391, Value: 100}, - {ID: 3392, Value: 100}, - {ID: 3393, Value: 100}, - {ID: 3394, Value: 100}, - {ID: 3395, Value: 100}, - {ID: 3396, Value: 100}, - {ID: 3397, Value: 100}, - {ID: 3398, Value: 100}, - {ID: 3399, Value: 100}, - {ID: 3400, Value: 100}, - {ID: 3401, Value: 100}, - {ID: 3402, Value: 100}, - {ID: 3416, Value: 100}, - {ID: 3417, Value: 100}, - {ID: 3418, Value: 100}, - {ID: 3419, Value: 100}, - {ID: 3420, Value: 100}, - {ID: 3421, Value: 100}, - {ID: 3422, Value: 100}, - {ID: 3423, Value: 100}, - {ID: 3424, Value: 100}, - {ID: 3425, Value: 100}, - {ID: 3426, Value: 100}, - {ID: 3427, Value: 100}, - {ID: 3428, Value: 100}, - {ID: 3442, Value: 100}, - {ID: 3443, Value: 100}, - {ID: 3444, Value: 100}, - {ID: 3445, Value: 100}, - {ID: 3446, Value: 100}, - {ID: 3447, Value: 100}, - {ID: 3448, Value: 100}, - {ID: 3449, Value: 100}, - {ID: 3450, Value: 100}, - {ID: 3451, Value: 100}, - {ID: 3452, Value: 100}, - {ID: 3453, Value: 100}, - {ID: 3454, Value: 100}, - {ID: 3468, Value: 100}, - {ID: 3469, Value: 100}, - {ID: 3470, Value: 100}, - {ID: 3471, Value: 100}, - {ID: 3472, Value: 100}, - {ID: 3473, Value: 100}, - {ID: 3474, Value: 100}, - {ID: 3475, Value: 100}, - {ID: 3476, Value: 100}, - {ID: 3477, Value: 100}, - {ID: 3478, Value: 100}, - {ID: 3479, Value: 100}, - {ID: 3480, Value: 100}, - {ID: 3494, Value: 0}, - {ID: 3495, Value: 0}, - {ID: 3496, Value: 0}, - {ID: 3497, Value: 0}, - {ID: 3498, Value: 0}, - {ID: 3499, Value: 0}, - {ID: 3500, Value: 0}, - {ID: 3501, Value: 0}, - {ID: 3502, Value: 0}, - {ID: 3503, Value: 0}, - {ID: 3504, Value: 0}, - {ID: 3505, Value: 0}, - {ID: 3506, Value: 0}, - {ID: 3520, Value: 0}, - {ID: 3521, Value: 0}, - {ID: 3522, Value: 0}, - {ID: 3523, Value: 0}, - {ID: 3524, Value: 0}, - {ID: 3525, Value: 0}, - {ID: 3526, Value: 0}, - {ID: 3527, Value: 0}, - {ID: 3528, Value: 0}, - {ID: 3529, Value: 0}, - {ID: 3530, Value: 0}, - {ID: 3531, Value: 0}, - {ID: 3532, Value: 0}, - {ID: 3546, Value: 0}, - {ID: 3547, Value: 0}, - {ID: 3548, Value: 0}, - {ID: 3549, Value: 0}, - {ID: 3550, Value: 0}, - {ID: 3551, Value: 0}, - {ID: 3552, Value: 0}, - {ID: 3553, Value: 0}, - {ID: 3554, Value: 0}, - {ID: 3555, Value: 0}, - {ID: 3556, Value: 0}, - {ID: 3557, Value: 0}, - {ID: 3558, Value: 0}, - {ID: 3572, Value: 0}, - {ID: 3573, Value: 0}, - {ID: 3574, Value: 0}, - {ID: 3575, Value: 0}, - {ID: 3576, Value: 0}, - {ID: 3577, Value: 0}, - {ID: 3578, Value: 0}, - {ID: 3579, Value: 0}, - {ID: 3580, Value: 0}, - {ID: 3581, Value: 0}, - {ID: 3582, Value: 0}, - {ID: 3583, Value: 0}, - {ID: 3584, Value: 0}, } tuneValues = append(tuneValues, tuneValue{1020, uint16(s.server.erupeConfig.GameplayOptions.GCPMultiplier * 100)}) @@ -653,65 +512,70 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { if s.server.erupeConfig.GameplayOptions.EnableKaijiEvent { tuneValues = append(tuneValues, tuneValue{1106, 1}) - } else { - tuneValues = append(tuneValues, tuneValue{1106, 0}) } if s.server.erupeConfig.GameplayOptions.EnableHiganjimaEvent { tuneValues = append(tuneValues, tuneValue{1144, 1}) - } else { - tuneValues = append(tuneValues, tuneValue{1144, 0}) } if s.server.erupeConfig.GameplayOptions.EnableNierEvent { tuneValues = append(tuneValues, tuneValue{1153, 1}) - } else { - tuneValues = append(tuneValues, tuneValue{1153, 0}) } if s.server.erupeConfig.GameplayOptions.DisableRoad { tuneValues = append(tuneValues, tuneValue{1155, 1}) - } else { - tuneValues = append(tuneValues, tuneValue{1155, 0}) } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3026, uint16(s.server.erupeConfig.GameplayOptions.GRPMultiplier * 100)}) - } + // get_hrp_rate_from_rank + tuneValues = append(tuneValues, getTuneValueRange(3000, uint16(s.server.erupeConfig.GameplayOptions.HRPMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3338, uint16(s.server.erupeConfig.GameplayOptions.HRPMultiplierNC*100))...) + // get_srp_rate_from_rank + tuneValues = append(tuneValues, getTuneValueRange(3013, uint16(s.server.erupeConfig.GameplayOptions.SRPMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3351, uint16(s.server.erupeConfig.GameplayOptions.SRPMultiplierNC*100))...) + // get_grp_rate_from_rank + tuneValues = append(tuneValues, getTuneValueRange(3026, uint16(s.server.erupeConfig.GameplayOptions.GRPMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3364, uint16(s.server.erupeConfig.GameplayOptions.GRPMultiplierNC*100))...) + // get_gsrp_rate_from_rank + tuneValues = append(tuneValues, getTuneValueRange(3039, uint16(s.server.erupeConfig.GameplayOptions.GSRPMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3377, uint16(s.server.erupeConfig.GameplayOptions.GSRPMultiplierNC*100))...) + // get_zeny_rate_from_hrank + tuneValues = append(tuneValues, getTuneValueRange(3052, uint16(s.server.erupeConfig.GameplayOptions.ZennyMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3390, uint16(s.server.erupeConfig.GameplayOptions.ZennyMultiplierNC*100))...) + // get_zeny_rate_from_grank + tuneValues = append(tuneValues, getTuneValueRange(3078, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3416, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplierNC*100))...) + // get_reward_rate_from_hrank + tuneValues = append(tuneValues, getTuneValueRange(3104, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3442, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplierNC*100))...) + // get_reward_rate_from_grank + tuneValues = append(tuneValues, getTuneValueRange(3130, uint16(s.server.erupeConfig.GameplayOptions.GMaterialMultiplier*100))...) + tuneValues = append(tuneValues, getTuneValueRange(3468, uint16(s.server.erupeConfig.GameplayOptions.GMaterialMultiplierNC*100))...) + // get_lottery_rate_from_hrank + tuneValues = append(tuneValues, getTuneValueRange(3156, 0)...) + tuneValues = append(tuneValues, getTuneValueRange(3494, 0)...) + // get_lottery_rate_from_grank + tuneValues = append(tuneValues, getTuneValueRange(3182, 0)...) + tuneValues = append(tuneValues, getTuneValueRange(3520, 0)...) + // get_hagi_rate_from_hrank + tuneValues = append(tuneValues, getTuneValueRange(3208, s.server.erupeConfig.GameplayOptions.ExtraCarves)...) + tuneValues = append(tuneValues, getTuneValueRange(3546, s.server.erupeConfig.GameplayOptions.ExtraCarvesNC)...) + // get_hagi_rate_from_grank + tuneValues = append(tuneValues, getTuneValueRange(3234, s.server.erupeConfig.GameplayOptions.GExtraCarves)...) + tuneValues = append(tuneValues, getTuneValueRange(3572, s.server.erupeConfig.GameplayOptions.GExtraCarvesNC)...) + // get_nboost_transcend_rate_from_hrank + tuneValues = append(tuneValues, getTuneValueRange(3286, 200)...) + tuneValues = append(tuneValues, getTuneValueRange(3312, 300)...) + // get_nboost_transcend_rate_from_grank + tuneValues = append(tuneValues, getTuneValueRange(3299, 200)...) + tuneValues = append(tuneValues, getTuneValueRange(3325, 300)...) - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3039, uint16(s.server.erupeConfig.GameplayOptions.GSRPMultiplier * 100)}) + var temp []tuneValue + for i := range tuneValues { + if tuneValues[i].Value > 0 { + temp = append(temp, tuneValues[i]) + } } - - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3052, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplier * 100)}) - } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3078, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplier * 100)}) - } - - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3104, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplier * 100)}) - } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3130, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplier * 100)}) - } - - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3156, s.server.erupeConfig.GameplayOptions.ExtraCarves}) - } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3182, s.server.erupeConfig.GameplayOptions.ExtraCarves}) - } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3208, s.server.erupeConfig.GameplayOptions.ExtraCarves}) - } - for i := uint16(0); i < 13; i++ { - tuneValues = append(tuneValues, tuneValue{i + 3234, s.server.erupeConfig.GameplayOptions.ExtraCarves}) - } - - offset := uint16(time.Now().Unix()) - bf.WriteUint16(offset) + tuneValues = temp tuneLimit := 770 if _config.ErupeConfig.RealClientMode <= _config.F5 { @@ -737,6 +601,9 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { tuneValues = tuneValues[:tuneLimit] } + offset := uint16(time.Now().Unix()) + bf.WriteUint16(offset) + bf.WriteUint16(uint16(len(tuneValues))) for i := range tuneValues { bf.WriteUint16(tuneValues[i].ID ^ offset) @@ -778,6 +645,14 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } +func getTuneValueRange(start uint16, value uint16) []tuneValue { + var tv []tuneValue + for i := uint16(0); i < 13; i++ { + tv = append(tv, tuneValue{start + i, value}) + } + return tv +} + func handleMsgMhfEnterTournamentQuest(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index a9de55b28..bc0b3689e 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -157,7 +157,6 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.stage.reservedClientSlots[s.charID] = false s.stage.Unlock() s.stageMoveStack.Push(s.stage.id) - s.stageMoveStack.Lock() } if s.reservationStage != nil { @@ -171,7 +170,6 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysBackStage) // Transfer back to the saved stage ID before the previous move or enter. - s.stageMoveStack.Unlock() backStage, err := s.stageMoveStack.Pop() if backStage == "" || err != nil { backStage = "sl1Ns200p0a0u0" @@ -190,12 +188,6 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysMoveStage) - - // Set a new move stack from the given stage ID - if !s.stageMoveStack.Locked { - s.stageMoveStack.Set(pkt.StageID) - } - doStageTransfer(s, pkt.AckHandle, pkt.StageID) } diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index 4ce0bcc9f..8f32a7882 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -1,8 +1,10 @@ package channelserver import ( + _config "erupe-ce/config" "fmt" "go.uber.org/zap" + "strings" "time" "erupe-ce/common/byteframe" @@ -16,8 +18,8 @@ type TowerInfoTRP struct { } type TowerInfoSkill struct { - TSP int32 - Unk1 []int16 // 40 + TSP int32 + Skills []int16 // 64 } type TowerInfoHistory struct { @@ -32,6 +34,14 @@ type TowerInfoLevel struct { Unk3 int32 } +func EmptyTowerCSV(len int) string { + temp := make([]string, len) + for i := range temp { + temp[i] = "0" + } + return strings.Join(temp, ",") +} + func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetTowerInfo) var data []*byteframe.ByteFrame @@ -44,21 +54,24 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { towerInfo := TowerInfo{ TRP: []TowerInfoTRP{{0, 0}}, - Skill: []TowerInfoSkill{{0, make([]int16, 40)}}, + Skill: []TowerInfoSkill{{0, make([]int16, 64)}}, History: []TowerInfoHistory{{make([]int16, 5), make([]int16, 5)}}, Level: []TowerInfoLevel{{0, 0, 0, 0}, {0, 0, 0, 0}}, } - tempSkills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - - err := s.server.db.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), skills FROM tower WHERE char_id=$1 - `, s.charID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills) + var tempSkills string + err := s.server.db.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), COALESCE(skills, $1) FROM tower WHERE char_id=$2 + `, EmptyTowerCSV(64), s.charID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills) if err != nil { s.server.db.Exec(`INSERT INTO tower (char_id) VALUES ($1)`, s.charID) } + if _config.ErupeConfig.RealClientMode <= _config.G7 { + towerInfo.Level = towerInfo.Level[:1] + } + for i, skill := range stringsupport.CSVElems(tempSkills) { - towerInfo.Skill[0].Unk1[i] = int16(skill) + towerInfo.Skill[0].Skills[i] = int16(skill) } switch pkt.InfoType { @@ -73,8 +86,8 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { for _, skills := range towerInfo.Skill { bf := byteframe.NewByteFrame() bf.WriteInt32(skills.TSP) - for i := range skills.Unk1 { - bf.WriteInt16(skills.Unk1[i]) + for i := range skills.Skills { + bf.WriteInt16(skills.Skills[i]) } data = append(data, bf) } @@ -89,7 +102,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { } data = append(data, bf) } - case 5: + case 3, 5: for _, level := range towerInfo.Level { bf := byteframe.NewByteFrame() bf.WriteInt32(level.Floors) @@ -105,7 +118,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPostTowerInfo) - if s.server.erupeConfig.DevModeOptions.QuestDebugTools { + if s.server.erupeConfig.DebugOptions.QuestTools { s.logger.Debug( p.Opcode().String(), zap.Uint32("InfoType", pkt.InfoType), @@ -123,8 +136,8 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { switch pkt.InfoType { case 2: - skills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - s.server.db.QueryRow(`SELECT skills FROM tower WHERE char_id=$1`, s.charID).Scan(&skills) + var skills string + s.server.db.QueryRow(`SELECT COALESCE(skills, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(64), s.charID).Scan(&skills) s.server.db.Exec(`UPDATE tower SET skills=$1, tsp=tsp-$2 WHERE char_id=$3`, stringsupport.CSVSetIndex(skills, int(pkt.Skill), stringsupport.CSVGetIndex(skills, int(pkt.Skill))+1), pkt.Cost, s.charID) case 1, 7: // This might give too much TSP? No idea what the rate is supposed to be @@ -328,7 +341,7 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPostTenrouirai) - if s.server.erupeConfig.DevModeOptions.QuestDebugTools { + if s.server.erupeConfig.DebugOptions.QuestTools { s.logger.Debug( p.Opcode().String(), zap.Uint8("Unk0", pkt.Unk0), @@ -412,10 +425,10 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) { gemInfo := []GemInfo{} gemHistory := []GemHistory{} - tempGems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&tempGems) + var tempGems string + s.server.db.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.charID).Scan(&tempGems) for i, v := range stringsupport.CSVElems(tempGems) { - gemInfo = append(gemInfo, GemInfo{uint16(((i / 5) * 256) + ((i % 5) + 1)), uint16(v)}) + gemInfo = append(gemInfo, GemInfo{uint16((i / 5 << 8) + (i%5 + 1)), uint16(v)}) } switch pkt.Unk0 { @@ -442,7 +455,7 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPostGemInfo) - if s.server.erupeConfig.DevModeOptions.QuestDebugTools { + if s.server.erupeConfig.DebugOptions.QuestTools { s.logger.Debug( p.Opcode().String(), zap.Uint32("Op", pkt.Op), @@ -455,11 +468,11 @@ func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) { ) } - gems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" - s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&gems) + var gems string + s.server.db.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.charID).Scan(&gems) switch pkt.Op { case 1: // Add gem - i := int(((pkt.Gem / 256) * 5) + (((pkt.Gem - ((pkt.Gem / 256) * 256)) - 1) % 5)) + i := int((pkt.Gem >> 8 * 5) + (pkt.Gem - pkt.Gem&0xFF00 - 1%5)) s.server.db.Exec(`UPDATE tower SET gems=$1 WHERE char_id=$2`, stringsupport.CSVSetIndex(gems, i, stringsupport.CSVGetIndex(gems, i)+int(pkt.Quantity)), s.charID) case 2: // Transfer gem // no way im doing this for now diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 70f52e461..19bd04123 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -57,7 +57,7 @@ type Server struct { stages map[string]*Stage // Used to map different languages - dict map[string]string + i18n i18n // UserBinary userBinaryPartsLock sync.RWMutex @@ -192,7 +192,7 @@ func NewServer(config *Config) *Server { // MezFes s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0") - s.dict = getLangStrings(s) + s.i18n = getLangStrings(s) return s } @@ -211,6 +211,7 @@ func (s *Server) Start() error { // Start the discord bot for chat integration. if s.erupeConfig.Discord.Enabled && s.discordBot != nil { s.discordBot.Session.AddHandler(s.onDiscordMessage) + s.discordBot.Session.AddHandler(s.onInteraction) } return nil @@ -336,13 +337,13 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u var text string switch _type { case 2: - text = s.dict["ravienteBerserk"] + text = s.i18n.raviente.berserk case 3: - text = s.dict["ravienteExtreme"] + text = s.i18n.raviente.extreme case 4: - text = s.dict["ravienteExtremeLimited"] + text = s.i18n.raviente.extremeLimited case 5: - text = s.dict["ravienteBerserkSmall"] + text = s.i18n.raviente.berserkSmall default: s.logger.Error("Unk raviente type", zap.Uint8("_type", _type)) } @@ -377,6 +378,26 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session { return nil } +func (s *Server) DisconnectUser(uid uint32) { + var cid uint32 + var cids []uint32 + rows, _ := s.db.Query(`SELECT id FROM characters WHERE user_id=$1`, uid) + for rows.Next() { + rows.Scan(&cid) + cids = append(cids, cid) + } + for _, c := range s.Channels { + for _, session := range c.sessions { + for _, cid := range cids { + if session.charID == cid { + session.rawConn.Close() + break + } + } + } + } +} + func (s *Server) FindObjectByChar(charID uint32) *Object { s.stagesLock.RLock() defer s.stagesLock.RUnlock() diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index df74e1212..aae8706bb 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -1,114 +1,238 @@ package channelserver -func getLangStrings(s *Server) map[string]string { - strings := make(map[string]string) +type i18n struct { + language string + cafe struct { + reset string + } + timer string + commands struct { + noOp string + disabled string + reload string + kqf struct { + get string + set struct { + error string + success string + } + version string + } + rights struct { + error string + success string + } + course struct { + error string + disabled string + enabled string + locked string + } + teleport struct { + error string + success string + } + psn struct { + error string + success string + exists string + } + discord struct { + success string + } + ban struct { + success string + noUser string + invalid string + error string + length string + } + timer struct { + enabled string + disabled string + } + ravi struct { + noCommand string + start struct { + success string + error string + } + multiplier string + res struct { + success string + error string + } + sed struct { + success string + } + request string + error string + noPlayers string + version string + } + } + raviente struct { + berserk string + extreme string + extremeLimited string + berserkSmall string + } + guild struct { + invite struct { + title string + body string + success struct { + title string + body string + } + accepted struct { + title string + body string + } + rejected struct { + title string + body string + } + declined struct { + title string + body string + } + } + } +} + +func getLangStrings(s *Server) i18n { + var i i18n switch s.erupeConfig.Language { case "jp": - strings["language"] = "日本語" - strings["cafeReset"] = "%d/%dにリセット" + i.language = "日本語" + i.cafe.reset = "%d/%dにリセット" + i.timer = "タイマー:%02d'%02d\"%02d.%03d (%df)" - strings["commandDisabled"] = "%sのコマンドは無効です" - strings["commandReload"] = "リロードします" - strings["commandKqfGet"] = "現在のキークエストフラグ:%x" - strings["commandKqfSetError"] = "キークエコマンドエラー 例:%s set xxxxxxxxxxxxxxxx" - strings["commandKqfSetSuccess"] = "キークエストのフラグが更新されました。ワールド/ランドを移動してください" - strings["commandKqfVersion"] = "This command is disabled prior to MHFG10" - strings["commandRightsError"] = "コース更新コマンドエラー 例:%s x" - strings["commandRightsSuccess"] = "コース情報を更新しました:%d" - strings["commandCourseError"] = "コース確認コマンドエラー 例:%s " - strings["commandCourseDisabled"] = "%sコースは無効です" - strings["commandCourseEnabled"] = "%sコースは有効です" - strings["commandCourseLocked"] = "%sコースはロックされています" - strings["commandTeleportError"] = "テレポートコマンドエラー 構文:%s x y" - strings["commandTeleportSuccess"] = "%d %dにテレポート" - strings["commandPSNError"] = "PSN連携コマンドエラー 例:%s " - strings["commandPSNSuccess"] = "PSN「%s」が連携されています" - strings["commandPSNExists"] = "PSNは既存のユーザに接続されています" + i.commands.noOp = "You don't have permission to use this command" + i.commands.disabled = "%sのコマンドは無効です" + i.commands.reload = "リロードします" + i.commands.kqf.get = "現在のキークエストフラグ:%x" + i.commands.kqf.set.error = "キークエコマンドエラー 例:%s set xxxxxxxxxxxxxxxx" + i.commands.kqf.set.success = "キークエストのフラグが更新されました。ワールド/ランドを移動してください" + i.commands.kqf.version = "This command is disabled prior to MHFG10" + i.commands.rights.error = "コース更新コマンドエラー 例:%s x" + i.commands.rights.success = "コース情報を更新しました:%d" + i.commands.course.error = "コース確認コマンドエラー 例:%s " + i.commands.course.disabled = "%sコースは無効です" + i.commands.course.enabled = "%sコースは有効です" + i.commands.course.locked = "%sコースはロックされています" + i.commands.teleport.error = "テレポートコマンドエラー 構文:%s x y" + i.commands.teleport.success = "%d %dにテレポート" + i.commands.psn.error = "PSN連携コマンドエラー 例:%s " + i.commands.psn.success = "PSN「%s」が連携されています" + i.commands.psn.exists = "PSNは既存のユーザに接続されています" - strings["commandRaviNoCommand"] = "ラヴィコマンドが指定されていません" - strings["commandRaviStartSuccess"] = "大討伐を開始します" - strings["commandRaviStartError"] = "大討伐は既に開催されています" - strings["commandRaviMultiplier"] = "ラヴィダメージ倍率:x%.2f" - strings["commandRaviResSuccess"] = "復活支援を実行します" - strings["commandRaviResError"] = "復活支援は実行されませんでした" - strings["commandRaviSedSuccess"] = "鎮静支援を実行します" - strings["commandRaviRequest"] = "鎮静支援を要請します" - strings["commandRaviError"] = "ラヴィコマンドが認識されません" - strings["commandRaviNoPlayers"] = "誰も大討伐に参加していません" - strings["commandRaviVersion"] = "This command is disabled outside of MHFZZ" + i.commands.discord.success = "あなたのDiscordトークン:%s" - strings["ravienteBerserk"] = "<大討伐:猛狂期>が開催されました!" - strings["ravienteExtreme"] = "<大討伐:猛狂期【極】>が開催されました!" - strings["ravienteExtremeLimited"] = "<大討伐:猛狂期【極】(制限付)>が開催されました!" - strings["ravienteBerserkSmall"] = "<大討伐:猛狂期(小数)>が開催されました!" + i.commands.ban.noUser = "Could not find user" + i.commands.ban.success = "Successfully banned %s" + i.commands.ban.invalid = "Invalid Character ID" + i.commands.ban.error = "Error in command. Format: %s [length]" + i.commands.ban.length = " until %s" - strings["guildInviteName"] = "猟団勧誘のご案内" - strings["guildInvite"] = "猟団「%s」からの勧誘通知です。\n「勧誘に返答」より、返答を行ってください。" + i.commands.ravi.noCommand = "ラヴィコマンドが指定されていません" + i.commands.ravi.start.success = "大討伐を開始します" + i.commands.ravi.start.error = "大討伐は既に開催されています" + i.commands.ravi.multiplier = "ラヴィダメージ倍率:x%.2f" + i.commands.ravi.res.success = "復活支援を実行します" + i.commands.ravi.res.error = "復活支援は実行されませんでした" + i.commands.ravi.sed.success = "鎮静支援を実行します" + i.commands.ravi.request = "鎮静支援を要請します" + i.commands.ravi.error = "ラヴィコマンドが認識されません" + i.commands.ravi.noPlayers = "誰も大討伐に参加していません" + i.commands.ravi.version = "This command is disabled outside of MHFZZ" - strings["guildInviteSuccessName"] = "成功" - strings["guildInviteSuccess"] = "あなたは「%s」に参加できました。" + i.raviente.berserk = "<大討伐:猛狂期>が開催されました!" + i.raviente.extreme = "<大討伐:猛狂期【極】>が開催されました!" + i.raviente.extremeLimited = "<大討伐:猛狂期【極】(制限付)>が開催されました!" + i.raviente.berserkSmall = "<大討伐:猛狂期(小数)>が開催されました!" - strings["guildInviteAcceptedName"] = "承諾されました" - strings["guildInviteAccepted"] = "招待した狩人が「%s」への招待を承諾しました。" + i.guild.invite.title = "猟団勧誘のご案内" + i.guild.invite.body = "猟団「%s」からの勧誘通知です。\n「勧誘に返答」より、返答を行ってください。" - strings["guildInviteRejectName"] = "却下しました" - strings["guildInviteReject"] = "あなたは「%s」への参加を却下しました。" + i.guild.invite.success.title = "成功" + i.guild.invite.success.body = "あなたは「%s」に参加できました。" - strings["guildInviteDeclinedName"] = "辞退しました" - strings["guildInviteDeclined"] = "招待した狩人が「%s」への招待を辞退しました。" + i.guild.invite.accepted.title = "承諾されました" + i.guild.invite.accepted.body = "招待した狩人が「%s」への招待を承諾しました。" + + i.guild.invite.rejected.title = "却下しました" + i.guild.invite.rejected.body = "あなたは「%s」への参加を却下しました。" + + i.guild.invite.declined.title = "辞退しました" + i.guild.invite.declined.body = "招待した狩人が「%s」への招待を辞退しました。" default: - strings["language"] = "English" - strings["cafeReset"] = "Resets on %d/%d" + i.language = "English" + i.cafe.reset = "Resets on %d/%d" + i.timer = "Time: %02d:%02d:%02d.%03d (%df)" - strings["commandDisabled"] = "%s command is disabled" - strings["commandReload"] = "Reloading players..." - strings["commandKqfGet"] = "KQF: %x" - strings["commandKqfSetError"] = "Error in command. Format: %s set xxxxxxxxxxxxxxxx" - strings["commandKqfSetSuccess"] = "KQF set, please switch Land/World" - strings["commandKqfVersion"] = "This command is disabled prior to MHFG10" - strings["commandRightsError"] = "Error in command. Format: %s x" - strings["commandRightsSuccess"] = "Set rights integer: %d" - strings["commandCourseError"] = "Error in command. Format: %s " - strings["commandCourseDisabled"] = "%s Course disabled" - strings["commandCourseEnabled"] = "%s Course enabled" - strings["commandCourseLocked"] = "%s Course is locked" - strings["commandTeleportError"] = "Error in command. Format: %s x y" - strings["commandTeleportSuccess"] = "Teleporting to %d %d" - strings["commandPSNError"] = "Error in command. Format: %s " - strings["commandPSNSuccess"] = "Connected PSN ID: %s" - strings["commandPSNExists"] = "PSN ID is connected to another account!" + i.commands.noOp = "You don't have permission to use this command" + i.commands.disabled = "%s command is disabled" + i.commands.reload = "Reloading players..." + i.commands.kqf.get = "KQF: %x" + i.commands.kqf.set.error = "Error in command. Format: %s set xxxxxxxxxxxxxxxx" + i.commands.kqf.set.success = "KQF set, please switch Land/World" + i.commands.kqf.version = "This command is disabled prior to MHFG10" + i.commands.rights.error = "Error in command. Format: %s x" + i.commands.rights.success = "Set rights integer: %d" + i.commands.course.error = "Error in command. Format: %s " + i.commands.course.disabled = "%s Course disabled" + i.commands.course.enabled = "%s Course enabled" + i.commands.course.locked = "%s Course is locked" + i.commands.teleport.error = "Error in command. Format: %s x y" + i.commands.teleport.success = "Teleporting to %d %d" + i.commands.psn.error = "Error in command. Format: %s " + i.commands.psn.success = "Connected PSN ID: %s" + i.commands.psn.exists = "PSN ID is connected to another account!" - strings["commandRaviNoCommand"] = "No Raviente command specified!" - strings["commandRaviStartSuccess"] = "The Great Slaying will begin in a moment" - strings["commandRaviStartError"] = "The Great Slaying has already begun!" - strings["commandRaviMultiplier"] = "Raviente multiplier is currently %.2fx" - strings["commandRaviResSuccess"] = "Sending resurrection support!" - strings["commandRaviResError"] = "Resurrection support has not been requested!" - strings["commandRaviSedSuccess"] = "Sending sedation support if requested!" - strings["commandRaviRequest"] = "Requesting sedation support!" - strings["commandRaviError"] = "Raviente command not recognised!" - strings["commandRaviNoPlayers"] = "No one has joined the Great Slaying!" - strings["commandRaviVersion"] = "This command is disabled outside of MHFZZ" + i.commands.discord.success = "Your Discord token: %s" - strings["ravienteBerserk"] = " is being held!" - strings["ravienteExtreme"] = " is being held!" - strings["ravienteExtremeLimited"] = " is being held!" - strings["ravienteBerserkSmall"] = " is being held!" + i.commands.ban.noUser = "Could not find user" + i.commands.ban.success = "Successfully banned %s" + i.commands.ban.invalid = "Invalid Character ID" + i.commands.ban.error = "Error in command. Format: %s [length]" + i.commands.ban.length = " until %s" - strings["guildInviteName"] = "Invitation!" - strings["guildInvite"] = "You have been invited to join\n「%s」\nDo you want to accept?" + i.commands.timer.enabled = "Quest timer enabled" + i.commands.timer.disabled = "Quest timer disabled" - strings["guildInviteSuccessName"] = "Success!" - strings["guildInviteSuccess"] = "You have successfully joined\n「%s」." + i.commands.ravi.noCommand = "No Raviente command specified!" + i.commands.ravi.start.success = "The Great Slaying will begin in a moment" + i.commands.ravi.start.error = "The Great Slaying has already begun!" + i.commands.ravi.multiplier = "Raviente multiplier is currently %.2fx" + i.commands.ravi.res.success = "Sending resurrection support!" + i.commands.ravi.res.error = "Resurrection support has not been requested!" + i.commands.ravi.sed.success = "Sending sedation support if requested!" + i.commands.ravi.request = "Requesting sedation support!" + i.commands.ravi.error = "Raviente command not recognised!" + i.commands.ravi.noPlayers = "No one has joined the Great Slaying!" + i.commands.ravi.version = "This command is disabled outside of MHFZZ" - strings["guildInviteAcceptedName"] = "Accepted" - strings["guildInviteAccepted"] = "The recipient accepted your invitation to join\n「%s」." + i.raviente.berserk = " is being held!" + i.raviente.extreme = " is being held!" + i.raviente.extremeLimited = " is being held!" + i.raviente.berserkSmall = " is being held!" - strings["guildInviteRejectName"] = "Rejected" - strings["guildInviteReject"] = "You rejected the invitation to join\n「%s」." + i.guild.invite.title = "Invitation!" + i.guild.invite.body = "You have been invited to join\n「%s」\nDo you want to accept?" - strings["guildInviteDeclinedName"] = "Declined" - strings["guildInviteDeclined"] = "The recipient declined your invitation to join\n「%s」." + i.guild.invite.success.title = "Success!" + i.guild.invite.success.body = "You have successfully joined\n「%s」." + + i.guild.invite.accepted.title = "Accepted" + i.guild.invite.accepted.body = "The recipient accepted your invitation to join\n「%s」." + + i.guild.invite.rejected.title = "Rejected" + i.guild.invite.rejected.body = "You rejected the invitation to join\n「%s」." + + i.guild.invite.declined.title = "Declined" + i.guild.invite.declined.body = "The recipient declined your invitation to join\n「%s」." } - return strings + return i } diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 5034f38c2..d7e55d621 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -86,13 +86,11 @@ func NewSession(server *Server, conn net.Conn) *Session { // Start starts the session packet send and recv loop(s). func (s *Session) Start() { - go func() { - s.logger.Debug("New connection", zap.String("RemoteAddr", s.rawConn.RemoteAddr().String())) - // Unlike the sign and entrance server, - // the client DOES NOT initalize the channel connection with 8 NULL bytes. - go s.sendLoop() - s.recvLoop() - }() + s.logger.Debug("New connection", zap.String("RemoteAddr", s.rawConn.RemoteAddr().String())) + // Unlike the sign and entrance server, + // the client DOES NOT initalize the channel connection with 8 NULL bytes. + go s.sendLoop() + go s.recvLoop() } // QueueSend queues a packet (raw []byte) to be sent. @@ -150,16 +148,19 @@ func (s *Session) QueueAck(ackHandle uint32, data []byte) { } func (s *Session) sendLoop() { + var pkt packet for { if s.closed { return } - pkt := <-s.sendPackets - err := s.cryptConn.SendPacket(append(pkt.data, []byte{0x00, 0x10}...)) - if err != nil { - s.logger.Warn("Failed to send packet") + for len(s.sendPackets) > 0 { + pkt = <-s.sendPackets + err := s.cryptConn.SendPacket(append(pkt.data, []byte{0x00, 0x10}...)) + if err != nil { + s.logger.Warn("Failed to send packet") + } } - time.Sleep(10 * time.Millisecond) + time.Sleep(5 * time.Millisecond) } } @@ -178,14 +179,13 @@ func (s *Session) recvLoop() { s.logger.Info(fmt.Sprintf("[%s] Disconnected", s.Name)) logoutPlayer(s) return - } - if err != nil { + } else if err != nil { s.logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err)) logoutPlayer(s) return } s.handlePacketGroup(pkt) - time.Sleep(10 * time.Millisecond) + time.Sleep(5 * time.Millisecond) } } @@ -253,13 +253,9 @@ func ignored(opcode network.PacketID) bool { } func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipient string) { - if !s.server.erupeConfig.DevMode { + if sender == "Server" && !s.server.erupeConfig.DebugOptions.LogOutboundMessages { return - } - - if sender == "Server" && !s.server.erupeConfig.DevModeOptions.LogOutboundMessages { - return - } else if sender != "Server" && !s.server.erupeConfig.DevModeOptions.LogInboundMessages { + } else if sender != "Server" && !s.server.erupeConfig.DebugOptions.LogInboundMessages { return } @@ -277,8 +273,8 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien fmt.Printf("[%s] -> [%s]\n", sender, recipient) } fmt.Printf("Opcode: %s\n", opcodePID) - if s.server.erupeConfig.DevModeOptions.LogMessageData { - if len(data) <= s.server.erupeConfig.DevModeOptions.MaxHexdumpLength { + if s.server.erupeConfig.DebugOptions.LogMessageData { + if len(data) <= s.server.erupeConfig.DebugOptions.MaxHexdumpLength { fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data)) } else { fmt.Printf("Data [%d bytes]: (Too long!)\n\n", len(data)) @@ -313,3 +309,12 @@ func (s *Session) NextObjectID() uint32 { bf.Seek(0, 0) return bf.ReadUint32() } + +func (s *Session) isOp() bool { + var op bool + err := s.server.db.QueryRow(`SELECT op FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&op) + if err == nil && op { + return true + } + return false +} diff --git a/server/discordbot/discord_bot.go b/server/discordbot/discord_bot.go index c082faf70..a9b327cc3 100644 --- a/server/discordbot/discord_bot.go +++ b/server/discordbot/discord_bot.go @@ -7,12 +7,39 @@ import ( "regexp" ) +var Commands = []*discordgo.ApplicationCommand{ + { + Name: "link", + Description: "Link your Erupe account to Discord", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "token", + Description: "The token provided by the Discord command in-game", + Required: true, + }, + }, + }, + { + Name: "password", + Description: "Change your Erupe account password", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "password", + Description: "Your new password", + Required: true, + }, + }, + }, +} + type DiscordBot struct { - Session *discordgo.Session - config *_config.Config - logger *zap.Logger - MainGuild *discordgo.Guild - RealtimeChannel *discordgo.Channel + Session *discordgo.Session + config *_config.Config + logger *zap.Logger + MainGuild *discordgo.Guild + RelayChannel *discordgo.Channel } type Options struct { @@ -28,18 +55,22 @@ func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) { return nil, err } - realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID) + var relayChannel *discordgo.Channel + + if options.Config.Discord.RelayChannel.Enabled { + relayChannel, err = session.Channel(options.Config.Discord.RelayChannel.RelayChannelID) + } if err != nil { - options.Logger.Fatal("Discord failed to create realtimeChannel", zap.Error(err)) + options.Logger.Fatal("Discord failed to create relayChannel", zap.Error(err)) return nil, err } discordBot = &DiscordBot{ - config: options.Config, - logger: options.Logger, - Session: session, - RealtimeChannel: realtimeChannel, + config: options.Config, + logger: options.Logger, + Session: session, + RelayChannel: relayChannel, } return @@ -51,7 +82,7 @@ func (bot *DiscordBot) Start() (err error) { return } -// Replace all mentions to real name from the message. +// NormalizeDiscordMessage replaces all mentions to real name from the message. func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`) emojiRegex := regexp.MustCompile(`(?:)?`) @@ -74,7 +105,11 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string { } func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) { - _, err = bot.Session.ChannelMessageSend(bot.RealtimeChannel.ID, message) + if bot.RelayChannel == nil { + return + } + + _, err = bot.Session.ChannelMessageSend(bot.RelayChannel.ID, message) return } diff --git a/server/entranceserver/crypto.go b/server/entranceserver/crypto.go index 20a361e7e..e9e76f9fa 100644 --- a/server/entranceserver/crypto.go +++ b/server/entranceserver/crypto.go @@ -12,45 +12,40 @@ var ( // CalcSum32 calculates the custom MHF "sum32" checksum of the given data. func CalcSum32(data []byte) uint32 { - tableIdx0 := int(len(data) & 0xFF) - tableIdx1 := int(data[len(data)>>1] & 0xFF) - + tableIdx0 := (len(data) + 1) & 0xFF + tableIdx1 := int((data[len(data)>>1] + 1) & 0xFF) out := make([]byte, 4) for i := 0; i < len(data); i++ { - tableIdx0++ - tableIdx1++ - - tmp := byte((_sum32Table1[tableIdx1%9] ^ _sum32Table0[tableIdx0%7]) ^ data[i]) - out[i&3] = (out[i&3] + tmp) & 0xFF + key := data[i] ^ _sum32Table0[(tableIdx0+i)%7] ^ _sum32Table1[(tableIdx1+i)%9] + out[i&3] = (out[i&3] + key) & 0xFF } - return binary.BigEndian.Uint32(out) } +func rotate(k *uint32) { + *k = uint32(((54323 * uint(*k)) + 1) & 0xFFFFFFFF) +} + // EncryptBin8 encrypts the given data using MHF's "binary8" encryption. func EncryptBin8(data []byte, key byte) []byte { - curKey := uint32(((54323 * uint(key)) + 1) & 0xFFFFFFFF) - + _key := uint32(key) var output []byte for i := 0; i < len(data); i++ { - tmp := (_bin8Key[i&7] ^ byte((curKey>>13)&0xFF)) + rotate(&_key) + tmp := _bin8Key[i&7] ^ byte((_key>>13)&0xFF) output = append(output, data[i]^tmp) - curKey = uint32(((54323 * uint(curKey)) + 1) & 0xFFFFFFFF) } - return output } // DecryptBin8 decrypts the given MHF "binary8" data. func DecryptBin8(data []byte, key byte) []byte { - curKey := uint32(((54323 * uint(key)) + 1) & 0xFFFFFFFF) - + _key := uint32(key) var output []byte for i := 0; i < len(data); i++ { - tmp := (data[i] ^ byte((curKey>>13)&0xFF)) + rotate(&_key) + tmp := data[i] ^ byte((_key>>13)&0xFF) output = append(output, tmp^_bin8Key[i&7]) - curKey = uint32(((54323 * uint(curKey)) + 1) & 0xFFFFFFFF) } - return output } diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go index 8b06be0e0..18869304b 100644 --- a/server/entranceserver/entrance_server.go +++ b/server/entranceserver/entrance_server.go @@ -111,7 +111,7 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) { return } - if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogInboundMessages { + if s.erupeConfig.DebugOptions.LogInboundMessages { fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt)) } diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 4b478fa24..ec0281abc 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -29,7 +29,6 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } } - sid := (4096 + serverIdx*256) * 6000 if si.IP == "" { si.IP = config.Host } @@ -38,8 +37,8 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } else { bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) } - bf.WriteUint16(16 + uint16(serverIdx)) - bf.WriteUint16(0x0000) + bf.WriteUint16(uint16(serverIdx | 16)) + bf.WriteUint16(0) bf.WriteUint16(uint16(len(si.Channels))) bf.WriteUint8(si.Type) bf.WriteUint8(uint8(((channelserver.TimeAdjusted().Unix() / 86400) + int64(serverIdx)) % 3)) @@ -47,20 +46,15 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { bf.WriteUint8(si.Recommended) } - if s.erupeConfig.RealClientMode <= _config.F5 { - combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) - combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) - bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) - } else if s.erupeConfig.RealClientMode <= _config.GG { - combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) - combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) - bf.WriteUint8(uint8(len(combined))) - bf.WriteBytes(combined) + fullName := append(append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...), stringsupport.UTF8ToSJIS(si.Description)...) + if s.erupeConfig.RealClientMode >= _config.G1 && s.erupeConfig.RealClientMode <= _config.G5 { + bf.WriteUint8(uint8(len(fullName))) + bf.WriteBytes(fullName) } else { - bf.WriteUint8(0) // Prevents malformed server name - combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) - combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) - bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) + if s.erupeConfig.RealClientMode >= _config.G51 { + bf.WriteUint8(0) // Ignored + } + bf.WriteBytes(stringsupport.PaddedString(string(fullName), 65, false)) } if s.erupeConfig.RealClientMode >= _config.GG { @@ -68,31 +62,31 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } for channelIdx, ci := range si.Channels { - sid = (4096 + serverIdx*256) + (16 + channelIdx) - if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 { - bf.WriteUint16(_config.ErupeConfig.ProxyPort) + sid := (serverIdx<<8 | 4096) + (channelIdx | 16) + if _config.ErupeConfig.DebugOptions.ProxyPort != 0 { + bf.WriteUint16(_config.ErupeConfig.DebugOptions.ProxyPort) } else { bf.WriteUint16(ci.Port) } - bf.WriteUint16(16 + uint16(channelIdx)) + bf.WriteUint16(uint16(channelIdx | 16)) bf.WriteUint16(ci.MaxPlayers) var currentPlayers uint16 s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(¤tPlayers) bf.WriteUint16(currentPlayers) - bf.WriteUint16(0) // Unk - bf.WriteUint16(0) // Unk - bf.WriteUint16(0) // Unk - bf.WriteUint16(0) // Unk - bf.WriteUint16(0) // Unk - bf.WriteUint16(0) // Unk - bf.WriteUint16(319) // Unk - bf.WriteUint16(252) // Unk - bf.WriteUint16(248) // Unk - bf.WriteUint16(12345) // Unk + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(319) // Unk + bf.WriteUint16(254 - currentPlayers) // Unk + bf.WriteUint16(255 - currentPlayers) // Unk + bf.WriteUint16(12345) } } bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix())) - bf.WriteUint32(0x0000003C) + bf.WriteUint32(60) return bf.Data() } @@ -136,7 +130,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte { } rawServerData := encodeServerInfo(config, s, local) - if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages { + if s.erupeConfig.DebugOptions.LogOutboundMessages { fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData)) } @@ -161,14 +155,14 @@ func makeUsrResp(pkt []byte, s *Server) []byte { var sid uint16 err := s.db.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid) if err != nil { - resp.WriteBytes(make([]byte, 4)) + resp.WriteUint16(0) } else { resp.WriteUint16(sid) - resp.WriteUint16(0) } + resp.WriteUint16(0) } - if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages { + if s.erupeConfig.DebugOptions.LogOutboundMessages { fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data())) } diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go index 751862a49..e8e0e944c 100644 --- a/server/signserver/dbutils.go +++ b/server/signserver/dbutils.go @@ -198,17 +198,17 @@ func (s *Server) checkToken(uid uint32) (bool, error) { } func (s *Server) registerUidToken(uid uint32) (uint32, string, error) { - token := token.Generate(16) + _token := token.Generate(16) var tid uint32 - err := s.db.QueryRow(`INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id`, uid, token).Scan(&tid) - return tid, token, err + err := s.db.QueryRow(`INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id`, uid, _token).Scan(&tid) + return tid, _token, err } func (s *Server) registerPsnToken(psn string) (uint32, string, error) { - token := token.Generate(16) + _token := token.Generate(16) var tid uint32 - err := s.db.QueryRow(`INSERT INTO sign_sessions (psn_id, token) VALUES ($1, $2) RETURNING id`, psn, token).Scan(&tid) - return tid, token, err + err := s.db.QueryRow(`INSERT INTO sign_sessions (psn_id, token) VALUES ($1, $2) RETURNING id`, psn, _token).Scan(&tid) + return tid, _token, err } func (s *Server) validateToken(token string, tokenID uint32) bool { @@ -229,9 +229,9 @@ func (s *Server) validateLogin(user string, pass string) (uint32, RespID) { var passDB string err := s.db.QueryRow(`SELECT id, password FROM users WHERE username = $1`, user).Scan(&uid, &passDB) if err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { s.logger.Info("User not found", zap.String("User", user)) - if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.AutoCreateAccount { + if s.erupeConfig.AutoCreateAccount { uid, err = s.registerDBAccount(user, pass) if err == nil { return uid, SIGN_SUCCESS @@ -244,6 +244,15 @@ func (s *Server) validateLogin(user string, pass string) (uint32, RespID) { return 0, SIGN_EABORT } else { if bcrypt.CompareHashAndPassword([]byte(passDB), []byte(pass)) == nil { + var bans int + err = s.db.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires IS NULL`, uid).Scan(&bans) + if err == nil && bans > 0 { + return uid, SIGN_EELIMINATE + } + err = s.db.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires > now()`, uid).Scan(&bans) + if err == nil && bans > 0 { + return uid, SIGN_ESUSPEND + } return uid, SIGN_SUCCESS } return 0, SIGN_EPASS diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 34cc371e2..715299f8f 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -72,7 +72,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte { bf.WriteUint32(char.ID) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR { + if s.server.erupeConfig.DebugOptions.MaxLauncherHR { bf.WriteUint16(999) } else { bf.WriteUint16(char.HRP) @@ -144,20 +144,38 @@ func (s *Session) makeSignResponse(uid uint32) []byte { s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser) bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true)) } - bf.WriteUint16(0xCA10) - bf.WriteUint16(0x4E20) - ps.Uint16(bf, "", false) // unk key - bf.WriteUint8(0x00) - bf.WriteUint16(0xCA11) - bf.WriteUint16(0x0001) - bf.WriteUint16(0x4E20) - ps.Uint16(bf, "", false) // unk ipv4 + + bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[0]) + if s.server.erupeConfig.DebugOptions.CapLink.Values[0] == 51728 { + bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[1]) + if s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20000 || s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20002 { + ps.Uint16(bf, s.server.erupeConfig.DebugOptions.CapLink.Key, false) + } + } + caStruct := []struct { + Unk0 uint8 + Unk1 uint32 + Unk2 string + }{} + bf.WriteUint8(uint8(len(caStruct))) + for i := range caStruct { + bf.WriteUint8(caStruct[i].Unk0) + bf.WriteUint32(caStruct[i].Unk1) + ps.Uint8(bf, caStruct[i].Unk2, false) + } + bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[2]) + bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[3]) + bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[4]) + if s.server.erupeConfig.DebugOptions.CapLink.Values[2] == 51729 && s.server.erupeConfig.DebugOptions.CapLink.Values[3] == 1 && s.server.erupeConfig.DebugOptions.CapLink.Values[4] == 20000 { + ps.Uint16(bf, fmt.Sprintf(`%s:%d`, s.server.erupeConfig.DebugOptions.CapLink.Host, s.server.erupeConfig.DebugOptions.CapLink.Port), false) + } + bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix())) bf.WriteUint32(0) tickets := []uint32{ - s.server.erupeConfig.GameplayOptions.MezfesSoloTickets, - s.server.erupeConfig.GameplayOptions.MezfesGroupTickets, + s.server.erupeConfig.GameplayOptions.MezFesSoloTickets, + s.server.erupeConfig.GameplayOptions.MezFesGroupTickets, } stalls := []uint8{ 10, 3, 6, 9, 4, 8, 5, 7, diff --git a/server/signserver/session.go b/server/signserver/session.go index 5806f1108..e4cbd5537 100644 --- a/server/signserver/session.go +++ b/server/signserver/session.go @@ -37,7 +37,7 @@ type Session struct { func (s *Session) work() { pkt, err := s.cryptConn.ReadPacket() - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages { + if s.server.erupeConfig.DebugOptions.LogInboundMessages { fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt)) } @@ -54,7 +54,7 @@ func (s *Session) handlePacket(pkt []byte) error { bf := byteframe.NewByteFrameFromBytes(pkt) reqType := string(bf.ReadNullTerminatedBytes()) switch reqType[:len(reqType)-3] { - case "DLTSKEYSIGN:", "DSGN:": + case "DLTSKEYSIGN:", "DSGN:", "SIGN:": s.handleDSGN(bf) case "PS3SGN:": s.client = PS3 @@ -78,7 +78,7 @@ func (s *Session) handlePacket(pkt []byte) error { } default: s.logger.Warn("Unknown request", zap.String("reqType", reqType)) - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages { + if s.server.erupeConfig.DebugOptions.LogInboundMessages { fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt)) } } @@ -102,7 +102,7 @@ func (s *Session) authenticate(username string, password string) { default: bf.WriteUint8(uint8(resp)) } - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages { + if s.server.erupeConfig.DebugOptions.LogOutboundMessages { fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(bf.Data()), hex.Dump(bf.Data())) } _ = s.cryptConn.SendPacket(bf.Data()) diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index b6cc03515..5a0a6c8e7 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -80,7 +80,7 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3 PatchServer: s.erupeConfig.SignV2.PatchServer, Notices: []string{}, } - if s.erupeConfig.DevModeOptions.MaxLauncherHR { + if s.erupeConfig.DebugOptions.MaxLauncherHR { for i := range resp.Characters { resp.Characters[i].HR = 7 } @@ -93,8 +93,8 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3 ID: uint32(channelserver.TimeWeekStart().Unix()), Start: uint32(channelserver.TimeWeekStart().Add(-time.Duration(s.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix()), End: uint32(channelserver.TimeWeekNext().Unix()), - SoloTickets: s.erupeConfig.GameplayOptions.MezfesSoloTickets, - GroupTickets: s.erupeConfig.GameplayOptions.MezfesGroupTickets, + SoloTickets: s.erupeConfig.GameplayOptions.MezFesSoloTickets, + GroupTickets: s.erupeConfig.GameplayOptions.MezFesGroupTickets, Stalls: stalls, } if !s.erupeConfig.HideLoginNotice { @@ -226,7 +226,7 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) return } - if s.erupeConfig.DevModeOptions.MaxLauncherHR { + if s.erupeConfig.DebugOptions.MaxLauncherHR { character.HR = 7 } w.Header().Add("Content-Type", "application/json")