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/.github/workflows/go.yml b/.github/workflows/go.yml index 955febd9a..e69ee1705 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -1,6 +1,15 @@ name: Build -on: [push] +on: + push: + paths: + - 'common/**' + - 'config/**' + - 'network/**' + - 'server/**' + - 'go.mod' + - 'go.sum' + - 'main.go' jobs: build: 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 ede542a20..b9be1fb05 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,13 @@ - PlayStation 3 - PlayStation Vita - Wii U (Up to Z2) -### Versions -- ZZ -- Z2 -- Z1 +### Versions (ClientMode) +- All versions after HR compression (G10-ZZ) have been tested extensively and have great functionality. +- All versions available on Wii U (G3-Z2) have been tested and should have good functionality. +- The second oldest found version is Forward.4 (FW.4), this version has basic functionality. +- The oldest found version is Season 6.0 (S6.0), however functionality is very limited. + +If you have an **installed** copy of Monster Hunter Frontier on an old hard drive, **please** get in contact so we can archive it! ## Setup @@ -29,119 +32,21 @@ 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) -- [PewPewDojo Discord](https://discord.gg/CFnzbhQ) - -## Configuration -This portion of the documentation goes over the `config.json` file. - -### General Configuration - -| Variable | Description | Default | Options | -|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------------------------------| -| Host | The IP or host address the server is running from | 127.0.0.1 | | -| BinPath | The bin path folder is where you place files needed for various parts of the game such as scenario and quest files | bin | | -| Language | This is the language the server will run in. Only English `en` and Japanese `ja` are available, if you wish to contribute to tranlation, get in touch | en | en/jp | -| DisableSoftCrash | | false | | -| HideLoginNotice | This hides the notices that appear on login from `LoginNotices` | true | | -| LoginNotices | This is where you place notices for users, you can have multiple notices | | | -| PatchServerManifest | | | | -| PatchServerFile | | | | -| ScreenshotAPIURL | This is the URL you want user sreenshots to go to | | | -| DeleteOnSaveCorruption | This option deletes a users save from the database if they corrupt it, can be used as punishment for cheaters | false | | -| ClientMode | This tells the server what client version it should target | ZZ | Check compatible versions above | -| DevMode | This enables DevModeOptions to be configured | true | | - -### `DevModeOptions` Configuraiton - -| Variable | Description | Default | Options | -|----------------------|---------------------------------------------------------------------------------------------|----------|----------------------------------| -| AutoCreateAccount | This allows users that don't exist to auto create there account from initial login | true | | -| CleanDB | This cleans the database down | false | | -| MaxLauncherHR | This sets the launcher value to HR7 to allow you to break World HR requirements | false | | -| LogInboundMessages | This will allow inbound messages to be logged to stdout | false | | -| LogOutboundMessages | This will allow outbound messages to be logged to stdout | false | | -| MaxHexdumpLength | This is the maximum amount of hex bytes that will be dumped to stdout | 0 | | -| DivaEvent | This overrides the Diva event stage in game | 2 | 0/1/2/3/-1 | -| FestaEvent | This overrides the Hunter Festival event stage in game | 2 | 0/1/2/3/-1 | -| TournamentEvent | This overrides the Hunter Tournament event stage in game | 2 | 0/1/2/3/-1 | -| MezFesEvent | Enables whether the MezFes event & World are active | true | | -| MezFesAlt | Switches the multiplayer MezFes event | false | | -| DisableTokenCheck | This disables the random token that is generated at login from being checked, very insecure | false | | -| QuestDebugTools | Enable various quest debug logs | false | | -| EarthStatusOverride | Enables Pallone Fest, Tower and Conquest War events | 0 | 2=Conquest, 11=Pallone, 21=Tower | -| EarthIDOverride | A random event ID | 0 | | -| EarthMonsterOverride | Sets the ID of the monster targeted in the Conquest War | 0 | | -| SaveDumps.Enables | Enables save dumps to a folder that is set at `SaveDumps.OutputDir` | true | | -| SaveDumps.OutputDir | The folder that save dumps are saved to | savedata | | - -### `GameplayOptions` Configuraiton - -| Variable | Description | Default | Options | -|----------------------|-----------------------------------------------------------------------------|---------|---------| -| FeaturedWeapons | Number of Active Feature weapons to generate daily | 0 | | -| MaximumNP | Maximum number of NP held by a player | 100000 | | -| MaximumRP | Maximum number of RP held by a player | 100000 | | -| DisableLoginBoost | Disables the Login Boost system | false | | -| DisableBoostTime | Disables the daily NetCafe Boost Time | false | | -| BoostTimeDuration | The number of minutes NetCafe Boost Time lasts for | 120 | | -| GuildMealDuration | The number of minutes a Guild Meal can be activated for after cooking | 60 | | -| BonusQuestAllowance | Number of Bonus Point Quests to allow daily | 3 | | -| DailyQuestAllowance | Number of Daily Quests to allow daily | 1 | | -| MezfesSoloTickets | Number of solo tickets given weekly | 10 | | -| MezfesGroupTickets | Number of group tickets given weekly | 4 | | -| GUrgentRate | Adjusts the rate of G Urgent quests spawning | 10 | | -| GCPMultiplier | Adjusts the multiplier of GCP rewarded for quest completion | 1.00 | | -| GRPMultiplier | Adjusts the multiplier of G Rank Points rewarded for quest completion | 1.00 | | -| GSRPMultiplier | Adjusts the multiplier of G Skill Rank Points rewarded for quest completion | 1.00 | | -| GZennyMultiplier | Adjusts the multiplier of G Zenny rewarded for quest completion | 1.00 | | -| MaterialMultiplier | Adjusts the multiplier of Monster Materials rewarded for quest completion | 1.00 | | -| ExtraCarves | Grant n extra chances to carve ALL carcasses | 0 | | -| DisableHunterNavi | Disables the Hunter Navi | false | | -| EnableHiganjimaEvent | Enables the Higanjima event in the Rasta Bar | false | | -| EnableNierEvent | Enables the Nier event in the Rasta Bar | false | | -| DisableRoad | Disables the Hunting Road | false | | - -### Discord -There is limited Discord capability in Erupe. The feature allows you to replay messages from your server into a channel. -This may be either be removed or revamped in a future version. - -### Commands -There are several chat commands that can be turned on and off. Most of them are really for admins or debugging purposes. - -| Name | command | Description | Options | -|----------|----------------|--------------------------------------------|---------------------| -| Rights | !rights VALUE | Sets the rights integer for your account | | -| Teleport | !tele X,Y | Teleports user to specific x,y coordinate | | -| Reload | !reload | Reloads all users and character objects | | -| KeyQuest | !kqf FLAGS | Sets the Key Quest Flag for your character | | -| Course | !course OPTION | Enables/Disables a course for your account | HL,EX,Premium,Boost | -| PSN | !psn USERNAME | Links the specified PSN to your account | | - -### Ravi Sub Commands -| Name | command | Description | -|----------|----------------------------------|-------------------------------| -| Raviente | !ravi start | Starts Ravi Event | -| Raviente | !ravi cm / !ravi checkmultiplier | Checks Ravi Damage Multiplier | -| Raviente | !ravi ss | Send Sedation Support | -| Raviente | !ravi sr | Send Resurrection Support | -| Raviente | !ravi rs | Request Sedation Support | - - -## World `Entries` config - -| Config Item | Description | Options | -|-------------|------------------|------------------------------------------------------------| -| Type | Server type. | 1=Normal, 2=Cities, 3=Newbie, 4=Tavern, 5=Return, 6=MezFes | -| Season | Server activity. | 0=Green/Breeding, 1=Orange/Warm, 2=Blue/Cold | - -### `Recommend` -This sets the types of quest that can be ordered from a world. -* 0 = All quests -* 1 = Up to 2 star quests -* 2 = Up to 4 star quests -* 4 = All Quests in HR (Enables G Experience Tab) -* 5 = Only G rank quests -* 6 = Mini games world there is no place to order quests \ No newline at end of file +- [Mezeporta Square Discord](https://discord.gg/DnwcpXM488) 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/bundled-schema/FPointItems.sql b/bundled-schema/FPointItems.sql deleted file mode 100644 index 918d81e54..000000000 --- a/bundled-schema/FPointItems.sql +++ /dev/null @@ -1,391 +0,0 @@ -BEGIN; - -INSERT INTO fpoint_items (item_type, item_id, quantity, fpoints, trade_type) VALUES -(7,8895,1,500,0), -(7,8891,1,300,0), -(7,8892,1,300,0), -(7,8893,1,300,0), -(7,8894,1,300,0), -(7,8890,1,10,0), -(7,10354,1,500,0), -(7,11983,1,300,0), -(7,11984,1,300,0), -(7,11985,1,300,0), -(7,11986,1,300,0), -(7,12524,1,500,0), -(7,12470,1,300,0), -(7,12471,1,300,0), -(7,12472,1,300,0), -(7,12473,1,300,0), -(7,2158,2,1,0), -(7,14548,1,500,0), -(7,9509,1,1,0), -(7,9510,1,1,0), -(7,9511,1,1,0), -(7,9512,1,1,0), -(7,9513,1,1,0), -(7,9514,1,1,0), -(7,9515,1,1,0), -(7,10753,1,1,0), -(7,10754,1,1,0), -(7,10755,1,1,0), -(7,10756,1,1,0), -(7,10757,1,1,0), -(7,10758,1,1,0), -(7,10759,1,1,0), -(7,11296,1,1,0), -(7,11297,1,1,0), -(7,11298,1,1,0), -(7,11299,1,1,0), -(7,11300,1,1,0), -(7,12386,1,1,0), -(7,12387,1,1,0), -(7,12388,1,1,0), -(7,12389,1,1,0), -(7,12390,1,1,0), -(7,13034,1,1,0), -(7,13035,1,1,0), -(7,13036,1,1,0), -(7,13037,1,1,0), -(7,13038,1,1,0), -(7,14179,1,1,0), -(7,14180,1,1,0), -(7,14181,1,1,0), -(7,14182,1,1,0), -(7,14183,1,1,0), -(7,13422,1,1,0), -(7,13423,1,1,0), -(7,13424,1,1,0), -(7,13425,1,1,0), -(7,13426,1,1,0), -(7,13427,1,1,0), -(7,9796,1,3,0), -(7,9700,1,3,0), -(7,10380,1,3,0), -(7,10810,1,3,0), -(7,10811,1,3,0), -(7,11436,1,3,0), -(7,9509,1,1,0), -(7,9510,1,1,0), -(7,9511,1,1,0), -(7,9512,1,1,0), -(7,9513,1,1,0), -(7,9514,1,1,0), -(7,9515,1,1,0), -(7,10753,1,1,0), -(7,10754,1,1,0), -(7,10755,1,1,0), -(7,10756,1,1,0), -(7,10757,1,1,0), -(7,10758,1,1,0), -(7,10759,1,1,0), -(7,11296,1,1,0), -(7,11297,1,1,0), -(7,11298,1,1,0), -(7,11299,1,1,0), -(7,11300,1,1,0), -(7,12509,1,3,0), -(7,12386,1,1,0), -(7,12387,1,1,0), -(7,12388,1,1,0), -(7,12389,1,1,0), -(7,12390,1,1,0), -(7,12872,1,3,0), -(7,12873,1,3,0), -(7,12840,1,1,0), -(7,12841,1,1,0), -(7,12874,1,1,0), -(7,12875,1,1,0), -(7,13191,1,3,0), -(7,13177,1,3,0), -(7,13326,1,3,0), -(7,13034,1,1,0), -(7,13035,1,1,0), -(7,13036,1,1,0), -(7,13037,1,1,0), -(7,13038,1,1,0), -(7,13178,1,3,0), -(7,13453,1,3,0), -(7,13449,1,3,0), -(7,13450,1,3,0), -(7,13404,1,3,0), -(7,13422,1,1,0), -(7,13423,1,1,0), -(7,13424,1,1,0), -(7,13425,1,1,0), -(7,13426,1,1,0), -(7,13427,1,1,0), -(7,13791,1,3,0), -(7,14006,1,3,0), -(7,14031,1,3,0), -(7,14032,1,3,0), -(7,13960,1,3,0), -(7,14029,1,3,0), -(7,13956,1,1,0), -(7,13958,1,1,0), -(7,13957,1,1,0), -(7,13959,1,1,0), -(7,13790,1,3,0), -(7,14005,1,3,0), -(7,14010,1,3,0), -(7,14009,1,3,0), -(7,14008,1,3,0), -(7,13965,1,3,0), -(7,14028,1,3,0), -(7,13963,1,3,0), -(7,14026,1,3,0), -(7,13964,1,3,0), -(7,14027,1,3,0), -(7,14069,1,3,0), -(7,14124,1,3,0), -(7,14065,1,1,0), -(7,14066,1,1,0), -(7,14067,1,1,0), -(7,14068,1,1,0), -(7,13962,1,3,0), -(7,14125,1,3,0), -(7,14089,1,3,0), -(7,14090,1,3,0), -(7,14091,1,3,0), -(7,14092,1,3,0), -(7,14194,1,3,0), -(7,14191,1,3,0), -(7,14198,1,3,0), -(7,14197,1,3,0), -(7,14179,1,1,0), -(7,14180,1,1,0), -(7,14181,1,1,0), -(7,14182,1,1,0), -(7,14183,1,1,0), -(7,14196,1,3,0), -(7,14195,1,3,0), -(7,14193,1,3,0), -(7,14192,1,3,0), -(7,14407,1,3,0), -(7,14414,1,3,0), -(7,14406,1,3,0), -(7,14413,1,3,0), -(7,14416,1,3,0), -(7,14549,1,3,0), -(7,14550,1,3,0), -(7,14502,1,3,0), -(7,14507,1,3,0), -(7,14501,1,3,0), -(7,14506,1,3,0), -(7,14500,1,3,0), -(7,14505,1,3,0), -(7,14498,1,3,0), -(7,14659,1,3,0), -(7,14660,1,3,0), -(7,14657,1,1,0), -(7,14658,1,1,0), -(7,11420,1,3,0), -(7,14704,1,3,0), -(7,11288,1,1,0), -(7,11289,1,1,0), -(7,11290,1,1,0), -(7,11291,1,1,0), -(7,10750,1,3,0), -(7,14705,1,3,0), -(7,10633,1,1,0), -(7,10634,1,1,0), -(7,10635,1,1,0), -(7,10636,1,1,0), -(7,14662,1,3,0), -(7,14663,1,3,0), -(7,14665,1,3,0), -(7,14666,1,3,0), -(7,14667,1,3,0), -(7,14668,1,3,0), -(7,14669,1,3,0), -(7,14670,1,3,0), -(7,14671,1,3,0), -(7,14672,1,3,0), -(7,14673,1,3,0), -(7,14674,1,3,0), -(7,14675,1,3,0), -(7,14676,1,3,0), -(7,14677,1,3,0), -(7,14678,1,3,0), -(7,14679,1,3,0), -(7,14680,1,3,0), -(7,14681,1,3,0), -(7,14682,1,3,0), -(7,14683,1,3,0), -(7,14684,1,3,0), -(7,14685,1,3,0), -(7,14686,1,3,0), -(7,14687,1,3,0), -(7,14688,1,3,0), -(7,14689,1,3,0), -(7,14690,1,3,0), -(7,14691,1,3,0), -(7,14692,1,3,0), -(7,14693,1,3,0), -(7,14694,1,3,0), -(7,14695,1,3,0), -(7,14696,1,3,0), -(7,14697,1,3,0), -(7,14698,1,3,0), -(7,14699,1,3,0), -(7,14700,1,3,0), -(7,14314,1,3,0), -(7,14503,1,3,0), -(7,14510,1,3,0), -(7,14904,1,3,0), -(7,14906,1,3,0), -(7,14910,1,1,0), -(7,14912,1,1,0), -(7,14905,1,3,0), -(7,14907,1,3,0), -(7,14911,1,1,0), -(7,14909,1,1,0), -(7,14855,1,3,0), -(7,14894,1,3,0), -(7,14913,1,3,0), -(7,14914,1,3,0), -(7,14891,1,3,0), -(7,14895,1,3,0), -(7,15027,1,3,0), -(7,15028,1,3,0), -(7,15026,1,1,0), -(7,15025,1,1,0), -(7,15024,1,1,0), -(7,15023,1,1,0), -(7,15064,1,3,0), -(7,15065,1,3,0), -(7,15030,1,3,0), -(7,15031,1,3,0), -(7,15062,1,3,0), -(7,15063,1,3,0), -(7,15066,1,3,0), -(7,15067,1,3,0), -(7,15061,1,3,0), -(7,15060,1,3,0), -(7,1227,1,2,0), -(7,13176,1,2,0), -(7,4360,1,2,0), -(7,4358,1,1,0), -(7,15118,1,3,0), -(7,15119,1,3,0), -(7,15113,1,3,0), -(7,15114,1,3,0), -(7,15115,1,3,0), -(7,15116,1,3,0), -(7,15220,1,3,0), -(7,15221,1,3,0), -(7,14126,1,3,0), -(7,15222,1,3,0), -(7,15223,1,3,0), -(7,15224,1,3,0), -(7,15225,1,3,0), -(7,15524,1,3,0), -(7,15525,1,3,0), -(7,15507,1,3,0), -(7,15508,1,3,0), -(7,15285,1,3,0), -(7,15286,1,3,0), -(7,15281,1,1,0), -(7,15282,1,1,0), -(7,15283,1,1,0), -(7,15284,1,1,0), -(7,15776,1,3,0), -(7,15777,1,3,0), -(7,15774,1,3,0), -(7,15775,1,3,0), -(7,15823,1,3,0), -(7,15824,1,3,0), -(7,15343,1,3,0), -(7,15342,1,3,0), -(7,15341,1,3,0), -(7,15340,1,3,0), -(7,15339,1,3,0), -(7,15338,1,3,0), -(7,15337,1,3,0), -(7,15336,1,3,0), -(7,15335,1,3,0), -(7,15334,1,3,0), -(7,15333,1,3,0), -(7,15332,1,3,0), -(7,15331,1,3,0), -(7,15330,1,3,0), -(7,15329,1,3,0), -(7,15328,1,3,0), -(7,15327,1,3,0), -(7,15326,1,3,0), -(7,15325,1,3,0), -(7,15324,1,3,0), -(7,15323,1,3,0), -(7,15322,1,3,0), -(7,15321,1,3,0), -(7,15314,1,3,0), -(7,15312,1,3,0), -(7,15311,1,3,0), -(7,15306,1,3,0), -(7,15307,1,3,0), -(7,15308,1,3,0), -(7,15309,1,3,0), -(7,15310,1,3,0), -(7,15305,1,3,0), -(7,15304,1,3,0), -(7,15303,1,3,0), -(7,15302,1,3,0), -(7,15301,1,3,0), -(7,15300,1,3,0), -(7,15299,1,3,0), -(7,15298,1,3,0), -(7,15297,1,3,0), -(7,15296,1,3,0), -(7,15295,1,3,0), -(7,15293,1,3,0), -(7,15294,1,3,0), -(7,15292,1,3,0), -(7,15291,1,3,0), -(7,15290,1,3,0), -(7,15289,1,3,0), -(7,15315,1,3,0), -(7,15316,1,3,0), -(7,15317,1,3,0), -(7,15318,1,3,0), -(7,15319,1,3,0), -(7,15320,1,3,0), -(7,15819,1,3,0), -(7,15820,1,3,0), -(7,15821,1,3,0), -(7,15822,1,3,0), -(7,16450,1,3,0), -(7,16451,1,3,0), -(7,16459,1,1,0), -(7,16460,1,1,0), -(7,16461,1,1,0), -(7,16462,1,1,0), -(7,16463,1,1,0), -(7,16464,1,1,0), -(7,16465,1,1,0), -(7,16466,1,1,0), -(7,16467,1,1,0), -(7,16468,1,1,0), -(7,16469,1,1,0), -(7,16470,1,1,0), -(7,16471,1,1,0), -(7,16472,1,1,0), -(7,16454,1,3,0), -(7,16455,1,3,0), -(7,16442,1,3,0), -(7,16443,1,3,0), -(7,16342,1,3,0), -(7,16343,1,3,0), -(7,16444,1,3,0), -(7,16445,1,3,0), -(7,16344,1,3,0), -(7,16345,1,3,0), -(7,16352,1,3,0), -(7,16353,1,3,0), -(7,16446,1,3,0), -(7,16447,1,3,0), -(7,16448,1,3,0), -(7,16449,1,3,0), -(7,16348,1,3,0), -(7,16349,1,3,0); - -END; \ No newline at end of file diff --git a/bundled-schema/OtherShops.sql b/bundled-schema/OtherShops.sql deleted file mode 100644 index d25e453b0..000000000 --- a/bundled-schema/OtherShops.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -INSERT INTO public.shop_items -(shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis) -VALUES - (5,5,16516,100,1,0,0,1,0,0,0,0), - (5,5,16517,100,1,0,0,1,0,0,0,0), - (7,0,13190,10,1,0,0,0,0,0,0,0), - (7,0,1662,10,1,0,0,0,0,0,0,0), - (7,0,10179,100,1,0,0,0,0,0,0,0); - -END; \ No newline at end of file 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/mhfmon/mhfmon.go b/common/mhfmon/mhfmon.go new file mode 100644 index 000000000..e192844fe --- /dev/null +++ b/common/mhfmon/mhfmon.go @@ -0,0 +1,366 @@ +package mhfmon + +const ( + Mon0 = iota + Rathian + Fatalis + Kelbi + Mosswine + Bullfango + YianKutKu + LaoShanLung + Cephadrome + Felyne + VeggieElder + Rathalos + Aptonoth + Genprey + Diablos + Khezu + Velociprey + Gravios + Mon18 + Vespoid + Gypceros + Plesioth + Basarios + Melynx + Hornetaur + Apceros + Monoblos + Velocidrome + Gendrome + Mon29 + Ioprey + Iodrome + Mon32 + Kirin + Cephalos + Giaprey + CrimsonFatalis + PinkRathian + BlueYianKutKu + PurpleGypceros + YianGaruga + SilverRathalos + GoldRathian + BlackDiablos + WhiteMonoblos + RedKhezu + GreenPlesioth + BlackGravios + DaimyoHermitaur + AzureRathalos + AshenLaoShanLung + Blangonga + Congalala + Rajang + KushalaDaora + ShenGaoren + GreatThunderbug + Shakalaka + YamaTsukami + Chameleos + RustedKushalaDaora + Blango + Conga + Remobra + Lunastra + Teostra + Hermitaur + ShogunCeanataur + Bulldrome + Anteka + Popo + WhiteFatalis + Mon72 + Ceanataur + Hypnocatrice + Lavasioth + Tigrex + Akantor + BrightHypnoc + RedLavasioth + Espinas + BurningEspinas + WhiteHypnoc + AqraVashimu + AqraJebia + Berukyurosu + Mon86 + Mon87 + Mon88 + Pariapuria + PearlEspinas + KamuOrugaron + NonoOrugaron + Raviente + Dyuragaua + Doragyurosu + Gurenzeburu + Burukku + Erupe + Rukodiora + Unknown + Gogomoa + Kokomoa + TaikunZamuza + Abiorugu + Kuarusepusu + Odibatorasu + Disufiroa + Rebidiora + Anorupatisu + Hyujikiki + Midogaron + Giaorugu + MiRu + Farunokku + Pokaradon + Shantien + Pokara + Mon118 + Goruganosu + Aruganosu + Baruragaru + Zerureusu + Gougarf + Uruki + Forokururu + Meraginasu + Diorex + GarubaDaora + Inagami + Varusaburosu + Poborubarumu + Block1Duremudira + Mon133 + Mon134 + Mon135 + Mon136 + Mon137 + Mon138 + Gureadomosu + Harudomerugu + Toridcless + Gasurabazura + Kusubami + YamaKurai + Block2Duremudira + Zinogre + Deviljho + Brachydios + BerserkRaviente + ToaTesukatora + Barioth + Uragaan + StygianZinogre + Guanzorumu + SavageDeviljho + Mon156 + Egyurasu + Voljang + Nargacuga + Keoaruboru + Zenaserisu + GoreMagala + BlinkingNargacuga + ShagaruMagala + Amatsu + Eruzerion + MusouDuremudira + Mon168 + Seregios + Bogabadorumu + Mon171 + MusouBogabadorumu + CostumedUruki + MusouZerureusu + Rappy + KingShakalaka +) + +type Monster struct { + Name string + Large bool +} + +var Monsters = []Monster{ + {"Mon0", false}, + {"Rathian", true}, + {"Fatalis", true}, + {"Kelbi", false}, + {"Mosswine", false}, + {"Bullfango", false}, + {"Yian Kut-Ku", true}, + {"Lao-Shan Lung", true}, + {"Cephadrome", true}, + {"Felyne", false}, + {"Veggie Elder", false}, + {"Rathalos", true}, + {"Aptonoth", false}, + {"Genprey", false}, + {"Diablos", true}, + {"Khezu", true}, + {"Velociprey", false}, + {"Gravios", true}, + {"Mon18", false}, + {"Vespoid", false}, + {"Gypceros", true}, + {"Plesioth", true}, + {"Basarios", true}, + {"Melynx", false}, + {"Hornetaur", false}, + {"Apceros", false}, + {"Monoblos", true}, + {"Velocidrome", true}, + {"Gendrome", true}, + {"Mon29", false}, + {"Ioprey", false}, + {"Iodrome", true}, + {"Mon32", false}, + {"Kirin", true}, + {"Cephalos", false}, + {"Giaprey", false}, + {"Crimson Fatalis", true}, + {"Pink Rathian", true}, + {"Blue Yian Kut-Ku", true}, + {"Purple Gypceros", true}, + {"Yian Garuga", true}, + {"Silver Rathalos", true}, + {"Gold Rathian", true}, + {"Black Diablos", true}, + {"White Monoblos", true}, + {"Red Khezu", true}, + {"Green Plesioth", true}, + {"Black Gravios", true}, + {"Daimyo Hermitaur", true}, + {"Azure Rathalos", true}, + {"Ashen Lao-Shan Lung", true}, + {"Blangonga", true}, + {"Congalala", true}, + {"Rajang", true}, + {"Kushala Daora", true}, + {"Shen Gaoren", true}, + {"Great Thunderbug", false}, + {"Shakalaka", false}, + {"Yama Tsukami", true}, + {"Chameleos", true}, + {"Rusted Kushala Daora", true}, + {"Blango", false}, + {"Conga", false}, + {"Remobra", false}, + {"Lunastra", true}, + {"Teostra", true}, + {"Hermitaur", false}, + {"Shogun Ceanataur", true}, + {"Bulldrome", true}, + {"Anteka", false}, + {"Popo", false}, + {"White Fatalis", true}, + {"Mon72", false}, + {"Ceanataur", false}, + {"Hypnocatrice", true}, + {"Lavasioth", true}, + {"Tigrex", true}, + {"Akantor", true}, + {"Bright Hypnocatrice", true}, + {"Red Lavasioth", true}, + {"Espinas", true}, + {"Burning Espinas", true}, + {"White Hypnocatrice", true}, + {"Aqra Vashimu", true}, + {"Aqra Jebia", true}, + {"Berukyurosu", true}, + {"Mon86", false}, + {"Mon87", false}, + {"Mon88", false}, + {"Pariapuria", true}, + {"Pearl Espinas", true}, + {"Kamu Orugaron", true}, + {"Nono Orugaron", true}, + {"Raviente", true}, // + Violent + {"Dyuragaua", true}, + {"Doragyurosu", true}, + {"Gurenzeburu", true}, + {"Burukku", false}, + {"Erupe", false}, + {"Rukodiora", true}, + {"Unknown", true}, + {"Gogomoa", true}, + {"Kokomoa", false}, + {"Taikun Zamuza", true}, + {"Abiorugu", true}, + {"Kuarusepusu", true}, + {"Odibatorasu", true}, + {"Disufiroa", true}, + {"Rebidiora", true}, + {"Anorupatisu", true}, + {"Hyujikiki", true}, + {"Midogaron", true}, + {"Giaorugu", true}, + {"Mi-Ru", true}, // + Musou + {"Farunokku", true}, + {"Pokaradon", true}, + {"Shantien", true}, + {"Pokara", false}, + {"Mon118", false}, + {"Goruganosu", true}, + {"Aruganosu", true}, + {"Baruragaru", true}, + {"Zerureusu", true}, + {"Gougarf", true}, // Both + {"Uruki", false}, + {"Forokururu", true}, + {"Meraginasu", true}, + {"Diorex", true}, + {"Garuba Daora", true}, + {"Inagami", true}, + {"Varusablos", true}, + {"Poborubarumu", true}, + {"1st Block Duremudira", true}, + {"Mon133", false}, + {"Mon134", false}, + {"Mon135", false}, + {"Mon136", false}, + {"Mon137", false}, + {"Mon138", false}, + {"Gureadomosu", true}, + {"Harudomerugu", true}, + {"Toridcless", true}, + {"Gasurabazura", true}, + {"Kusubami", false}, + {"Yama Kurai", true}, + {"2nd Block Duremudira", true}, + {"Zinogre", true}, + {"Deviljho", true}, + {"Brachydios", true}, + {"Berserk Raviente", true}, + {"Toa Tesukatora", true}, + {"Barioth", true}, + {"Uragaan", true}, + {"Stygian Zinogre", true}, + {"Guanzorumu", true}, + {"Savage Deviljho", true}, // + Starving/Heavenly + {"Mon156", false}, + {"Egyurasu", false}, + {"Voljang", true}, + {"Nargacuga", true}, + {"Keoaruboru", true}, + {"Zenaserisu", true}, + {"Gore Magala", true}, + {"Blinking Nargacuga", true}, + {"Shagaru Magala", true}, + {"Amatsu", true}, + {"Eruzerion", true}, // + Musou + {"Musou Duremudira", true}, + {"Mon168", false}, + {"Seregios", true}, + {"Bogabadorumu", true}, + {"Mon171", false}, + {"Musou Bogabadorumu", true}, + {"Costumed Uruki", false}, + {"Musou Zerureusu", true}, + {"Rappy", false}, + {"King Shakalaka", false}, +} diff --git a/common/stringstack/stringstack.go b/common/stringstack/stringstack.go index 5f936e6a2..a7ddef31e 100644 --- a/common/stringstack/stringstack.go +++ b/common/stringstack/stringstack.go @@ -6,13 +6,12 @@ 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 func New() *StringStack { - return &StringStack{Locked: false} + return &StringStack{} } // Set sets up a new 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) @@ -41,11 +26,12 @@ func (s *StringStack) Push(v string) { // Pop pops a string from the stack. func (s *StringStack) Pop() (string, error) { + var x string if len(s.stack) == 0 { - return "", errors.New("no items on stack") + return x, errors.New("no items on stack") } - x := s.stack[len(s.stack)-1] + x = s.stack[len(s.stack)-1] s.stack = s.stack[:len(s.stack)-1] return x, nil diff --git a/common/stringsupport/string_convert.go b/common/stringsupport/string_convert.go index 452c85321..de4d04364 100644 --- a/common/stringsupport/string_convert.go +++ b/common/stringsupport/string_convert.go @@ -69,7 +69,7 @@ func CSVRemove(csv string, v int) string { func CSVContains(csv string, v int) bool { s := strings.Split(csv, ",") for i := 0; i < len(s); i++ { - j, _ := strconv.ParseInt(s[i], 10, 64) + j, _ := strconv.ParseInt(s[i], 10, 32) if int(j) == v { return true } @@ -92,7 +92,7 @@ func CSVElems(csv string) []int { } s := strings.Split(csv, ",") for i := 0; i < len(s); i++ { - j, _ := strconv.ParseInt(s[i], 10, 64) + j, _ := strconv.ParseInt(s[i], 10, 32) r = append(r, int(j)) } return r diff --git a/config.json b/config.json index 6a9a1748b..48e166824 100644 --- a/config.json +++ b/config.json @@ -12,27 +12,37 @@ "ScreenshotAPIURL": "", "DeleteOnSaveCorruption": false, "ClientMode": "ZZ", - "DevMode": true, - "DevModeOptions": { - "AutoCreateAccount": true, + "QuestCacheExpiry": 300, + "CommandPrefix": "!", + "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": true, - "LogOutboundMessages": true, + "LogInboundMessages": false, + "LogOutboundMessages": false, + "LogMessageData": false, "MaxHexdumpLength": 256, - "DivaEvent": 0, - "FestaEvent": -1, - "TournamentEvent": -1, - "MezFesEvent": true, - "MezFesAlt": false, + "DivaOverride": 0, + "FestaOverride": -1, + "TournamentOverride": 0, "DisableTokenCheck": false, - "QuestDebugTools": false, - "EarthStatusOverride": 0, - "EarthIDOverride": 0, - "EarthMonsterOverride": 0, - "SaveDumps": { - "Enabled": true, - "OutputDir": "save-backups" + "QuestTools": false, + "AutoQuestBackport": true, + "ProxyPort": 0, + "CapLink": { + "Values": [51728, 20000, 51729, 1, 20000], + "Key": "", + "Host": "", + "Port": 80 } }, "GameplayOptions": { @@ -40,28 +50,47 @@ "MaximumNP": 100000, "MaximumRP": 50000, "MaximumFP": 120000, + "TreasureHuntExpiry": 604800, "DisableLoginBoost": false, "DisableBoostTime": false, - "BoostTimeDuration": 120, - "GuildMealDuration": 60, + "BoostTimeDuration": 7200, + "ClanMealDuration": 3600, + "ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]], "BonusQuestAllowance": 3, "DailyQuestAllowance": 1, - "MezfesSoloTickets": 10, - "MezfesGroupTickets": 4, "LowLatencyRaviente": false, "RegularRavienteMaxPlayers": 8, "ViolentRavienteMaxPlayers": 8, "BerserkRavienteMaxPlayers": 32, "ExtremeRavienteMaxPlayers": 32, "SmallBerserkRavienteMaxPlayers": 8, - "GUrgentRate": 10, + "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, "EnableHiganjimaEvent": false, "EnableNierEvent": false, @@ -71,37 +100,68 @@ "Discord": { "Enabled": false, "BotToken": "", - "RealtimeChannelID": "" + "RelayChannel": { + "Enabled": false, + "MaxMessageLength": 183, + "RelayChannelID": "" + } }, "Commands": [ { + "Name": "Help", + "Enabled": true, + "Description": "Show enabled chat commands", + "Prefix": "help" + }, { "Name": "Rights", "Enabled": false, + "Description": "Overwrite the Rights value on your account", "Prefix": "rights" }, { "Name": "Raviente", "Enabled": true, + "Description": "Various Raviente siege commands", "Prefix": "ravi" }, { "Name": "Teleport", "Enabled": false, + "Description": "Teleport to specified coordinates", "Prefix": "tele" }, { "Name": "Reload", "Enabled": true, + "Description": "Reload all players in your Land", "Prefix": "reload" }, { "Name": "KeyQuest", "Enabled": false, + "Description": "Overwrite your HR Key Quest progress", "Prefix": "kqf" }, { "Name": "Course", "Enabled": true, + "Description": "Toggle Courses on your account", "Prefix": "course" }, { "Name": "PSN", "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": [ @@ -130,7 +190,11 @@ }, "SignV2": { "Enabled": false, - "Port": 8080 + "Port": 8080, + "PatchServer": "", + "Banners": [], + "Messages": [], + "Links": [] }, "Channel": { "Enabled": true diff --git a/config/config.go b/config/config.go index b5afc6798..b0094f628 100644 --- a/config/config.go +++ b/config/config.go @@ -58,7 +58,7 @@ const ( ) var versionStrings = []string{"S1.0", "S1.5", "S2.0", "S2.5", "S3.0", "S3.5", "S4.0", "S5.0", "S5.5", "S6.0", "S7.0", - "S8.0", "S8.5", "S9", "S10", "FW.1", "FW.2", "FW.3", "FW.4", "FW.5", "G1", "G2", "G3", "G3.1", "G3.2", "GG", "G5", + "S8.0", "S8.5", "S9.0", "S10", "FW.1", "FW.2", "FW.3", "FW.4", "FW.5", "G1", "G2", "G3", "G3.1", "G3.2", "GG", "G5", "G5.1", "G5.2", "G6", "G6.1", "G7", "G8", "G8.1", "G9", "G9.1", "G10", "G10.1", "Z1", "Z2", "ZZ"} func (m Mode) String() string { @@ -79,74 +79,105 @@ type Config struct { DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion ClientMode string RealClientMode Mode - 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 - 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 - MezFesEvent bool // MezFes status - MezFesAlt bool // Swaps out Volpakkun for Tokotoko - 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 + QuestCacheExpiry int // Number of seconds to keep quest data cached + CommandPrefix string // The prefix for commands + 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 { - Enabled bool - OutputDir string + Enabled bool + RawEnabled bool + 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 - MaximumNP int // Maximum number of NP held by a player - MaximumRP uint16 // Maximum number of RP held by a player - MaximumFP uint32 // Maximum number of FP held by a player - DisableLoginBoost bool // Disables the Login Boost system - DisableBoostTime bool // Disables the daily NetCafe Boost Time - BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for - GuildMealDuration int // The number of minutes a Guild Meal can be activated for after cooking - 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 + FeaturedWeapons int // Number of Active Feature weapons to generate daily + MaximumNP int // Maximum number of NP held by a player + MaximumRP uint16 // Maximum number of RP held by a player + MaximumFP uint32 // Maximum number of FP held by a player + TreasureHuntExpiry uint32 // Seconds until a Clan Treasure Hunt will expire + TreasureHuntPartnyaCooldown uint32 // Seconds until a Partnya can be assigned to another Clan Treasure Hunt + DisableLoginBoost bool // Disables the Login Boost system + DisableBoostTime bool // Disables the daily NetCafe Boost Time + BoostTimeDuration int // Second that the NetCafe Boost Time lasts + ClanMealDuration int // Second that a Clan Meal can be activated for after cooking + 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 + LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive RegularRavienteMaxPlayers uint8 ViolentRavienteMaxPlayers uint8 BerserkRavienteMaxPlayers uint8 ExtremeRavienteMaxPlayers uint8 SmallBerserkRavienteMaxPlayers uint8 - GUrgentRate uint16 // Adjusts the rate of G Urgent quests spawning + 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 EnableHiganjimaEvent bool // Enables the Higanjima event in the Rasta Bar EnableNierEvent bool // Enables the Nier event in the Rasta Bar @@ -156,16 +187,23 @@ 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 type Command struct { - Name string - Enabled bool - Prefix string + Name string + Enabled bool + Description string + Prefix string } // Course represents a course within MHF @@ -191,8 +229,30 @@ type Sign struct { // SignV2 holds the new sign server config type SignV2 struct { - Enabled bool - Port int + Enabled bool + Port int + PatchServer string + Banners []SignV2Banner + Messages []SignV2Message + Links []SignV2Link +} + +type SignV2Banner struct { + Src string `json:"src"` // Displayed image URL + Link string `json:"link"` // Link accessed on click +} + +type SignV2Message struct { + Message string `json:"message"` // Displayed message + Date int64 `json:"date"` // Displayed date + Kind int `json:"kind"` // 0 for 'Default', 1 for 'New' + Link string `json:"link"` // Link accessed on click +} + +type SignV2Link struct { + Name string `json:"name"` // Displayed name + Icon string `json:"icon"` // Displayed icon. It will be cast as a monochrome color as long as it is transparent. + Link string `json:"link"` // Link accessed on click } type Channel struct { 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 93c8f83ad..5047c6609 100644 --- a/go.mod +++ b/go.mod @@ -4,32 +4,35 @@ go 1.21 require ( github.com/bwmarrin/discordgo v0.27.1 - github.com/gorilla/handlers v1.5.1 - github.com/gorilla/mux v1.8.0 + github.com/gorilla/handlers v1.5.2 + github.com/gorilla/mux v1.8.1 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.9 - github.com/spf13/viper v1.16.0 - go.uber.org/zap v1.25.0 - golang.org/x/crypto v0.12.0 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 - golang.org/x/text v0.12.0 + github.com/spf13/viper v1.17.0 + go.uber.org/zap v1.26.0 + golang.org/x/crypto v0.17.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/text v0.14.0 ) require ( - github.com/felixge/httpsnoop v1.0.3 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.9 // indirect - github.com/spf13/afero v1.9.5 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.11.0 // indirect + golang.org/x/net v0.18.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 9078343f0..d2cc39776 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -51,21 +49,21 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -127,13 +125,13 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= +github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -162,30 +160,34 @@ github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRU github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= -github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -209,8 +211,8 @@ go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -218,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.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +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= @@ -230,8 +232,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -287,6 +289,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -341,9 +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.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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= @@ -353,8 +356,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -498,8 +501,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/main.go b/main.go index f549f4bc8..a7d368930 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,6 @@ func cleanDB(db *sqlx.DB) { _ = db.MustExec("DELETE FROM guild_characters") _ = db.MustExec("DELETE FROM guilds") _ = db.MustExec("DELETE FROM characters") - _ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM users") } @@ -46,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") @@ -96,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") @@ -124,11 +125,14 @@ func main() { logger.Info("Database: Started successfully") // Clear stale data - _ = db.MustExec("DELETE FROM sign_sessions") + 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) 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_acquire_festa.go b/network/mhfpacket/msg_mhf_acquire_festa.go index 30e75d7a4..87cf758f0 100644 --- a/network/mhfpacket/msg_mhf_acquire_festa.go +++ b/network/mhfpacket/msg_mhf_acquire_festa.go @@ -1,19 +1,19 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfAcquireFesta represents the MSG_MHF_ACQUIRE_FESTA type MsgMhfAcquireFesta struct { - AckHandle uint32 - FestaID uint32 - GuildID uint32 - Unk uint16 + AckHandle uint32 + FestaID uint32 + GuildID uint32 + Unk uint8 } // Opcode returns the ID associated with this packet type. @@ -23,11 +23,12 @@ func (m *MsgMhfAcquireFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.FestaID = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - m.Unk = bf.ReadUint16() - return nil + m.AckHandle = bf.ReadUint32() + m.FestaID = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + m.Unk = bf.ReadUint8() + bf.ReadUint8() // Zeroed + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_acquire_guild_tresure.go b/network/mhfpacket/msg_mhf_acquire_guild_tresure.go index 775e98bf5..ac9feb557 100644 --- a/network/mhfpacket/msg_mhf_acquire_guild_tresure.go +++ b/network/mhfpacket/msg_mhf_acquire_guild_tresure.go @@ -12,7 +12,7 @@ import ( type MsgMhfAcquireGuildTresure struct { AckHandle uint32 HuntID uint32 - Unk uint8 + Unk bool } // Opcode returns the ID associated with this packet type. @@ -24,7 +24,7 @@ func (m *MsgMhfAcquireGuildTresure) Opcode() network.PacketID { func (m *MsgMhfAcquireGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.HuntID = bf.ReadUint32() - m.Unk = bf.ReadUint8() + m.Unk = bf.ReadBool() return nil } diff --git a/network/mhfpacket/msg_mhf_acquire_monthly_item.go b/network/mhfpacket/msg_mhf_acquire_monthly_item.go index acc10b42a..d64288f9f 100644 --- a/network/mhfpacket/msg_mhf_acquire_monthly_item.go +++ b/network/mhfpacket/msg_mhf_acquire_monthly_item.go @@ -11,9 +11,9 @@ import ( // MsgMhfAcquireMonthlyItem represents the MSG_MHF_ACQUIRE_MONTHLY_ITEM type MsgMhfAcquireMonthlyItem struct { AckHandle uint32 - Unk0 uint16 - Unk1 uint16 - Unk2 uint32 + Unk0 uint8 + Unk1 uint8 + Unk2 uint16 Unk3 uint32 } @@ -25,10 +25,11 @@ func (m *MsgMhfAcquireMonthlyItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() - m.Unk2 = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.Unk2 = bf.ReadUint16() m.Unk3 = bf.ReadUint32() + bf.ReadUint32() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_acquire_title.go b/network/mhfpacket/msg_mhf_acquire_title.go index fe3a5ca95..9b9dd84dc 100644 --- a/network/mhfpacket/msg_mhf_acquire_title.go +++ b/network/mhfpacket/msg_mhf_acquire_title.go @@ -11,9 +11,7 @@ import ( // MsgMhfAcquireTitle represents the MSG_MHF_ACQUIRE_TITLE type MsgMhfAcquireTitle struct { AckHandle uint32 - Unk0 uint16 - Unk1 uint16 - TitleID uint16 + TitleIDs []uint16 } // Opcode returns the ID associated with this packet type. @@ -24,9 +22,11 @@ func (m *MsgMhfAcquireTitle) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() - m.TitleID = bf.ReadUint16() + titles := int(bf.ReadUint16()) + bf.ReadUint16() // Zeroed + for i := 0; i < titles; i++ { + m.TitleIDs = append(m.TitleIDs, bf.ReadUint16()) + } return nil } diff --git a/network/mhfpacket/msg_mhf_announce.go b/network/mhfpacket/msg_mhf_announce.go index c4c9deb7f..2b1d9ea76 100644 --- a/network/mhfpacket/msg_mhf_announce.go +++ b/network/mhfpacket/msg_mhf_announce.go @@ -14,7 +14,7 @@ type MsgMhfAnnounce struct { IPAddress uint32 Port uint16 StageID []byte - Type uint8 + Data *byteframe.ByteFrame } // Opcode returns the ID associated with this packet type. @@ -31,8 +31,7 @@ func (m *MsgMhfAnnounce) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon _ = bf.ReadUint8() _ = bf.ReadUint8() m.StageID = bf.ReadBytes(32) - _ = bf.ReadUint32() - m.Type = bf.ReadUint8() + m.Data = byteframe.NewByteFrameFromBytes(bf.ReadBytes(uint(bf.ReadUint32()))) return nil } diff --git a/network/mhfpacket/msg_mhf_apply_dist_item.go b/network/mhfpacket/msg_mhf_apply_dist_item.go index 456d8fb12..a68354d2b 100644 --- a/network/mhfpacket/msg_mhf_apply_dist_item.go +++ b/network/mhfpacket/msg_mhf_apply_dist_item.go @@ -1,18 +1,20 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" + _config "erupe-ce/config" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfApplyDistItem represents the MSG_MHF_APPLY_DIST_ITEM type MsgMhfApplyDistItem struct { - AckHandle uint32 + AckHandle uint32 DistributionType uint8 - DistributionID uint32 - Unk2 uint32 - Unk3 uint32 + DistributionID uint32 + Unk2 uint32 + Unk3 uint32 } // Opcode returns the ID associated with this packet type. @@ -25,17 +27,16 @@ func (m *MsgMhfApplyDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clie m.AckHandle = bf.ReadUint32() m.DistributionType = bf.ReadUint8() m.DistributionID = bf.ReadUint32() - m.Unk2 = bf.ReadUint32() - m.Unk3 = bf.ReadUint32() + if _config.ErupeConfig.RealClientMode >= _config.G8 { + m.Unk2 = bf.ReadUint32() + } + if _config.ErupeConfig.RealClientMode >= _config.G10 { + m.Unk3 = bf.ReadUint32() + } return nil } // Build builds a binary packet from the current data. func (m *MsgMhfApplyDistItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint8(m.DistributionType) - bf.WriteUint32(m.DistributionID) - bf.WriteUint32(m.Unk2) - bf.WriteUint32(m.Unk3) - return nil -} \ No newline at end of file + return errors.New("NOT IMPLEMENTED") +} diff --git a/network/mhfpacket/msg_mhf_arrange_guild_member.go b/network/mhfpacket/msg_mhf_arrange_guild_member.go index 3bd4e73f0..066735a10 100644 --- a/network/mhfpacket/msg_mhf_arrange_guild_member.go +++ b/network/mhfpacket/msg_mhf_arrange_guild_member.go @@ -1,9 +1,10 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfArrangeGuildMember represents the MSG_MHF_ARRANGE_GUILD_MEMBER @@ -22,11 +23,11 @@ func (m *MsgMhfArrangeGuildMember) Opcode() network.PacketID { func (m *MsgMhfArrangeGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.GuildID = bf.ReadUint32() - charCount := bf.ReadUint16() - + bf.ReadUint8() // Zeroed + charCount := int(bf.ReadUint8()) m.CharIDs = make([]uint32, charCount) - for i := uint16(0); i < charCount; i++ { + for i := 0; i < charCount; i++ { m.CharIDs[i] = bf.ReadUint32() } @@ -35,13 +36,5 @@ func (m *MsgMhfArrangeGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx // Build builds a binary packet from the current data. func (m *MsgMhfArrangeGuildMember) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint32(m.GuildID) - bf.WriteUint16(uint16(len(m.CharIDs))) - - for _, charID := range m.CharIDs { - bf.WriteUint32(charID) - } - - return nil + return errors.New("NOT IMPLEMENTED") } 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/network/mhfpacket/msg_mhf_check_daily_cafepoint.go b/network/mhfpacket/msg_mhf_check_daily_cafepoint.go index 6e4c26b97..fee1ec222 100644 --- a/network/mhfpacket/msg_mhf_check_daily_cafepoint.go +++ b/network/mhfpacket/msg_mhf_check_daily_cafepoint.go @@ -1,9 +1,10 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfCheckDailyCafepoint represents the MSG_MHF_CHECK_DAILY_CAFEPOINT @@ -25,7 +26,5 @@ func (m *MsgMhfCheckDailyCafepoint) Parse(bf *byteframe.ByteFrame, ctx *clientct } func (m *MsgMhfCheckDailyCafepoint) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint32(m.Unk) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_check_monthly_item.go b/network/mhfpacket/msg_mhf_check_monthly_item.go index 860725aa4..257e0f855 100644 --- a/network/mhfpacket/msg_mhf_check_monthly_item.go +++ b/network/mhfpacket/msg_mhf_check_monthly_item.go @@ -12,7 +12,6 @@ import ( type MsgMhfCheckMonthlyItem struct { AckHandle uint32 Type uint8 - Unk []byte } // Opcode returns the ID associated with this packet type. @@ -24,7 +23,9 @@ func (m *MsgMhfCheckMonthlyItem) Opcode() network.PacketID { func (m *MsgMhfCheckMonthlyItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Type = bf.ReadUint8() - m.Unk = bf.ReadBytes(3) + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_check_weekly_stamp.go b/network/mhfpacket/msg_mhf_check_weekly_stamp.go index f03b1d1e7..069a17456 100644 --- a/network/mhfpacket/msg_mhf_check_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_check_weekly_stamp.go @@ -12,7 +12,6 @@ type MsgMhfCheckWeeklyStamp struct { AckHandle uint32 StampType string Unk1 bool - Unk2 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. @@ -31,7 +30,7 @@ func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.C m.StampType = "ex" } m.Unk1 = bf.ReadBool() - m.Unk2 = bf.ReadUint16() + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_create_guild.go b/network/mhfpacket/msg_mhf_create_guild.go index e37267885..e82f7157e 100644 --- a/network/mhfpacket/msg_mhf_create_guild.go +++ b/network/mhfpacket/msg_mhf_create_guild.go @@ -12,8 +12,6 @@ import ( // MsgMhfCreateGuild represents the MSG_MHF_CREATE_GUILD type MsgMhfCreateGuild struct { AckHandle uint32 - Unk0 uint8 - Unk1 uint8 Name string } @@ -25,9 +23,8 @@ func (m *MsgMhfCreateGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCreateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() - _ = bf.ReadUint16() // len + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Name length m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_mhf_create_joint.go b/network/mhfpacket/msg_mhf_create_joint.go index 1a969fc4a..5a9a9f5fd 100644 --- a/network/mhfpacket/msg_mhf_create_joint.go +++ b/network/mhfpacket/msg_mhf_create_joint.go @@ -1,19 +1,19 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/common/stringsupport" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/common/stringsupport" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfCreateJoint represents the MSG_MHF_CREATE_JOINT type MsgMhfCreateJoint struct { - AckHandle uint32 - GuildID uint32 - Name string + AckHandle uint32 + GuildID uint32 + Name string } // Opcode returns the ID associated with this packet type. @@ -23,11 +23,12 @@ func (m *MsgMhfCreateJoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCreateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - _ = bf.ReadUint32() // len - m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) - return nil + m.AckHandle = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Name length + m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_displayed_achievement.go b/network/mhfpacket/msg_mhf_displayed_achievement.go index 03de31f39..2633c081e 100644 --- a/network/mhfpacket/msg_mhf_displayed_achievement.go +++ b/network/mhfpacket/msg_mhf_displayed_achievement.go @@ -1,15 +1,14 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfDisplayedAchievement represents the MSG_MHF_DISPLAYED_ACHIEVEMENT -type MsgMhfDisplayedAchievement struct { - Unk0 uint8 -} +type MsgMhfDisplayedAchievement struct{} // Opcode returns the ID associated with this packet type. func (m *MsgMhfDisplayedAchievement) Opcode() network.PacketID { @@ -18,12 +17,11 @@ func (m *MsgMhfDisplayedAchievement) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfDisplayedAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.Unk0 = bf.ReadUint8() + bf.ReadUint8() // Zeroed return nil } // Build builds a binary packet from the current data. func (m *MsgMhfDisplayedAchievement) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint8(m.Unk0) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_entry_festa.go b/network/mhfpacket/msg_mhf_entry_festa.go index 9572cede6..de46b2d0e 100644 --- a/network/mhfpacket/msg_mhf_entry_festa.go +++ b/network/mhfpacket/msg_mhf_entry_festa.go @@ -1,18 +1,18 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEntryFesta represents the MSG_MHF_ENTRY_FESTA type MsgMhfEntryFesta struct { - AckHandle uint32 - FestaID uint32 - GuildID uint32 + AckHandle uint32 + FestaID uint32 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,11 +22,11 @@ func (m *MsgMhfEntryFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEntryFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.FestaID = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - _ = bf.ReadUint16() // Always 0 - return nil + m.AckHandle = bf.ReadUint32() + m.FestaID = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + bf.ReadUint16() // Zeroed + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_enumerate_dist_item.go b/network/mhfpacket/msg_mhf_enumerate_dist_item.go index 778a91ac6..d4164f1e5 100644 --- a/network/mhfpacket/msg_mhf_enumerate_dist_item.go +++ b/network/mhfpacket/msg_mhf_enumerate_dist_item.go @@ -1,17 +1,20 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" + _config "erupe-ce/config" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfEnumerateDistItem represents the MSG_MHF_ENUMERATE_DIST_ITEM type MsgMhfEnumerateDistItem struct { AckHandle uint32 - Unk0 uint8 - Unk1 uint16 + DistType uint8 + Unk1 uint8 Unk2 uint16 + Unk3 []byte } // Opcode returns the ID associated with this packet type. @@ -22,17 +25,16 @@ func (m *MsgMhfEnumerateDistItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateDistItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint16() - m.Unk2 = bf.ReadUint16() + m.DistType = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.Unk2 = bf.ReadUint16() // Maximum? Hardcoded to 256 + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + m.Unk3 = bf.ReadBytes(uint(bf.ReadUint8())) + } return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateDistItem) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint8(m.Unk0) - bf.WriteUint16(m.Unk1) - bf.WriteUint16(m.Unk2) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_enumerate_event.go b/network/mhfpacket/msg_mhf_enumerate_event.go index d73f92cdc..6a863f92f 100644 --- a/network/mhfpacket/msg_mhf_enumerate_event.go +++ b/network/mhfpacket/msg_mhf_enumerate_event.go @@ -1,16 +1,15 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfEnumerateEvent represents the MSG_MHF_ENUMERATE_EVENT type MsgMhfEnumerateEvent struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in the binary - Unk1 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. @@ -21,15 +20,12 @@ func (m *MsgMhfEnumerateEvent) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateEvent) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Zeroed return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateEvent) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint16(m.Unk0) - bf.WriteUint16(m.Unk1) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_enumerate_festa_member.go b/network/mhfpacket/msg_mhf_enumerate_festa_member.go index f0b5cc478..4b90589a9 100644 --- a/network/mhfpacket/msg_mhf_enumerate_festa_member.go +++ b/network/mhfpacket/msg_mhf_enumerate_festa_member.go @@ -1,18 +1,18 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateFestaMember represents the MSG_MHF_ENUMERATE_FESTA_MEMBER type MsgMhfEnumerateFestaMember struct { - AckHandle uint32 - FestaID uint32 - GuildID uint32 + AckHandle uint32 + FestaID uint32 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,11 +22,11 @@ func (m *MsgMhfEnumerateFestaMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateFestaMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() + m.AckHandle = bf.ReadUint32() m.FestaID = bf.ReadUint32() m.GuildID = bf.ReadUint32() - _ = bf.ReadUint16() // Hardcoded 0 in the binary. - return nil + bf.ReadUint16() // Zeroed + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_enumerate_guacot.go b/network/mhfpacket/msg_mhf_enumerate_guacot.go index 4fcdba523..71cae7adf 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guacot.go +++ b/network/mhfpacket/msg_mhf_enumerate_guacot.go @@ -2,7 +2,6 @@ package mhfpacket import ( "errors" - "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" @@ -11,9 +10,7 @@ import ( // MsgMhfEnumerateGuacot represents the MSG_MHF_ENUMERATE_GUACOT type MsgMhfEnumerateGuacot struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in binary - Unk1 uint16 // Hardcoded 0 in binary - Unk2 uint16 // Hardcoded 0 in binary + Unk0 uint32 } // Opcode returns the ID associated with this packet type. @@ -24,9 +21,8 @@ func (m *MsgMhfEnumerateGuacot) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() - m.Unk2 = bf.ReadUint16() + m.Unk0 = bf.ReadUint32() + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_guild.go b/network/mhfpacket/msg_mhf_enumerate_guild.go index f90a7cc7b..61ead7870 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild.go @@ -2,9 +2,7 @@ package mhfpacket import ( "errors" - "erupe-ce/common/bfutil" "erupe-ce/common/byteframe" - "erupe-ce/common/stringsupport" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -34,8 +32,8 @@ type MsgMhfEnumerateGuild struct { Type EnumerateGuildType Page uint8 Sorting bool - Data1 []byte - Data2 string + Data1 *byteframe.ByteFrame + Data2 *byteframe.ByteFrame } // Opcode returns the ID associated with this packet type. @@ -49,12 +47,12 @@ func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.Type = EnumerateGuildType(bf.ReadUint8()) m.Page = bf.ReadUint8() m.Sorting = bf.ReadBool() - _ = bf.ReadBytes(1) - m.Data1 = bf.ReadBytes(4) - _ = bf.ReadBytes(2) - lenData2 := uint(bf.ReadUint8()) - _ = bf.ReadBytes(1) - m.Data2 = stringsupport.SJISToUTF8(bfutil.UpToNull(bf.ReadBytes(lenData2))) + bf.ReadUint8() // Zeroed + m.Data1 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(4)) + bf.ReadUint16() // Zeroed + dataLen := uint(bf.ReadUint8()) + bf.ReadUint8() // Zeroed + m.Data2 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(dataLen)) return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_guild_item.go b/network/mhfpacket/msg_mhf_enumerate_guild_item.go index 4f538ca5d..6d3516371 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild_item.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild_item.go @@ -1,18 +1,17 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateGuildItem represents the MSG_MHF_ENUMERATE_GUILD_ITEM type MsgMhfEnumerateGuildItem struct { AckHandle uint32 - GuildId uint32 - Unk0 uint16 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,9 +21,10 @@ func (m *MsgMhfEnumerateGuildItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.GuildId = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() + m.AckHandle = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_guild_member.go b/network/mhfpacket/msg_mhf_enumerate_guild_member.go index f15de35f6..e90c431f2 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild_member.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild_member.go @@ -1,17 +1,17 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfEnumerateGuildMember represents the MSG_MHF_ENUMERATE_GUILD_MEMBER type MsgMhfEnumerateGuildMember struct { - AckHandle uint32 - Unk0 uint16 // Hardcoded 00 01 in the binary - Unk1 uint32 // Alliance related - GuildID uint32 + AckHandle uint32 + AllianceID uint32 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,17 +22,14 @@ func (m *MsgMhfEnumerateGuildMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint32() + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Always 1 + m.AllianceID = bf.ReadUint32() m.GuildID = bf.ReadUint32() return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateGuildMember) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint32(m.AckHandle) - bf.WriteUint16(m.Unk0) - bf.WriteUint32(m.Unk1) - bf.WriteUint32(m.GuildID) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go b/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go index 61475d655..f03202bd4 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild_tresure.go @@ -12,7 +12,8 @@ import ( type MsgMhfEnumerateGuildTresure struct { AckHandle uint32 MaxHunts uint16 - Unk uint32 + Unk0 uint16 + Unk1 uint16 } // Opcode returns the ID associated with this packet type. @@ -24,9 +25,8 @@ func (m *MsgMhfEnumerateGuildTresure) Opcode() network.PacketID { func (m *MsgMhfEnumerateGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.MaxHunts = bf.ReadUint16() - // Changes with MaxHunts - // 0 if MaxHunts = 1, 1 if MaxHunts = 30 - m.Unk = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_house.go b/network/mhfpacket/msg_mhf_enumerate_house.go index da6a25de7..41f57323a 100644 --- a/network/mhfpacket/msg_mhf_enumerate_house.go +++ b/network/mhfpacket/msg_mhf_enumerate_house.go @@ -14,7 +14,6 @@ type MsgMhfEnumerateHouse struct { AckHandle uint32 CharID uint32 Method uint8 - Unk uint16 Name string } @@ -28,7 +27,7 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.AckHandle = bf.ReadUint32() m.CharID = bf.ReadUint32() m.Method = bf.ReadUint8() - m.Unk = bf.ReadUint16() + bf.ReadUint16() // Zeroed lenName := bf.ReadUint8() if lenName > 0 { m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) diff --git a/network/mhfpacket/msg_mhf_enumerate_inv_guild.go b/network/mhfpacket/msg_mhf_enumerate_inv_guild.go index cf2057bed..994c374d8 100644 --- a/network/mhfpacket/msg_mhf_enumerate_inv_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_inv_guild.go @@ -10,8 +10,13 @@ import ( // MsgMhfEnumerateInvGuild represents the MSG_MHF_ENUMERATE_INV_GUILD type MsgMhfEnumerateInvGuild struct { - AckHandle uint32 - Unk []byte + AckHandle uint32 + Unk uint32 + Operation uint8 + ActiveHours uint8 + DaysActive uint8 + PlayStyle uint8 + GuildRequest uint8 } // Opcode returns the ID associated with this packet type. @@ -22,7 +27,12 @@ func (m *MsgMhfEnumerateInvGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk = bf.ReadBytes(9) + m.Unk = bf.ReadUint32() + m.Operation = bf.ReadUint8() + m.ActiveHours = bf.ReadUint8() + m.DaysActive = bf.ReadUint8() + m.PlayStyle = bf.ReadUint8() + m.GuildRequest = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_price.go b/network/mhfpacket/msg_mhf_enumerate_price.go index 5dcfa69f3..e36246364 100644 --- a/network/mhfpacket/msg_mhf_enumerate_price.go +++ b/network/mhfpacket/msg_mhf_enumerate_price.go @@ -1,18 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumeratePrice represents the MSG_MHF_ENUMERATE_PRICE type MsgMhfEnumeratePrice struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in the binary - Unk1 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. @@ -23,8 +21,8 @@ func (m *MsgMhfEnumeratePrice) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumeratePrice) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_ranking.go b/network/mhfpacket/msg_mhf_enumerate_ranking.go index 4baef5738..a891a0e12 100644 --- a/network/mhfpacket/msg_mhf_enumerate_ranking.go +++ b/network/mhfpacket/msg_mhf_enumerate_ranking.go @@ -1,18 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfEnumerateRanking represents the MSG_MHF_ENUMERATE_RANKING type MsgMhfEnumerateRanking struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in the binary - Unk1 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. @@ -23,8 +21,9 @@ func (m *MsgMhfEnumerateRanking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + bf.ReadUint16() // Zeroed + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_enumerate_shop.go b/network/mhfpacket/msg_mhf_enumerate_shop.go index 153095db4..d57655e98 100644 --- a/network/mhfpacket/msg_mhf_enumerate_shop.go +++ b/network/mhfpacket/msg_mhf_enumerate_shop.go @@ -14,7 +14,7 @@ type MsgMhfEnumerateShop struct { AckHandle uint32 ShopType uint8 // 1 running gachas, 10 normal shop extensions, 8 Diva Defense shop ShopID uint32 - Unk2 uint16 // 00 80 running gachas, 00 20 normal shop + Limit uint16 Unk3 uint8 Unk4 uint8 Unk5 uint32 @@ -30,7 +30,7 @@ func (m *MsgMhfEnumerateShop) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clie m.AckHandle = bf.ReadUint32() m.ShopType = bf.ReadUint8() m.ShopID = bf.ReadUint32() - m.Unk2 = bf.ReadUint16() + m.Limit = bf.ReadUint16() m.Unk3 = bf.ReadUint8() if _config.ErupeConfig.RealClientMode >= _config.G2 { m.Unk4 = bf.ReadUint8() diff --git a/network/mhfpacket/msg_mhf_enumerate_union_item.go b/network/mhfpacket/msg_mhf_enumerate_union_item.go index 780539b12..38ff89f5c 100644 --- a/network/mhfpacket/msg_mhf_enumerate_union_item.go +++ b/network/mhfpacket/msg_mhf_enumerate_union_item.go @@ -11,7 +11,6 @@ import ( // MsgMhfEnumerateUnionItem represents the MSG_MHF_ENUMERATE_UNION_ITEM type MsgMhfEnumerateUnionItem struct { AckHandle uint32 - Unk0 uint16 } // Opcode returns the ID associated with this packet type. @@ -22,8 +21,8 @@ func (m *MsgMhfEnumerateUnionItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go index 918a870ac..829bb6fb2 100644 --- a/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_exchange_weekly_stamp.go @@ -13,7 +13,6 @@ type MsgMhfExchangeWeeklyStamp struct { AckHandle uint32 StampType string Unk1 uint8 - Unk2 uint16 } // Opcode returns the ID associated with this packet type. @@ -32,7 +31,7 @@ func (m *MsgMhfExchangeWeeklyStamp) Parse(bf *byteframe.ByteFrame, ctx *clientct m.StampType = "ex" } m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint16() + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_get_achievement.go b/network/mhfpacket/msg_mhf_get_achievement.go index afa49d0d4..bca41bb7b 100644 --- a/network/mhfpacket/msg_mhf_get_achievement.go +++ b/network/mhfpacket/msg_mhf_get_achievement.go @@ -12,7 +12,6 @@ import ( type MsgMhfGetAchievement struct { AckHandle uint32 CharID uint32 - Unk1 uint32 // char? } // Opcode returns the ID associated with this packet type. @@ -24,7 +23,7 @@ func (m *MsgMhfGetAchievement) Opcode() network.PacketID { func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.CharID = bf.ReadUint32() - m.Unk1 = bf.ReadUint32() + bf.ReadUint32() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_get_rengoku_binary.go b/network/mhfpacket/msg_mhf_get_rengoku_binary.go index fc43a3718..f7dda97ad 100644 --- a/network/mhfpacket/msg_mhf_get_rengoku_binary.go +++ b/network/mhfpacket/msg_mhf_get_rengoku_binary.go @@ -1,17 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetRengokuBinary represents the MSG_MHF_GET_RENGOKU_BINARY type MsgMhfGetRengokuBinary struct { AckHandle uint32 - Unk0 uint8 // Hardcoded 0 in binary } // Opcode returns the ID associated with this packet type. @@ -22,7 +21,7 @@ func (m *MsgMhfGetRengokuBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetRengokuBinary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_info_festa.go b/network/mhfpacket/msg_mhf_info_festa.go index 6926f0b8d..0877e6a4c 100644 --- a/network/mhfpacket/msg_mhf_info_festa.go +++ b/network/mhfpacket/msg_mhf_info_festa.go @@ -1,18 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfInfoFesta represents the MSG_MHF_INFO_FESTA type MsgMhfInfoFesta struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in the binary - Unk1 uint16 // Hardcoded 0 in the binary + Unk0 uint8 } // Opcode returns the ID associated with this packet type. @@ -23,8 +22,10 @@ func (m *MsgMhfInfoFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfInfoFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + m.Unk0 = bf.ReadUint8() + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_info_joint.go b/network/mhfpacket/msg_mhf_info_joint.go index 17e468c7c..c349c9768 100644 --- a/network/mhfpacket/msg_mhf_info_joint.go +++ b/network/mhfpacket/msg_mhf_info_joint.go @@ -12,7 +12,6 @@ import ( type MsgMhfInfoJoint struct { AckHandle uint32 AllianceID uint32 - Unk uint32 } // Opcode returns the ID associated with this packet type. @@ -24,7 +23,7 @@ func (m *MsgMhfInfoJoint) Opcode() network.PacketID { func (m *MsgMhfInfoJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.AllianceID = bf.ReadUint32() - m.Unk = bf.ReadUint32() + bf.ReadUint32() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_list_mail.go b/network/mhfpacket/msg_mhf_list_mail.go index 2dfb351e9..645baf548 100644 --- a/network/mhfpacket/msg_mhf_list_mail.go +++ b/network/mhfpacket/msg_mhf_list_mail.go @@ -1,17 +1,16 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfListMail represents the MSG_MHF_LIST_MAIL type MsgMhfListMail struct { AckHandle uint32 - Unk0 uint32 } // Opcode returns the ID associated with this packet type. @@ -22,7 +21,8 @@ func (m *MsgMhfListMail) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfListMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_list_member.go b/network/mhfpacket/msg_mhf_list_member.go index 0eaf4ca5f..bee4a4874 100644 --- a/network/mhfpacket/msg_mhf_list_member.go +++ b/network/mhfpacket/msg_mhf_list_member.go @@ -1,17 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfListMember represents the MSG_MHF_LIST_MEMBER type MsgMhfListMember struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 01 00 in the JP client. + Unk0 uint8 // Hardcoded 01 in the JP client. } // Opcode returns the ID associated with this packet type. @@ -22,7 +22,8 @@ func (m *MsgMhfListMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfListMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() + m.Unk0 = bf.ReadUint8() + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_load_guild_cooking.go b/network/mhfpacket/msg_mhf_load_guild_cooking.go index 515187bc2..0e293c073 100644 --- a/network/mhfpacket/msg_mhf_load_guild_cooking.go +++ b/network/mhfpacket/msg_mhf_load_guild_cooking.go @@ -1,17 +1,17 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfLoadGuildCooking represents the MSG_MHF_LOAD_GUILD_COOKING -type MsgMhfLoadGuildCooking struct{ - AckHandle uint32 - MaxMeals uint8 +type MsgMhfLoadGuildCooking struct { + AckHandle uint32 + MaxMeals uint8 } // Opcode returns the ID associated with this packet type. @@ -22,7 +22,7 @@ func (m *MsgMhfLoadGuildCooking) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadGuildCooking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - _ = bf.ReadUint8() + m.MaxMeals = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_load_house.go b/network/mhfpacket/msg_mhf_load_house.go index a012b2f1c..138c8af22 100644 --- a/network/mhfpacket/msg_mhf_load_house.go +++ b/network/mhfpacket/msg_mhf_load_house.go @@ -16,7 +16,6 @@ type MsgMhfLoadHouse struct { Destination uint8 // False if already in hosts My Series, in case host updates PW CheckPass bool - Unk3 uint16 // Hardcoded 0 in binary Password string } @@ -31,8 +30,8 @@ func (m *MsgMhfLoadHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCo m.CharID = bf.ReadUint32() m.Destination = bf.ReadUint8() m.CheckPass = bf.ReadBool() - _ = bf.ReadUint16() - _ = bf.ReadUint8() // Password length + bf.ReadUint16() // Zeroed + bf.ReadUint8() // Password length m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_mhf_operate_guild_member.go b/network/mhfpacket/msg_mhf_operate_guild_member.go index 7aed7a21d..8daf82dc5 100644 --- a/network/mhfpacket/msg_mhf_operate_guild_member.go +++ b/network/mhfpacket/msg_mhf_operate_guild_member.go @@ -23,7 +23,6 @@ type MsgMhfOperateGuildMember struct { GuildID uint32 CharID uint32 Action uint8 - Unk []byte } // Opcode returns the ID associated with this packet type. @@ -37,7 +36,8 @@ func (m *MsgMhfOperateGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx m.GuildID = bf.ReadUint32() m.CharID = bf.ReadUint32() m.Action = bf.ReadUint8() - m.Unk = bf.ReadBytes(3) + bf.ReadUint8() // Zeroed + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go index 1fa360d01..eccb3139d 100644 --- a/network/mhfpacket/msg_mhf_operate_joint.go +++ b/network/mhfpacket/msg_mhf_operate_joint.go @@ -22,7 +22,8 @@ type MsgMhfOperateJoint struct { AllianceID uint32 GuildID uint32 Action OperateJointAction - UnkData *byteframe.ByteFrame + Data1 *byteframe.ByteFrame + Data2 *byteframe.ByteFrame } // Opcode returns the ID associated with this packet type. @@ -36,8 +37,9 @@ func (m *MsgMhfOperateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien m.AllianceID = bf.ReadUint32() m.GuildID = bf.ReadUint32() m.Action = OperateJointAction(bf.ReadUint8()) - m.UnkData = byteframe.NewByteFrameFromBytes(bf.DataFromCurrent()) - bf.Seek(int64(len(bf.Data())-2), 0) + dataLen := uint(bf.ReadUint8()) + m.Data1 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(4)) + m.Data2 = byteframe.NewByteFrameFromBytes(bf.ReadBytes(dataLen)) return nil } diff --git a/network/mhfpacket/msg_mhf_opr_member.go b/network/mhfpacket/msg_mhf_opr_member.go index 32641cb39..186ccc44d 100644 --- a/network/mhfpacket/msg_mhf_opr_member.go +++ b/network/mhfpacket/msg_mhf_opr_member.go @@ -1,20 +1,19 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfOprMember represents the MSG_MHF_OPR_MEMBER type MsgMhfOprMember struct { - AckHandle uint32 - Blacklist bool - Operation bool - Unk uint16 - CharID uint32 + AckHandle uint32 + Blacklist bool + Operation bool + CharIDs []uint32 } // Opcode returns the ID associated with this packet type. @@ -24,12 +23,15 @@ func (m *MsgMhfOprMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfOprMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.Blacklist = bf.ReadBool() - m.Operation = bf.ReadBool() - m.Unk = bf.ReadUint16() - m.CharID = bf.ReadUint32() - return nil + m.AckHandle = bf.ReadUint32() + m.Blacklist = bf.ReadBool() + m.Operation = bf.ReadBool() + bf.ReadUint8() + chars := int(bf.ReadUint8()) + for i := 0; i < chars; i++ { + m.CharIDs = append(m.CharIDs, bf.ReadUint32()) + } + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_oprt_mail.go b/network/mhfpacket/msg_mhf_oprt_mail.go index 2c9e06828..95ec561ee 100644 --- a/network/mhfpacket/msg_mhf_oprt_mail.go +++ b/network/mhfpacket/msg_mhf_oprt_mail.go @@ -11,10 +11,11 @@ import ( type OperateMailOperation uint8 const ( - OPERATE_MAIL_DELETE = 0x01 - OPERATE_MAIL_LOCK = 0x02 - OPERATE_MAIL_UNLOCK = 0x03 - OPERATE_MAIL_ACQUIRE_ITEM = 0x05 + OperateMailDelete = iota + 1 + OperateMailLock + OperateMailUnlock + OpreateMailNull + OperateMailAcquireItem ) // MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL @@ -23,7 +24,6 @@ type MsgMhfOprtMail struct { AccIndex uint8 Index uint8 Operation OperateMailOperation - Unk0 uint8 Data []byte Amount uint16 ItemID uint16 @@ -40,8 +40,8 @@ func (m *MsgMhfOprtMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon m.AccIndex = bf.ReadUint8() m.Index = bf.ReadUint8() m.Operation = OperateMailOperation(bf.ReadUint8()) - m.Unk0 = bf.ReadUint8() - if m.Operation == OPERATE_MAIL_ACQUIRE_ITEM { + bf.ReadUint8() // Zeroed + if m.Operation == OperateMailAcquireItem { m.Amount = bf.ReadUint16() m.ItemID = bf.ReadUint16() } diff --git a/network/mhfpacket/msg_mhf_play_free_gacha.go b/network/mhfpacket/msg_mhf_play_free_gacha.go index c96c5cefc..6d57124f3 100644 --- a/network/mhfpacket/msg_mhf_play_free_gacha.go +++ b/network/mhfpacket/msg_mhf_play_free_gacha.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfPlayFreeGacha represents the MSG_MHF_PLAY_FREE_GACHA -type MsgMhfPlayFreeGacha struct{} +type MsgMhfPlayFreeGacha struct { + AckHandle uint32 + GachaID uint32 + GachaType uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPlayFreeGacha) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgMhfPlayFreeGacha) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPlayFreeGacha) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.GachaID = bf.ReadUint32() + m.GachaType = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_post_seibattle.go b/network/mhfpacket/msg_mhf_post_seibattle.go index 7e3e578c4..9c9101747 100644 --- a/network/mhfpacket/msg_mhf_post_seibattle.go +++ b/network/mhfpacket/msg_mhf_post_seibattle.go @@ -1,15 +1,24 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfPostSeibattle represents the MSG_MHF_POST_SEIBATTLE -type MsgMhfPostSeibattle struct{} +type MsgMhfPostSeibattle struct { + AckHandle uint32 + Unk0 uint8 + Unk1 uint8 + Unk2 uint32 + Unk3 uint8 + Unk4 uint16 + Unk5 uint16 + Unk6 uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostSeibattle) Opcode() network.PacketID { @@ -18,7 +27,15 @@ func (m *MsgMhfPostSeibattle) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostSeibattle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint8() + m.Unk4 = bf.ReadUint16() + m.Unk5 = bf.ReadUint16() + m.Unk6 = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_read_mail.go b/network/mhfpacket/msg_mhf_read_mail.go index 1d2b03cf5..957f144f9 100644 --- a/network/mhfpacket/msg_mhf_read_mail.go +++ b/network/mhfpacket/msg_mhf_read_mail.go @@ -19,7 +19,6 @@ type MsgMhfReadMail struct { // This is the index within the current mail list Index uint8 - Unk0 uint16 } // Opcode returns the ID associated with this packet type. @@ -32,7 +31,7 @@ func (m *MsgMhfReadMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon m.AckHandle = bf.ReadUint32() m.AccIndex = bf.ReadUint8() m.Index = bf.ReadUint8() - m.Unk0 = bf.ReadUint16() + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_read_mercenary_w.go b/network/mhfpacket/msg_mhf_read_mercenary_w.go index 3aa9597d9..d70ef4f38 100644 --- a/network/mhfpacket/msg_mhf_read_mercenary_w.go +++ b/network/mhfpacket/msg_mhf_read_mercenary_w.go @@ -13,7 +13,6 @@ type MsgMhfReadMercenaryW struct { AckHandle uint32 Op uint8 Unk1 uint8 - Unk2 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. @@ -25,8 +24,9 @@ func (m *MsgMhfReadMercenaryW) Opcode() network.PacketID { func (m *MsgMhfReadMercenaryW) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Op = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint16() + m.Unk1 = bf.ReadUint8() // Supposed to be 0 or 1, but always 1 + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_register_event.go b/network/mhfpacket/msg_mhf_register_event.go index aaa5b51a8..46afb1a2e 100644 --- a/network/mhfpacket/msg_mhf_register_event.go +++ b/network/mhfpacket/msg_mhf_register_event.go @@ -1,20 +1,18 @@ package mhfpacket import ( + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgMhfRegisterEvent represents the MSG_MHF_REGISTER_EVENT type MsgMhfRegisterEvent struct { AckHandle uint32 Unk0 uint16 - Unk1 uint8 - Unk2 uint8 - Unk3 uint8 - Unk4 uint8 - Unk5 uint16 + WorldID uint16 + LandID uint16 + Unk1 bool } // Opcode returns the ID associated with this packet type. @@ -26,11 +24,10 @@ func (m *MsgMhfRegisterEvent) Opcode() network.PacketID { func (m *MsgMhfRegisterEvent) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint8() - m.Unk3 = bf.ReadUint8() - m.Unk4 = bf.ReadUint8() - m.Unk5 = bf.ReadUint16() + m.WorldID = bf.ReadUint16() + m.LandID = bf.ReadUint16() + m.Unk1 = bf.ReadBool() + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_release_event.go b/network/mhfpacket/msg_mhf_release_event.go index 08e507c81..20ebcdba0 100644 --- a/network/mhfpacket/msg_mhf_release_event.go +++ b/network/mhfpacket/msg_mhf_release_event.go @@ -11,8 +11,7 @@ import ( // MsgMhfReleaseEvent represents the MSG_MHF_RELEASE_EVENT type MsgMhfReleaseEvent struct { AckHandle uint32 - Unk0 uint32 - Unk1 uint32 + RaviID uint32 } // Opcode returns the ID associated with this packet type. @@ -23,8 +22,8 @@ func (m *MsgMhfReleaseEvent) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfReleaseEvent) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint32() + m.RaviID = bf.ReadUint32() + bf.ReadUint32() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_send_mail.go b/network/mhfpacket/msg_mhf_send_mail.go index e0f34ba54..2a21ef93b 100644 --- a/network/mhfpacket/msg_mhf_send_mail.go +++ b/network/mhfpacket/msg_mhf_send_mail.go @@ -15,7 +15,7 @@ type MsgMhfSendMail struct { RecipientID uint32 SubjectLength uint16 BodyLength uint16 - Quantity uint32 + Quantity uint16 ItemID uint16 Subject string Body string @@ -32,7 +32,8 @@ func (m *MsgMhfSendMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon m.RecipientID = bf.ReadUint32() m.SubjectLength = bf.ReadUint16() m.BodyLength = bf.ReadUint16() - m.Quantity = bf.ReadUint32() + bf.ReadUint16() // Zeroed + m.Quantity = bf.ReadUint16() m.ItemID = bf.ReadUint16() m.Subject = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) m.Body = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) diff --git a/network/mhfpacket/msg_mhf_set_guild_manage_right.go b/network/mhfpacket/msg_mhf_set_guild_manage_right.go index 3feed2654..9eb2d8b21 100644 --- a/network/mhfpacket/msg_mhf_set_guild_manage_right.go +++ b/network/mhfpacket/msg_mhf_set_guild_manage_right.go @@ -13,7 +13,6 @@ type MsgMhfSetGuildManageRight struct { AckHandle uint32 CharID uint32 Allowed bool - Unk []byte } // Opcode returns the ID associated with this packet type. @@ -26,7 +25,7 @@ func (m *MsgMhfSetGuildManageRight) Parse(bf *byteframe.ByteFrame, ctx *clientct m.AckHandle = bf.ReadUint32() m.CharID = bf.ReadUint32() m.Allowed = bf.ReadBool() - m.Unk = bf.ReadBytes(3) + bf.ReadBytes(3) // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_stampcard_stamp.go b/network/mhfpacket/msg_mhf_stampcard_stamp.go index 5ac650ad2..f9da9612e 100644 --- a/network/mhfpacket/msg_mhf_stampcard_stamp.go +++ b/network/mhfpacket/msg_mhf_stampcard_stamp.go @@ -34,7 +34,7 @@ func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.HR = bf.ReadUint16() m.GR = bf.ReadUint16() m.Stamps = bf.ReadUint16() - _ = bf.ReadUint16() + bf.ReadUint16() // Zeroed if _config.ErupeConfig.RealClientMode > _config.Z1 { m.Reward1 = uint16(bf.ReadUint32()) m.Reward2 = uint16(bf.ReadUint32()) diff --git a/network/mhfpacket/msg_mhf_state_festa_g.go b/network/mhfpacket/msg_mhf_state_festa_g.go index 86c6526bf..e356b98be 100644 --- a/network/mhfpacket/msg_mhf_state_festa_g.go +++ b/network/mhfpacket/msg_mhf_state_festa_g.go @@ -1,18 +1,18 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfStateFestaG represents the MSG_MHF_STATE_FESTA_G type MsgMhfStateFestaG struct { AckHandle uint32 - FestaID uint32 - GuildID uint32 + FestaID uint32 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -25,7 +25,7 @@ func (m *MsgMhfStateFestaG) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.AckHandle = bf.ReadUint32() m.FestaID = bf.ReadUint32() m.GuildID = bf.ReadUint32() - _ = bf.ReadUint16() // Hardcoded 0 in the binary. + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_state_festa_u.go b/network/mhfpacket/msg_mhf_state_festa_u.go index ef76498bd..013966dba 100644 --- a/network/mhfpacket/msg_mhf_state_festa_u.go +++ b/network/mhfpacket/msg_mhf_state_festa_u.go @@ -1,18 +1,18 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfStateFestaU represents the MSG_MHF_STATE_FESTA_U type MsgMhfStateFestaU struct { - AckHandle uint32 - FestaID uint32 - GuildID uint32 + AckHandle uint32 + FestaID uint32 + GuildID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,11 +22,11 @@ func (m *MsgMhfStateFestaU) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfStateFestaU) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.FestaID = bf.ReadUint32() - m.GuildID = bf.ReadUint32() - _ = bf.ReadUint16() // Hardcoded 0 in the binary. - return nil + m.AckHandle = bf.ReadUint32() + m.FestaID = bf.ReadUint32() + m.GuildID = bf.ReadUint32() + bf.ReadUint16() // Zeroed + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_transfer_item.go b/network/mhfpacket/msg_mhf_transfer_item.go index e3c2cc67c..69dfdb13f 100644 --- a/network/mhfpacket/msg_mhf_transfer_item.go +++ b/network/mhfpacket/msg_mhf_transfer_item.go @@ -1,11 +1,11 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfTransferItem represents the MSG_MHF_TRANSFER_ITEM @@ -15,8 +15,8 @@ type MsgMhfTransferItem struct { // correlate with any item IDs that would make sense to get after quests so // I have no idea what this actually does Unk0 uint32 - Unk1 uint16 // Hardcoded - Unk2 uint16 // Hardcoded + Unk1 uint8 + Unk2 uint16 } // Opcode returns the ID associated with this packet type. @@ -28,7 +28,8 @@ func (m *MsgMhfTransferItem) Opcode() network.PacketID { func (m *MsgMhfTransferItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint16() + m.Unk1 = bf.ReadUint8() + bf.ReadUint8() // Zeroed m.Unk2 = bf.ReadUint16() return nil } diff --git a/network/mhfpacket/msg_mhf_transit_message.go b/network/mhfpacket/msg_mhf_transit_message.go index 1d15c6d42..1442af08d 100644 --- a/network/mhfpacket/msg_mhf_transit_message.go +++ b/network/mhfpacket/msg_mhf_transit_message.go @@ -12,7 +12,6 @@ import ( type MsgMhfTransitMessage struct { AckHandle uint32 Unk0 uint8 - Unk1 uint8 SearchType uint16 MessageData []byte } @@ -26,7 +25,7 @@ func (m *MsgMhfTransitMessage) Opcode() network.PacketID { func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() + bf.ReadUint8() // Zeroed m.SearchType = bf.ReadUint16() m.MessageData = bf.ReadBytes(uint(bf.ReadUint16())) return nil diff --git a/network/mhfpacket/msg_mhf_unreserve_srg.go b/network/mhfpacket/msg_mhf_unreserve_srg.go index f273662aa..9f545dabd 100644 --- a/network/mhfpacket/msg_mhf_unreserve_srg.go +++ b/network/mhfpacket/msg_mhf_unreserve_srg.go @@ -1,15 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfUnreserveSrg represents the MSG_MHF_UNRESERVE_SRG -type MsgMhfUnreserveSrg struct{} +type MsgMhfUnreserveSrg struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfUnreserveSrg) Opcode() network.PacketID { @@ -18,7 +20,8 @@ func (m *MsgMhfUnreserveSrg) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUnreserveSrg) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_update_cafepoint.go b/network/mhfpacket/msg_mhf_update_cafepoint.go index 671e893aa..aea9a43aa 100644 --- a/network/mhfpacket/msg_mhf_update_cafepoint.go +++ b/network/mhfpacket/msg_mhf_update_cafepoint.go @@ -1,18 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfUpdateCafepoint represents the MSG_MHF_UPDATE_CAFEPOINT type MsgMhfUpdateCafepoint struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 0 in binary - Unk1 uint16 // Hardcoded 0 in binary } // Opcode returns the ID associated with this packet type. @@ -23,8 +21,8 @@ func (m *MsgMhfUpdateCafepoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateCafepoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_mhf_update_guacot.go b/network/mhfpacket/msg_mhf_update_guacot.go index 99aa215e2..729c84547 100644 --- a/network/mhfpacket/msg_mhf_update_guacot.go +++ b/network/mhfpacket/msg_mhf_update_guacot.go @@ -2,27 +2,23 @@ package mhfpacket import ( "errors" - "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" ) -type Gook struct { - Exists bool - Index uint32 - Type uint16 - Data []byte - NameLen uint8 - Name []byte +type Goocoo struct { + Index uint32 + Data1 []int16 + Data2 []uint32 + Name []byte } // MsgMhfUpdateGuacot represents the MSG_MHF_UPDATE_GUACOT type MsgMhfUpdateGuacot struct { AckHandle uint32 EntryCount uint16 - Unk0 uint16 // Hardcoded 0 in binary - Gooks []Gook + Goocoos []Goocoo } // Opcode returns the ID associated with this packet type. @@ -34,20 +30,18 @@ func (m *MsgMhfUpdateGuacot) Opcode() network.PacketID { func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.EntryCount = bf.ReadUint16() - m.Unk0 = bf.ReadUint16() + bf.ReadUint16() // Zeroed + var temp Goocoo for i := 0; i < int(m.EntryCount); i++ { - e := Gook{} - e.Index = bf.ReadUint32() - e.Type = bf.ReadUint16() - e.Data = bf.ReadBytes(50) - e.NameLen = bf.ReadUint8() - e.Name = bf.ReadBytes(uint(e.NameLen)) - if e.Type > 0 { - e.Exists = true - } else { - e.Exists = false + temp.Index = bf.ReadUint32() + for j := 0; j < 22; j++ { + temp.Data1 = append(temp.Data1, bf.ReadInt16()) } - m.Gooks = append(m.Gooks, e) + for j := 0; j < 2; j++ { + temp.Data2 = append(temp.Data2, bf.ReadUint32()) + } + temp.Name = bf.ReadBytes(uint(bf.ReadUint8())) + m.Goocoos = append(m.Goocoos, temp) } return nil } diff --git a/network/mhfpacket/msg_mhf_update_guild_icon.go b/network/mhfpacket/msg_mhf_update_guild_icon.go index 6e3a780e5..248bb93ea 100644 --- a/network/mhfpacket/msg_mhf_update_guild_icon.go +++ b/network/mhfpacket/msg_mhf_update_guild_icon.go @@ -25,8 +25,6 @@ type GuildIconMsgPart struct { type MsgMhfUpdateGuildIcon struct { AckHandle uint32 GuildID uint32 - PartCount uint16 - Unk1 uint16 IconParts []GuildIconMsgPart } @@ -39,12 +37,12 @@ func (m *MsgMhfUpdateGuildIcon) Opcode() network.PacketID { func (m *MsgMhfUpdateGuildIcon) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.GuildID = bf.ReadUint32() - m.PartCount = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + partCount := int(bf.ReadUint16()) + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + m.IconParts = make([]GuildIconMsgPart, partCount) - m.IconParts = make([]GuildIconMsgPart, m.PartCount) - - for i := 0; i < int(m.PartCount); i++ { + for i := 0; i < partCount; i++ { m.IconParts[i] = GuildIconMsgPart{ Index: bf.ReadUint16(), ID: bf.ReadUint16(), diff --git a/network/mhfpacket/msg_mhf_update_guild_item.go b/network/mhfpacket/msg_mhf_update_guild_item.go index 3eb37a8cb..ddd7ef6e5 100644 --- a/network/mhfpacket/msg_mhf_update_guild_item.go +++ b/network/mhfpacket/msg_mhf_update_guild_item.go @@ -10,7 +10,7 @@ import ( type Item struct { Unk0 uint32 - ItemId uint16 + ItemID uint16 Amount uint16 Unk1 uint32 } @@ -18,10 +18,8 @@ type Item struct { // MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM type MsgMhfUpdateGuildItem struct { AckHandle uint32 - GuildId uint32 - Amount uint16 - Unk1 uint16 // 0x00 0x00 - Items []Item // Array of updated item IDs + GuildID uint32 + Items []Item } // Opcode returns the ID associated with this packet type. @@ -32,14 +30,15 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GuildId = bf.ReadUint32() - m.Amount = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() - m.Items = make([]Item, int(m.Amount)) + m.GuildID = bf.ReadUint32() + itemCount := int(bf.ReadUint16()) + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + m.Items = make([]Item, itemCount) - for i := 0; i < int(m.Amount); i++ { + for i := 0; i < itemCount; i++ { m.Items[i].Unk0 = bf.ReadUint32() - m.Items[i].ItemId = bf.ReadUint16() + m.Items[i].ItemID = bf.ReadUint16() m.Items[i].Amount = bf.ReadUint16() m.Items[i].Unk1 = bf.ReadUint32() } diff --git a/network/mhfpacket/msg_mhf_update_house.go b/network/mhfpacket/msg_mhf_update_house.go index 320972673..2c6f0401d 100644 --- a/network/mhfpacket/msg_mhf_update_house.go +++ b/network/mhfpacket/msg_mhf_update_house.go @@ -13,8 +13,7 @@ import ( type MsgMhfUpdateHouse struct { AckHandle uint32 State uint8 - Unk1 uint8 // Always 0x01 - Unk2 uint16 // Always 0x0000 + Unk1 uint8 // Always 0x01 Password string } @@ -28,8 +27,9 @@ func (m *MsgMhfUpdateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.AckHandle = bf.ReadUint32() m.State = bf.ReadUint8() m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint16() - _ = bf.ReadUint8() // Password length + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Password length m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_mhf_update_myhouse_info.go b/network/mhfpacket/msg_mhf_update_myhouse_info.go index 469920127..c5bf26d7a 100644 --- a/network/mhfpacket/msg_mhf_update_myhouse_info.go +++ b/network/mhfpacket/msg_mhf_update_myhouse_info.go @@ -1,17 +1,18 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + _config "erupe-ce/config" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfUpdateMyhouseInfo represents the MSG_MHF_UPDATE_MYHOUSE_INFO type MsgMhfUpdateMyhouseInfo struct { AckHandle uint32 - Unk0 []byte + Data []byte } // Opcode returns the ID associated with this packet type. @@ -22,7 +23,16 @@ func (m *MsgMhfUpdateMyhouseInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateMyhouseInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadBytes(0x16A) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + m.Data = bf.ReadBytes(362) + } else if _config.ErupeConfig.RealClientMode >= _config.GG { + m.Data = bf.ReadBytes(338) + } else if _config.ErupeConfig.RealClientMode >= _config.F5 { + // G1 is a guess + m.Data = bf.ReadBytes(314) + } else { + m.Data = bf.ReadBytes(290) + } return nil } diff --git a/network/mhfpacket/msg_mhf_update_union_item.go b/network/mhfpacket/msg_mhf_update_union_item.go index 5e4d83d24..68e8de365 100644 --- a/network/mhfpacket/msg_mhf_update_union_item.go +++ b/network/mhfpacket/msg_mhf_update_union_item.go @@ -11,9 +11,7 @@ import ( // MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM type MsgMhfUpdateUnionItem struct { AckHandle uint32 - Amount uint16 - Unk1 uint16 // 0x00 0x00 - Items []Item // Array of updated item IDs + Items []Item } // Opcode returns the ID associated with this packet type. @@ -24,13 +22,14 @@ func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Amount = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() - m.Items = make([]Item, int(m.Amount)) + itemCount := int(bf.ReadUint16()) + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + m.Items = make([]Item, itemCount) - for i := 0; i < int(m.Amount); i++ { + for i := 0; i < itemCount; i++ { m.Items[i].Unk0 = bf.ReadUint32() - m.Items[i].ItemId = bf.ReadUint16() + m.Items[i].ItemID = bf.ReadUint16() m.Items[i].Amount = bf.ReadUint16() m.Items[i].Unk1 = bf.ReadUint32() } diff --git a/network/mhfpacket/msg_mhf_use_gacha_point.go b/network/mhfpacket/msg_mhf_use_gacha_point.go index 33a4ef143..5245b5eaf 100644 --- a/network/mhfpacket/msg_mhf_use_gacha_point.go +++ b/network/mhfpacket/msg_mhf_use_gacha_point.go @@ -1,19 +1,20 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfUseGachaPoint represents the MSG_MHF_USE_GACHA_POINT -type MsgMhfUseGachaPoint struct{ - AckHandle uint32 - Unk0 uint16 // padding? - TrialCoins uint32 - PremiumCoins uint32 +type MsgMhfUseGachaPoint struct { + AckHandle uint32 + Unk0 uint8 + Unk1 uint8 + TrialCoins uint32 + PremiumCoins uint32 } // Opcode returns the ID associated with this packet type. @@ -24,7 +25,8 @@ func (m *MsgMhfUseGachaPoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUseGachaPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() m.TrialCoins = bf.ReadUint32() m.PremiumCoins = bf.ReadUint32() return nil diff --git a/network/mhfpacket/msg_mhf_vote_festa.go b/network/mhfpacket/msg_mhf_vote_festa.go index b3c05d5fd..201c1b5ac 100644 --- a/network/mhfpacket/msg_mhf_vote_festa.go +++ b/network/mhfpacket/msg_mhf_vote_festa.go @@ -11,7 +11,7 @@ import ( // MsgMhfVoteFesta represents the MSG_MHF_VOTE_FESTA type MsgMhfVoteFesta struct { AckHandle uint32 - Unk uint32 + FestaID uint32 GuildID uint32 TrialID uint32 } @@ -24,7 +24,7 @@ func (m *MsgMhfVoteFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfVoteFesta) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk = bf.ReadUint32() + m.FestaID = bf.ReadUint32() m.GuildID = bf.ReadUint32() m.TrialID = bf.ReadUint32() return nil diff --git a/network/mhfpacket/msg_sys_cast_binary.go b/network/mhfpacket/msg_sys_cast_binary.go index 07935f2aa..f0b0c2bd3 100644 --- a/network/mhfpacket/msg_sys_cast_binary.go +++ b/network/mhfpacket/msg_sys_cast_binary.go @@ -1,17 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysCastBinary represents the MSG_SYS_CAST_BINARY type MsgSysCastBinary struct { - Unk0 uint16 - Unk1 uint16 + Unk uint32 BroadcastType uint8 MessageType uint8 RawDataPayload []byte @@ -24,8 +23,7 @@ func (m *MsgSysCastBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysCastBinary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + m.Unk = bf.ReadUint32() m.BroadcastType = bf.ReadUint8() m.MessageType = bf.ReadUint8() dataSize := bf.ReadUint16() diff --git a/network/mhfpacket/msg_sys_create_acquire_semaphore.go b/network/mhfpacket/msg_sys_create_acquire_semaphore.go index 694aaaeed..9e22c50e7 100644 --- a/network/mhfpacket/msg_sys_create_acquire_semaphore.go +++ b/network/mhfpacket/msg_sys_create_acquire_semaphore.go @@ -2,7 +2,6 @@ package mhfpacket import ( "errors" - "erupe-ce/common/bfutil" "erupe-ce/common/byteframe" _config "erupe-ce/config" "erupe-ce/network" @@ -29,8 +28,8 @@ func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clien if _config.ErupeConfig.RealClientMode >= _config.S7 { // Assuming this was added with Ravi? m.PlayerCount = bf.ReadUint8() } - SemaphoreIDLength := bf.ReadUint8() - m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength)))) + bf.ReadUint8() // SemaphoreID length + m.SemaphoreID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_create_semaphore.go b/network/mhfpacket/msg_sys_create_semaphore.go index 361802e05..c9b29d2ab 100644 --- a/network/mhfpacket/msg_sys_create_semaphore.go +++ b/network/mhfpacket/msg_sys_create_semaphore.go @@ -2,6 +2,7 @@ package mhfpacket import ( "errors" + _config "erupe-ce/config" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -10,10 +11,10 @@ import ( // MsgSysCreateSemaphore represents the MSG_SYS_CREATE_SEMAPHORE type MsgSysCreateSemaphore struct { - AckHandle uint32 - Unk0 uint16 - DataSize uint16 - RawDataPayload []byte + AckHandle uint32 + Unk0 uint16 + PlayerCount uint8 + SemaphoreID string } // Opcode returns the ID associated with this packet type. @@ -25,8 +26,11 @@ func (m *MsgSysCreateSemaphore) Opcode() network.PacketID { func (m *MsgSysCreateSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint16() - m.DataSize = bf.ReadUint16() - m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + if _config.ErupeConfig.RealClientMode >= _config.S7 { // Assuming this was added with Ravi? + m.PlayerCount = bf.ReadUint8() + } + bf.ReadUint8() // SemaphoreID length + m.SemaphoreID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_create_stage.go b/network/mhfpacket/msg_sys_create_stage.go index fe6e533ff..9c11ba46c 100644 --- a/network/mhfpacket/msg_sys_create_stage.go +++ b/network/mhfpacket/msg_sys_create_stage.go @@ -3,7 +3,6 @@ package mhfpacket import ( "errors" "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -13,7 +12,7 @@ type MsgSysCreateStage struct { AckHandle uint32 Unk0 uint8 // Likely only has 1 and 2 as values. PlayerCount uint8 - StageID string // NULL terminated string. + StageID string } // Opcode returns the ID associated with this packet type. @@ -26,8 +25,8 @@ func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint8() m.PlayerCount = bf.ReadUint8() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + bf.ReadUint8() // Length StageID + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_enter_stage.go b/network/mhfpacket/msg_sys_enter_stage.go index 977ff486f..17ba468f2 100644 --- a/network/mhfpacket/msg_sys_enter_stage.go +++ b/network/mhfpacket/msg_sys_enter_stage.go @@ -4,7 +4,6 @@ import ( "errors" "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -12,7 +11,7 @@ import ( // MsgSysEnterStage represents the MSG_SYS_ENTER_STAGE type MsgSysEnterStage struct { AckHandle uint32 - UnkBool uint8 + Unk bool StageID string } @@ -24,9 +23,9 @@ func (m *MsgSysEnterStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.UnkBool = bf.ReadUint8() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + m.Unk = bf.ReadBool() // IsQuest? + bf.ReadUint8() // Length StageID + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_enumerate_client.go b/network/mhfpacket/msg_sys_enumerate_client.go index 5318549a6..06764d65b 100644 --- a/network/mhfpacket/msg_sys_enumerate_client.go +++ b/network/mhfpacket/msg_sys_enumerate_client.go @@ -3,7 +3,6 @@ package mhfpacket import ( "errors" - "erupe-ce/common/bfutil" "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" @@ -27,8 +26,8 @@ func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint8() m.Get = bf.ReadUint8() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + bf.ReadUint8() // StageID length + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_enumerate_stage.go b/network/mhfpacket/msg_sys_enumerate_stage.go index a3f125941..b0d25c099 100644 --- a/network/mhfpacket/msg_sys_enumerate_stage.go +++ b/network/mhfpacket/msg_sys_enumerate_stage.go @@ -2,8 +2,6 @@ package mhfpacket import ( "errors" - "erupe-ce/common/stringsupport" - "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" @@ -12,8 +10,7 @@ import ( // MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE type MsgSysEnumerateStage struct { AckHandle uint32 - Unk0 uint8 // Hardcoded 1 in the binary - StagePrefix string // NULL terminated string. + StagePrefix string } // Opcode returns the ID associated with this packet type. @@ -24,9 +21,9 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() - bf.ReadUint8() - m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + bf.ReadUint8() // Always 1 + bf.ReadUint8() // Length StagePrefix + m.StagePrefix = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_get_file.go b/network/mhfpacket/msg_sys_get_file.go index 41b2a9029..99af6aa5e 100644 --- a/network/mhfpacket/msg_sys_get_file.go +++ b/network/mhfpacket/msg_sys_get_file.go @@ -4,9 +4,9 @@ import ( "errors" "erupe-ce/common/bfutil" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) type scenarioFileIdentifer struct { diff --git a/network/mhfpacket/msg_sys_get_stage_binary.go b/network/mhfpacket/msg_sys_get_stage_binary.go index 336f563d5..c2da50122 100644 --- a/network/mhfpacket/msg_sys_get_stage_binary.go +++ b/network/mhfpacket/msg_sys_get_stage_binary.go @@ -2,7 +2,6 @@ package mhfpacket import ( "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -27,8 +26,8 @@ func (m *MsgSysGetStageBinary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.BinaryType0 = bf.ReadUint8() m.BinaryType1 = bf.ReadUint8() m.Unk0 = bf.ReadUint32() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + bf.ReadUint8() // StageID length + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_hide_client.go b/network/mhfpacket/msg_sys_hide_client.go index e01c22cab..a2c714a41 100644 --- a/network/mhfpacket/msg_sys_hide_client.go +++ b/network/mhfpacket/msg_sys_hide_client.go @@ -1,18 +1,16 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysHideClient represents the MSG_SYS_HIDE_CLIENT type MsgSysHideClient struct { Hide bool - Unk0 uint16 // Hardcoded 0 in binary - Unk1 uint8 // Hardcoded 0 in binary } // Opcode returns the ID associated with this packet type. @@ -23,8 +21,9 @@ func (m *MsgSysHideClient) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysHideClient) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.Hide = bf.ReadBool() - m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint8() + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Zeroed return nil } diff --git a/network/mhfpacket/msg_sys_issue_logkey.go b/network/mhfpacket/msg_sys_issue_logkey.go index a22956186..d5bb6522d 100644 --- a/network/mhfpacket/msg_sys_issue_logkey.go +++ b/network/mhfpacket/msg_sys_issue_logkey.go @@ -1,18 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysIssueLogkey represents the MSG_SYS_ISSUE_LOGKEY type MsgSysIssueLogkey struct { AckHandle uint32 - Unk0 uint16 // Hardcoded 00 01 in binary - Unk1 uint16 // Hardcoded 0 in binary. + Unk0 uint16 } // Opcode returns the ID associated with this packet type. @@ -24,7 +23,7 @@ func (m *MsgSysIssueLogkey) Opcode() network.PacketID { func (m *MsgSysIssueLogkey) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint16() - m.Unk1 = bf.ReadUint16() + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_sys_load_register.go b/network/mhfpacket/msg_sys_load_register.go index 730616d65..edf4eafb4 100644 --- a/network/mhfpacket/msg_sys_load_register.go +++ b/network/mhfpacket/msg_sys_load_register.go @@ -11,7 +11,7 @@ import ( type MsgSysLoadRegister struct { AckHandle uint32 RegisterID uint32 - Unk1 uint8 + Values uint8 } // Opcode returns the ID associated with this packet type. @@ -23,9 +23,9 @@ func (m *MsgSysLoadRegister) Opcode() network.PacketID { func (m *MsgSysLoadRegister) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.RegisterID = bf.ReadUint32() - m.Unk1 = bf.ReadUint8() - _ = bf.ReadUint8() - _ = bf.ReadUint16() + m.Values = bf.ReadUint8() + bf.ReadUint8() // Zeroed + bf.ReadUint16() // Zeroed return nil } diff --git a/network/mhfpacket/msg_sys_lock_stage.go b/network/mhfpacket/msg_sys_lock_stage.go index 14b082596..13867b825 100644 --- a/network/mhfpacket/msg_sys_lock_stage.go +++ b/network/mhfpacket/msg_sys_lock_stage.go @@ -1,20 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysLockStage represents the MSG_SYS_LOCK_STAGE type MsgSysLockStage struct { - AckHandle uint32 - Unk0 uint8 // Hardcoded 1 in the binary - Unk1 uint8 // Hardcoded 1 in the binary - StageIDLength uint8 - StageID string + AckHandle uint32 + StageID string } // Opcode returns the ID associated with this packet type. @@ -25,10 +22,10 @@ func (m *MsgSysLockStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysLockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() - m.StageIDLength = bf.ReadUint8() - m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + bf.ReadUint8() // Always 1 + bf.ReadUint8() // Always 1 + bf.ReadUint8() // Length StageID + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_login.go b/network/mhfpacket/msg_sys_login.go index fc881b991..5f8a7c7cc 100644 --- a/network/mhfpacket/msg_sys_login.go +++ b/network/mhfpacket/msg_sys_login.go @@ -1,24 +1,22 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysLogin represents the MSG_SYS_LOGIN type MsgSysLogin struct { - AckHandle uint32 - CharID0 uint32 - LoginTokenNumber uint32 - HardcodedZero0 uint16 - RequestVersion uint16 - CharID1 uint32 - HardcodedZero1 uint16 - LoginTokenStringLength uint16 // Hardcoded to 0x11 - LoginTokenString string + AckHandle uint32 + CharID0 uint32 + LoginTokenNumber uint32 + HardcodedZero0 uint16 + RequestVersion uint16 + CharID1 uint32 + LoginTokenString string } // Opcode returns the ID associated with this packet type. @@ -34,8 +32,8 @@ func (m *MsgSysLogin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContex m.HardcodedZero0 = bf.ReadUint16() m.RequestVersion = bf.ReadUint16() m.CharID1 = bf.ReadUint32() - m.HardcodedZero1 = bf.ReadUint16() - m.LoginTokenStringLength = bf.ReadUint16() + bf.ReadUint16() // Zeroed + bf.ReadUint16() // Always 11 m.LoginTokenString = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_operate_register.go b/network/mhfpacket/msg_sys_operate_register.go index 6978609b1..75946d340 100644 --- a/network/mhfpacket/msg_sys_operate_register.go +++ b/network/mhfpacket/msg_sys_operate_register.go @@ -23,7 +23,7 @@ func (m *MsgSysOperateRegister) Opcode() network.PacketID { func (m *MsgSysOperateRegister) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.SemaphoreID = bf.ReadUint32() - _ = bf.ReadUint16() + bf.ReadUint16() // Zeroed dataSize := bf.ReadUint16() m.RawDataPayload = bf.ReadBytes(uint(dataSize)) return nil diff --git a/network/mhfpacket/msg_sys_record_log.go b/network/mhfpacket/msg_sys_record_log.go index 4266ec774..dcdf3e5a2 100644 --- a/network/mhfpacket/msg_sys_record_log.go +++ b/network/mhfpacket/msg_sys_record_log.go @@ -1,21 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysRecordLog represents the MSG_SYS_RECORD_LOG type MsgSysRecordLog struct { - AckHandle uint32 - Unk0 uint32 - Unk1 uint16 // Hardcoded 0 - HardcodedDataSize uint16 // Hardcoded 0x4AC - Unk3 uint32 // Some shared ID with MSG_MHF_GET_SEIBATTLE. World ID?? - DataBuf []byte + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 + Data []byte } // Opcode returns the ID associated with this packet type. @@ -27,10 +25,10 @@ func (m *MsgSysRecordLog) Opcode() network.PacketID { func (m *MsgSysRecordLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint16() - m.HardcodedDataSize = bf.ReadUint16() - m.Unk3 = bf.ReadUint32() - m.DataBuf = bf.ReadBytes(uint(m.HardcodedDataSize)) + bf.ReadUint16() // Zeroed + size := bf.ReadUint16() + m.Unk1 = bf.ReadUint32() + m.Data = bf.ReadBytes(uint(size)) return nil } diff --git a/network/mhfpacket/msg_sys_reserve_stage.go b/network/mhfpacket/msg_sys_reserve_stage.go index 13e47c41b..d2f688af4 100644 --- a/network/mhfpacket/msg_sys_reserve_stage.go +++ b/network/mhfpacket/msg_sys_reserve_stage.go @@ -3,7 +3,6 @@ package mhfpacket import ( "errors" "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -24,8 +23,8 @@ func (m *MsgSysReserveStage) Opcode() network.PacketID { func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Ready = bf.ReadUint8() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + _ = bf.ReadUint8() // StageID length + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_rights_reload.go b/network/mhfpacket/msg_sys_rights_reload.go index 7a8ac06e0..a70be8f38 100644 --- a/network/mhfpacket/msg_sys_rights_reload.go +++ b/network/mhfpacket/msg_sys_rights_reload.go @@ -1,17 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysRightsReload represents the MSG_SYS_RIGHTS_RELOAD -type MsgSysRightsReload struct{ +type MsgSysRightsReload struct { AckHandle uint32 - Unk0 byte + Unk0 []byte } // Opcode returns the ID associated with this packet type. @@ -22,7 +22,7 @@ func (m *MsgSysRightsReload) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysRightsReload) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + m.Unk0 = bf.ReadBytes(uint(bf.ReadUint8())) return nil } diff --git a/network/mhfpacket/msg_sys_set_stage_binary.go b/network/mhfpacket/msg_sys_set_stage_binary.go index eecee64a1..79832c7bb 100644 --- a/network/mhfpacket/msg_sys_set_stage_binary.go +++ b/network/mhfpacket/msg_sys_set_stage_binary.go @@ -2,7 +2,6 @@ package mhfpacket import ( "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -24,9 +23,9 @@ func (m *MsgSysSetStageBinary) Opcode() network.PacketID { func (m *MsgSysSetStageBinary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.BinaryType0 = bf.ReadUint8() m.BinaryType1 = bf.ReadUint8() - stageIDLength := bf.ReadUint8() // <= 0x20 - dataSize := bf.ReadUint16() // <= 0x400 - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + bf.ReadUint8() // StageID length <= 0x20 + dataSize := bf.ReadUint16() // <= 0x400 + m.StageID = string(bf.ReadNullTerminatedBytes()) m.RawDataPayload = bf.ReadBytes(uint(dataSize)) return nil } diff --git a/network/mhfpacket/msg_sys_set_stage_pass.go b/network/mhfpacket/msg_sys_set_stage_pass.go index 5cdbb2b88..1461241b0 100644 --- a/network/mhfpacket/msg_sys_set_stage_pass.go +++ b/network/mhfpacket/msg_sys_set_stage_pass.go @@ -1,17 +1,16 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysSetStagePass represents the MSG_SYS_SET_STAGE_PASS type MsgSysSetStagePass struct { - Unk0 uint8 // Hardcoded 0 in the binary - Password string // NULL-terminated string + Password string // NULL-terminated string } // Opcode returns the ID associated with this packet type. @@ -21,8 +20,8 @@ func (m *MsgSysSetStagePass) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetStagePass) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.Unk0 = bf.ReadUint8() - _ = bf.ReadUint8() // Password length + bf.ReadUint8() // Zeroed + bf.ReadUint8() // Password length m.Password = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index bc8f3f7c5..5033346b4 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -23,11 +23,9 @@ type TerminalLogEntry struct { // MsgSysTerminalLog represents the MSG_SYS_TERMINAL_LOG type MsgSysTerminalLog struct { - AckHandle uint32 - LogID uint32 // 0 on the first packet, and the server sends back a value to use for subsequent requests. - EntryCount uint16 - Unk0 uint16 // Hardcoded 0 in the binary - Entries []*TerminalLogEntry + AckHandle uint32 + LogID uint32 // 0 on the first packet, and the server sends back a value to use for subsequent requests. + Entries []TerminalLogEntry } // Opcode returns the ID associated with this packet type. @@ -39,11 +37,11 @@ func (m *MsgSysTerminalLog) Opcode() network.PacketID { func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.LogID = bf.ReadUint32() - m.EntryCount = bf.ReadUint16() - m.Unk0 = bf.ReadUint16() + entryCount := int(bf.ReadUint16()) + bf.ReadUint16() // Zeroed - for i := 0; i < int(m.EntryCount); i++ { - e := &TerminalLogEntry{} + var e TerminalLogEntry + for i := 0; i < entryCount; i++ { e.Index = bf.ReadUint32() e.Type1 = bf.ReadUint8() e.Type2 = bf.ReadUint8() diff --git a/network/mhfpacket/msg_sys_unlock_stage.go b/network/mhfpacket/msg_sys_unlock_stage.go index 74af57424..ec1effdc5 100644 --- a/network/mhfpacket/msg_sys_unlock_stage.go +++ b/network/mhfpacket/msg_sys_unlock_stage.go @@ -1,15 +1,14 @@ package mhfpacket import ( + "errors" + "erupe-ce/common/byteframe" "erupe-ce/network" "erupe-ce/network/clientctx" - "erupe-ce/common/byteframe" ) // MsgSysUnlockStage represents the MSG_SYS_UNLOCK_STAGE -type MsgSysUnlockStage struct { - Unk0 uint16 // Hardcoded 0 in the binary. -} +type MsgSysUnlockStage struct{} // Opcode returns the ID associated with this packet type. func (m *MsgSysUnlockStage) Opcode() network.PacketID { @@ -18,12 +17,11 @@ func (m *MsgSysUnlockStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.Unk0 = bf.ReadUint16() + bf.ReadUint16() // Zeroed return nil } // Build builds a binary packet from the current data. func (m *MsgSysUnlockStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - bf.WriteUint16(m.Unk0) - return nil + return errors.New("NOT IMPLEMENTED") } diff --git a/network/mhfpacket/msg_sys_wait_stage_binary.go b/network/mhfpacket/msg_sys_wait_stage_binary.go index 2a443cc72..5127e53de 100644 --- a/network/mhfpacket/msg_sys_wait_stage_binary.go +++ b/network/mhfpacket/msg_sys_wait_stage_binary.go @@ -2,7 +2,6 @@ package mhfpacket import ( "erupe-ce/common/byteframe" - "erupe-ce/common/bfutil" "erupe-ce/network" "erupe-ce/network/clientctx" ) @@ -27,8 +26,8 @@ func (m *MsgSysWaitStageBinary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl m.BinaryType0 = bf.ReadUint8() m.BinaryType1 = bf.ReadUint8() m.Unk0 = bf.ReadUint32() - stageIDLength := bf.ReadUint8() - m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) + bf.ReadUint8() // StageID length + m.StageID = string(bf.ReadNullTerminatedBytes()) return nil } 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/schemas/bundled-schema/DistributionDemo.sql b/schemas/bundled-schema/DistributionDemo.sql new file mode 100644 index 000000000..d5da8688e --- /dev/null +++ b/schemas/bundled-schema/DistributionDemo.sql @@ -0,0 +1,11 @@ +BEGIN; + +-- Adds a Distribution that can be accepted up to 20 times that gives one of Item Type 30 (Item Box extra page) +INSERT INTO distribution (type, event_name, description, times_acceptable) VALUES (1, 'Extra Item Storage', '~C05Adds one new page to your Item Box.', 20); +INSERT INTO distribution_items (distribution_id, item_type, quantity) VALUES ((SELECT id FROM distribution ORDER BY id DESC LIMIT 1), 30, 1); + +-- Adds a Distribution that can be accepted up to 20 times that gives one of Item Type 31 (Equipment Box extra page) +INSERT INTO distribution (type, event_name, description, times_acceptable) VALUES (1, 'Extra Equipment Storage', '~C05Adds one new page to your Equipment Box.', 20); +INSERT INTO distribution_items (distribution_id, item_type, quantity) VALUES ((SELECT id FROM distribution ORDER BY id DESC LIMIT 1), 31, 1); + +END; \ No newline at end of file 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/schemas/bundled-schema/FPointItems.sql b/schemas/bundled-schema/FPointItems.sql new file mode 100644 index 000000000..7012e6f25 --- /dev/null +++ b/schemas/bundled-schema/FPointItems.sql @@ -0,0 +1,391 @@ +BEGIN; + +INSERT INTO fpoint_items (item_type, item_id, quantity, fpoints, buyable) VALUES +(7,8895,1,500,true), +(7,8891,1,300,true), +(7,8892,1,300,true), +(7,8893,1,300,true), +(7,8894,1,300,true), +(7,8890,1,10,true), +(7,10354,1,500,true), +(7,11983,1,300,true), +(7,11984,1,300,true), +(7,11985,1,300,true), +(7,11986,1,300,true), +(7,12524,1,500,true), +(7,12470,1,300,true), +(7,12471,1,300,true), +(7,12472,1,300,true), +(7,12473,1,300,true), +(7,2158,2,1,true), +(7,14548,1,500,true), +(7,9509,1,1,true), +(7,9510,1,1,true), +(7,9511,1,1,true), +(7,9512,1,1,true), +(7,9513,1,1,true), +(7,9514,1,1,true), +(7,9515,1,1,true), +(7,10753,1,1,true), +(7,10754,1,1,true), +(7,10755,1,1,true), +(7,10756,1,1,true), +(7,10757,1,1,true), +(7,10758,1,1,true), +(7,10759,1,1,true), +(7,11296,1,1,true), +(7,11297,1,1,true), +(7,11298,1,1,true), +(7,11299,1,1,true), +(7,11300,1,1,true), +(7,12386,1,1,true), +(7,12387,1,1,true), +(7,12388,1,1,true), +(7,12389,1,1,true), +(7,12390,1,1,true), +(7,13034,1,1,true), +(7,13035,1,1,true), +(7,13036,1,1,true), +(7,13037,1,1,true), +(7,13038,1,1,true), +(7,14179,1,1,true), +(7,14180,1,1,true), +(7,14181,1,1,true), +(7,14182,1,1,true), +(7,14183,1,1,true), +(7,13422,1,1,true), +(7,13423,1,1,true), +(7,13424,1,1,true), +(7,13425,1,1,true), +(7,13426,1,1,true), +(7,13427,1,1,true), +(7,9796,1,3,false), +(7,9700,1,3,false), +(7,10380,1,3,false), +(7,10810,1,3,false), +(7,10811,1,3,false), +(7,11436,1,3,false), +(7,9509,1,1,false), +(7,9510,1,1,false), +(7,9511,1,1,false), +(7,9512,1,1,false), +(7,9513,1,1,false), +(7,9514,1,1,false), +(7,9515,1,1,false), +(7,10753,1,1,false), +(7,10754,1,1,false), +(7,10755,1,1,false), +(7,10756,1,1,false), +(7,10757,1,1,false), +(7,10758,1,1,false), +(7,10759,1,1,false), +(7,11296,1,1,false), +(7,11297,1,1,false), +(7,11298,1,1,false), +(7,11299,1,1,false), +(7,11300,1,1,false), +(7,12509,1,3,false), +(7,12386,1,1,false), +(7,12387,1,1,false), +(7,12388,1,1,false), +(7,12389,1,1,false), +(7,12390,1,1,false), +(7,12872,1,3,false), +(7,12873,1,3,false), +(7,12840,1,1,false), +(7,12841,1,1,false), +(7,12874,1,1,false), +(7,12875,1,1,false), +(7,13191,1,3,false), +(7,13177,1,3,false), +(7,13326,1,3,false), +(7,13034,1,1,false), +(7,13035,1,1,false), +(7,13036,1,1,false), +(7,13037,1,1,false), +(7,13038,1,1,false), +(7,13178,1,3,false), +(7,13453,1,3,false), +(7,13449,1,3,false), +(7,13450,1,3,false), +(7,13404,1,3,false), +(7,13422,1,1,false), +(7,13423,1,1,false), +(7,13424,1,1,false), +(7,13425,1,1,false), +(7,13426,1,1,false), +(7,13427,1,1,false), +(7,13791,1,3,false), +(7,14006,1,3,false), +(7,14031,1,3,false), +(7,14032,1,3,false), +(7,13960,1,3,false), +(7,14029,1,3,false), +(7,13956,1,1,false), +(7,13958,1,1,false), +(7,13957,1,1,false), +(7,13959,1,1,false), +(7,13790,1,3,false), +(7,14005,1,3,false), +(7,14010,1,3,false), +(7,14009,1,3,false), +(7,14008,1,3,false), +(7,13965,1,3,false), +(7,14028,1,3,false), +(7,13963,1,3,false), +(7,14026,1,3,false), +(7,13964,1,3,false), +(7,14027,1,3,false), +(7,14069,1,3,false), +(7,14124,1,3,false), +(7,14065,1,1,false), +(7,14066,1,1,false), +(7,14067,1,1,false), +(7,14068,1,1,false), +(7,13962,1,3,false), +(7,14125,1,3,false), +(7,14089,1,3,false), +(7,14090,1,3,false), +(7,14091,1,3,false), +(7,14092,1,3,false), +(7,14194,1,3,false), +(7,14191,1,3,false), +(7,14198,1,3,false), +(7,14197,1,3,false), +(7,14179,1,1,false), +(7,14180,1,1,false), +(7,14181,1,1,false), +(7,14182,1,1,false), +(7,14183,1,1,false), +(7,14196,1,3,false), +(7,14195,1,3,false), +(7,14193,1,3,false), +(7,14192,1,3,false), +(7,14407,1,3,false), +(7,14414,1,3,false), +(7,14406,1,3,false), +(7,14413,1,3,false), +(7,14416,1,3,false), +(7,14549,1,3,false), +(7,14550,1,3,false), +(7,14502,1,3,false), +(7,14507,1,3,false), +(7,14501,1,3,false), +(7,14506,1,3,false), +(7,14500,1,3,false), +(7,14505,1,3,false), +(7,14498,1,3,false), +(7,14659,1,3,false), +(7,14660,1,3,false), +(7,14657,1,1,false), +(7,14658,1,1,false), +(7,11420,1,3,false), +(7,14704,1,3,false), +(7,11288,1,1,false), +(7,11289,1,1,false), +(7,11290,1,1,false), +(7,11291,1,1,false), +(7,10750,1,3,false), +(7,14705,1,3,false), +(7,10633,1,1,false), +(7,10634,1,1,false), +(7,10635,1,1,false), +(7,10636,1,1,false), +(7,14662,1,3,false), +(7,14663,1,3,false), +(7,14665,1,3,false), +(7,14666,1,3,false), +(7,14667,1,3,false), +(7,14668,1,3,false), +(7,14669,1,3,false), +(7,14670,1,3,false), +(7,14671,1,3,false), +(7,14672,1,3,false), +(7,14673,1,3,false), +(7,14674,1,3,false), +(7,14675,1,3,false), +(7,14676,1,3,false), +(7,14677,1,3,false), +(7,14678,1,3,false), +(7,14679,1,3,false), +(7,14680,1,3,false), +(7,14681,1,3,false), +(7,14682,1,3,false), +(7,14683,1,3,false), +(7,14684,1,3,false), +(7,14685,1,3,false), +(7,14686,1,3,false), +(7,14687,1,3,false), +(7,14688,1,3,false), +(7,14689,1,3,false), +(7,14690,1,3,false), +(7,14691,1,3,false), +(7,14692,1,3,false), +(7,14693,1,3,false), +(7,14694,1,3,false), +(7,14695,1,3,false), +(7,14696,1,3,false), +(7,14697,1,3,false), +(7,14698,1,3,false), +(7,14699,1,3,false), +(7,14700,1,3,false), +(7,14314,1,3,false), +(7,14503,1,3,false), +(7,14510,1,3,false), +(7,14904,1,3,false), +(7,14906,1,3,false), +(7,14910,1,1,false), +(7,14912,1,1,false), +(7,14905,1,3,false), +(7,14907,1,3,false), +(7,14911,1,1,false), +(7,14909,1,1,false), +(7,14855,1,3,false), +(7,14894,1,3,false), +(7,14913,1,3,false), +(7,14914,1,3,false), +(7,14891,1,3,false), +(7,14895,1,3,false), +(7,15027,1,3,false), +(7,15028,1,3,false), +(7,15026,1,1,false), +(7,15025,1,1,false), +(7,15024,1,1,false), +(7,15023,1,1,false), +(7,15064,1,3,false), +(7,15065,1,3,false), +(7,15030,1,3,false), +(7,15031,1,3,false), +(7,15062,1,3,false), +(7,15063,1,3,false), +(7,15066,1,3,false), +(7,15067,1,3,false), +(7,15061,1,3,false), +(7,15060,1,3,false), +(7,1227,1,2,false), +(7,13176,1,2,false), +(7,4360,1,2,false), +(7,4358,1,1,false), +(7,15118,1,3,false), +(7,15119,1,3,false), +(7,15113,1,3,false), +(7,15114,1,3,false), +(7,15115,1,3,false), +(7,15116,1,3,false), +(7,15220,1,3,false), +(7,15221,1,3,false), +(7,14126,1,3,false), +(7,15222,1,3,false), +(7,15223,1,3,false), +(7,15224,1,3,false), +(7,15225,1,3,false), +(7,15524,1,3,false), +(7,15525,1,3,false), +(7,15507,1,3,false), +(7,15508,1,3,false), +(7,15285,1,3,false), +(7,15286,1,3,false), +(7,15281,1,1,false), +(7,15282,1,1,false), +(7,15283,1,1,false), +(7,15284,1,1,false), +(7,15776,1,3,false), +(7,15777,1,3,false), +(7,15774,1,3,false), +(7,15775,1,3,false), +(7,15823,1,3,false), +(7,15824,1,3,false), +(7,15343,1,3,false), +(7,15342,1,3,false), +(7,15341,1,3,false), +(7,15340,1,3,false), +(7,15339,1,3,false), +(7,15338,1,3,false), +(7,15337,1,3,false), +(7,15336,1,3,false), +(7,15335,1,3,false), +(7,15334,1,3,false), +(7,15333,1,3,false), +(7,15332,1,3,false), +(7,15331,1,3,false), +(7,15330,1,3,false), +(7,15329,1,3,false), +(7,15328,1,3,false), +(7,15327,1,3,false), +(7,15326,1,3,false), +(7,15325,1,3,false), +(7,15324,1,3,false), +(7,15323,1,3,false), +(7,15322,1,3,false), +(7,15321,1,3,false), +(7,15314,1,3,false), +(7,15312,1,3,false), +(7,15311,1,3,false), +(7,15306,1,3,false), +(7,15307,1,3,false), +(7,15308,1,3,false), +(7,15309,1,3,false), +(7,15310,1,3,false), +(7,15305,1,3,false), +(7,15304,1,3,false), +(7,15303,1,3,false), +(7,15302,1,3,false), +(7,15301,1,3,false), +(7,15300,1,3,false), +(7,15299,1,3,false), +(7,15298,1,3,false), +(7,15297,1,3,false), +(7,15296,1,3,false), +(7,15295,1,3,false), +(7,15293,1,3,false), +(7,15294,1,3,false), +(7,15292,1,3,false), +(7,15291,1,3,false), +(7,15290,1,3,false), +(7,15289,1,3,false), +(7,15315,1,3,false), +(7,15316,1,3,false), +(7,15317,1,3,false), +(7,15318,1,3,false), +(7,15319,1,3,false), +(7,15320,1,3,false), +(7,15819,1,3,false), +(7,15820,1,3,false), +(7,15821,1,3,false), +(7,15822,1,3,false), +(7,16450,1,3,false), +(7,16451,1,3,false), +(7,16459,1,1,false), +(7,16460,1,1,false), +(7,16461,1,1,false), +(7,16462,1,1,false), +(7,16463,1,1,false), +(7,16464,1,1,false), +(7,16465,1,1,false), +(7,16466,1,1,false), +(7,16467,1,1,false), +(7,16468,1,1,false), +(7,16469,1,1,false), +(7,16470,1,1,false), +(7,16471,1,1,false), +(7,16472,1,1,false), +(7,16454,1,3,false), +(7,16455,1,3,false), +(7,16442,1,3,false), +(7,16443,1,3,false), +(7,16342,1,3,false), +(7,16343,1,3,false), +(7,16444,1,3,false), +(7,16445,1,3,false), +(7,16344,1,3,false), +(7,16345,1,3,false), +(7,16352,1,3,false), +(7,16353,1,3,false), +(7,16446,1,3,false), +(7,16447,1,3,false), +(7,16448,1,3,false), +(7,16449,1,3,false), +(7,16348,1,3,false), +(7,16349,1,3,false); + +END; \ No newline at end of file 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/schemas/bundled-schema/OtherShops.sql b/schemas/bundled-schema/OtherShops.sql new file mode 100644 index 000000000..3c88bb896 --- /dev/null +++ b/schemas/bundled-schema/OtherShops.sql @@ -0,0 +1,48 @@ +BEGIN; + +INSERT INTO public.shop_items +(shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis) +VALUES + (5,5,16516,100,1,0,0,1,0,0,0,0), + (5,5,16517,100,1,0,0,1,0,0,0,0), + (6,5,9958,3,3,1,0,0,0,0,0,0), + (6,5,1897,3,1,1,0,0,0,0,0,0), + (6,5,8889,3,1,0,0,1,0,0,0,0), + (6,5,6176,3,6,1,0,0,0,0,0,0), + (6,5,1472,3,10,1,0,0,0,0,0,0), + (6,5,7280,3,3,0,0,1,0,0,0,0), + (6,5,8027,3,30,1,0,0,0,0,0,0), + (6,5,8028,3,30,1,0,0,0,0,0,0), + (6,5,8029,3,30,1,0,0,0,0,0,0), + (6,5,8026,3,30,1,0,0,0,0,0,0), + (6,5,8030,3,30,1,0,0,0,0,0,0), + (6,5,4353,3,30,1,0,0,0,0,0,0), + (6,5,4354,3,30,1,0,0,0,0,0,0), + (6,5,4355,3,30,1,0,0,0,0,0,0), + (6,5,4356,3,30,1,0,0,0,0,0,0), + (6,5,4357,3,30,1,0,0,0,0,0,0), + (6,5,4745,3,30,1,0,0,0,0,0,0), + (6,5,4746,3,30,1,0,0,0,0,0,0), + (6,5,4747,3,30,1,0,0,0,0,0,0), + (6,5,4748,3,30,1,0,0,0,0,0,0), + (6,5,4749,3,30,1,0,0,0,0,0,0), + (6,5,5122,3,30,1,0,0,0,0,0,0), + (6,5,5123,3,30,1,0,0,0,0,0,0), + (6,5,5124,3,30,1,0,0,0,0,0,0), + (6,5,5125,3,30,1,0,0,0,0,0,0), + (6,5,5126,3,30,1,0,0,0,0,0,0), + (6,5,5795,3,30,1,0,0,0,0,0,0), + (6,5,5796,3,30,1,0,0,0,0,0,0), + (6,5,5797,3,30,1,0,0,0,0,0,0), + (6,5,5798,3,30,1,0,0,0,0,0,0), + (6,5,5799,3,30,1,0,0,0,0,0,0), + (6,5,6168,3,30,1,0,0,0,0,0,0), + (6,5,6169,3,30,1,0,0,0,0,0,0), + (6,5,6170,3,30,1,0,0,0,0,0,0), + (6,5,6171,3,30,1,0,0,0,0,0,0), + (6,5,6172,3,30,1,0,0,0,0,0,0), + (7,0,13190,10,1,0,0,0,0,0,0,0), + (7,0,1662,10,1,0,0,0,0,0,0,0), + (7,0,10179,100,1,0,0,0,0,0,0,0); + +END; \ No newline at end of file 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/schemas/bundled-schema/ScenarioDefaults.sql b/schemas/bundled-schema/ScenarioDefaults.sql new file mode 100644 index 000000000..ec7b3d99e --- /dev/null +++ b/schemas/bundled-schema/ScenarioDefaults.sql @@ -0,0 +1,178 @@ +BEGIN; + +INSERT INTO public.scenario_counter +(scenario_id, category_id) +VALUES + (17,0), + (93,1), + (92,1), + (81,1), + (91,1), + (90,1), + (89,1), + (88,1), + (87,1), + (86,1), + (85,1), + (84,1), + (83,1), + (82,1), + (87,3), + (88,3), + (89,3), + (90,3), + (91,3), + (92,3), + (83,3), + (86,3), + (60,3), + (58,3), + (59,3), + (27,3), + (25,3), + (26,3), + (23,3), + (2,3), + (3,3), + (4,3), + (31,3), + (32,3), + (33,3), + (34,3), + (35,3), + (36,3), + (37,3), + (40,3), + (38,3), + (39,3), + (48,3), + (12,3), + (13,3), + (30,3), + (29,3), + (46,3), + (0,4), + (1,4), + (2,4), + (3,4), + (4,4), + (5,4), + (6,4), + (7,4), + (8,4), + (9,4), + (10,4), + (11,4), + (12,4), + (13,4), + (14,4), + (50,5), + (51,5), + (52,5), + (53,5), + (54,5), + (55,5), + (56,5), + (58,5), + (63,5), + (64,5), + (65,5), + (67,5), + (71,5), + (75,5), + (61,5), + (68,5), + (66,5), + (76,5), + (70,5), + (77,5), + (72,5), + (74,5), + (73,5), + (78,5), + (69,5), + (62,5), + (79,5), + (0,6), + (1,6), + (2,6), + (3,6), + (4,6), + (5,6), + (6,6), + (7,6), + (8,6), + (9,6), + (17,6), + (10,6), + (11,6), + (12,6), + (13,6), + (14,6), + (15,6), + (16,6), + (50,7), + (53,7), + (62,7), + (52,7), + (56,7), + (51,7), + (49,7), + (54,7), + (57,7), + (55,7), + (61,7), + (58,7), + (60,7), + (59,7), + (42,7), + (48,7), + (40,7), + (39,7), + (43,7), + (46,7), + (41,7), + (44,7), + (45,7), + (47,7), + (37,7), + (34,7), + (33,7), + (32,7), + (28,7), + (26,7), + (36,7), + (38,7), + (35,7), + (27,7), + (30,7), + (31,7), + (29,7), + (24,7), + (23,7), + (22,7), + (21,7), + (25,7), + (20,7), + (7,7), + (9,7), + (13,7), + (16,7), + (12,7), + (14,7), + (15,7), + (19,7), + (10,7), + (8,7), + (11,7), + (18,7), + (17,7), + (6,7), + (5,7), + (4,7), + (3,7), + (2,7), + (1,7), + (0,7); + +END; \ No newline at end of file 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 98% rename from patch-schema/03-event_quests.sql rename to schemas/patch-schema/03-event_quests.sql index 94aac0c65..1374a3d08 100644 --- a/patch-schema/03-event_quests.sql +++ b/schemas/patch-schema/03-event_quests.sql @@ -11,4 +11,4 @@ create table if not exists event_quests ALTER TABLE IF EXISTS public.servers DROP COLUMN IF EXISTS season; -END; \ No newline at end of file +END; 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/schemas/patch-schema/06-goocoo-rename.sql b/schemas/patch-schema/06-goocoo-rename.sql new file mode 100644 index 000000000..e72585ab3 --- /dev/null +++ b/schemas/patch-schema/06-goocoo-rename.sql @@ -0,0 +1,11 @@ +BEGIN; + +ALTER TABLE gook RENAME TO goocoo; + +ALTER TABLE goocoo RENAME COLUMN gook0 TO goocoo0; +ALTER TABLE goocoo RENAME COLUMN gook1 TO goocoo1; +ALTER TABLE goocoo RENAME COLUMN gook2 TO goocoo2; +ALTER TABLE goocoo RENAME COLUMN gook3 TO goocoo3; +ALTER TABLE goocoo RENAME COLUMN gook4 TO goocoo4; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/07-scenarios-counter.sql b/schemas/patch-schema/07-scenarios-counter.sql new file mode 100644 index 000000000..3ea2c65b2 --- /dev/null +++ b/schemas/patch-schema/07-scenarios-counter.sql @@ -0,0 +1,9 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS scenario_counter ( + id serial primary key, + scenario_id numeric not null, + category_id numeric not null +); + +END; \ No newline at end of file diff --git a/schemas/patch-schema/08-kill-counts.sql b/schemas/patch-schema/08-kill-counts.sql new file mode 100644 index 000000000..1c170cedd --- /dev/null +++ b/schemas/patch-schema/08-kill-counts.sql @@ -0,0 +1,12 @@ +CREATE TABLE public.kill_logs +( + id serial, + character_id integer NOT NULL, + monster integer NOT NULL, + quantity integer NOT NULL, + timestamp timestamp with time zone NOT NULL, + PRIMARY KEY (id) +); + +ALTER TABLE IF EXISTS public.guild_characters + ADD COLUMN box_claimed timestamp with time zone DEFAULT now(); \ No newline at end of file diff --git a/schemas/patch-schema/09-fix-guild-treasure.sql b/schemas/patch-schema/09-fix-guild-treasure.sql new file mode 100644 index 000000000..1c022292f --- /dev/null +++ b/schemas/patch-schema/09-fix-guild-treasure.sql @@ -0,0 +1,26 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS hunters; + +ALTER TABLE IF EXISTS public.guild_characters + ADD COLUMN treasure_hunt integer; + +ALTER TABLE IF EXISTS public.guild_hunts + ADD COLUMN start timestamp with time zone NOT NULL DEFAULT now(); + +UPDATE guild_hunts SET start=to_timestamp(return); + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS "return"; + +ALTER TABLE IF EXISTS public.guild_hunts + RENAME claimed TO collected; + +CREATE TABLE public.guild_hunts_claimed +( + hunt_id integer NOT NULL, + character_id integer NOT NULL +); + +ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS treasure; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/10-rework-distributions.sql b/schemas/patch-schema/10-rework-distributions.sql new file mode 100644 index 000000000..7945de343 --- /dev/null +++ b/schemas/patch-schema/10-rework-distributions.sql @@ -0,0 +1,36 @@ +BEGIN; + +-- This will delete all of your old distribution data! +--ALTER TABLE IF EXISTS public.distribution DROP COLUMN IF EXISTS data; + +CREATE TABLE public.distribution_items +( + id serial PRIMARY KEY, + distribution_id integer NOT NULL, + item_type integer NOT NULL, + item_id integer, + quantity integer +); + +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_hr DROP DEFAULT; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_hr DROP DEFAULT; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_sr DROP DEFAULT; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_sr DROP DEFAULT; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_gr DROP DEFAULT; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_gr DROP DEFAULT; + +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_hr DROP NOT NULL; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_hr DROP NOT NULL; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_sr DROP NOT NULL; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_sr DROP NOT NULL; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN min_gr DROP NOT NULL; +ALTER TABLE IF EXISTS public.distribution ALTER COLUMN max_gr DROP NOT NULL; + +UPDATE distribution SET min_hr=NULL WHERE min_hr=65535; +UPDATE distribution SET max_hr=NULL WHERE max_hr=65535; +UPDATE distribution SET min_sr=NULL WHERE min_sr=65535; +UPDATE distribution SET max_sr=NULL WHERE max_sr=65535; +UPDATE distribution SET min_gr=NULL WHERE min_gr=65535; +UPDATE distribution SET max_gr=NULL WHERE max_gr=65535; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/11-event-quest-flags.sql b/schemas/patch-schema/11-event-quest-flags.sql new file mode 100644 index 000000000..5f88d732d --- /dev/null +++ b/schemas/patch-schema/11-event-quest-flags.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.event_quests ADD COLUMN IF NOT EXISTS flags integer; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/12-event_quest_cycling.sql b/schemas/patch-schema/12-event_quest_cycling.sql new file mode 100644 index 000000000..8760bdab4 --- /dev/null +++ b/schemas/patch-schema/12-event_quest_cycling.sql @@ -0,0 +1,10 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.event_quests ADD COLUMN IF NOT EXISTS start_time timestamp with time zone NOT NULL DEFAULT now(); +ALTER TABLE IF EXISTS public.event_quests ADD COLUMN IF NOT EXISTS active_duration int; +ALTER TABLE IF EXISTS public.event_quests ADD COLUMN IF NOT EXISTS inactive_duration int; +UPDATE public.event_quests SET active_duration=NULL, inactive_duration=NULL; +ALTER TABLE IF EXISTS public.event_quests RENAME active_duration TO active_days; +ALTER TABLE IF EXISTS public.event_quests RENAME inactive_duration TO inactive_days; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/13-festa-trial-votes.sql b/schemas/patch-schema/13-festa-trial-votes.sql new file mode 100644 index 000000000..d9e3d0290 --- /dev/null +++ b/schemas/patch-schema/13-festa-trial-votes.sql @@ -0,0 +1,5 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.guild_characters ADD COLUMN trial_vote integer; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/14-fix-fpoint-trades.sql b/schemas/patch-schema/14-fix-fpoint-trades.sql new file mode 100644 index 000000000..c4e698655 --- /dev/null +++ b/schemas/patch-schema/14-fix-fpoint-trades.sql @@ -0,0 +1,11 @@ +BEGIN; + +DELETE FROM public.fpoint_items; +ALTER TABLE IF EXISTS public.fpoint_items ALTER COLUMN item_type SET NOT NULL; +ALTER TABLE IF EXISTS public.fpoint_items ALTER COLUMN item_id SET NOT NULL; +ALTER TABLE IF EXISTS public.fpoint_items ALTER COLUMN quantity SET NOT NULL; +ALTER TABLE IF EXISTS public.fpoint_items ALTER COLUMN fpoints SET NOT NULL; +ALTER TABLE IF EXISTS public.fpoint_items DROP COLUMN IF EXISTS trade_type; +ALTER TABLE IF EXISTS public.fpoint_items ADD COLUMN buyable boolean NOT NULL; + +END; \ No newline at end of file diff --git a/schemas/patch-schema/15-reset-goocoos.sql b/schemas/patch-schema/15-reset-goocoos.sql new file mode 100644 index 000000000..ca4d3fa11 --- /dev/null +++ b/schemas/patch-schema/15-reset-goocoos.sql @@ -0,0 +1,5 @@ +BEGIN; + +UPDATE goocoo SET goocoo0=NULL, goocoo1=NULL, goocoo2=NULL, goocoo3=NULL, goocoo4=NULL; + +END; \ No newline at end of file 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 6ce0ff6af..5463b75db 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -2,8 +2,8 @@ package channelserver import ( "encoding/binary" - "encoding/hex" "erupe-ce/common/mhfcourse" + "erupe-ce/common/mhfmon" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" _config "erupe-ce/config" @@ -16,8 +16,9 @@ import ( "crypto/rand" "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" - "go.uber.org/zap" "math/bits" + + "go.uber.org/zap" ) // Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet @@ -30,7 +31,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))) @@ -126,9 +127,9 @@ 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 WHERE token=$1", pkt.LoginTokenString).Scan(&token) + 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 { s.rawConn.Close() s.logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0)) @@ -231,7 +232,7 @@ func logoutPlayer(s *Session) { s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) - treasureHuntUnregister(s) + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, s.charID) if s.stage == nil { return @@ -304,9 +305,20 @@ func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysRecordLog) + if _config.ErupeConfig.RealClientMode == _config.ZZ { + bf := byteframe.NewByteFrameFromBytes(pkt.Data) + bf.Seek(32, 0) + var val uint8 + for i := 0; i < 176; i++ { + val = bf.ReadUint8() + if val > 0 && mhfmon.Monsters[i].Large { + s.server.db.Exec(`INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, $2, $3, $4)`, s.charID, i, val, TimeAdjusted()) + } + } + } // remove a client returning to town from reserved slots to make sure the stage is hidden from board delete(s.stage.reservedClientSlots, s.charID) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgSysEcho(s *Session, p mhfpacket.MHFPacket) {} @@ -353,143 +365,117 @@ func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfTransitMessage) + + local := false + if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" { + local = true + } + + var maxResults, port, count uint16 + var cid uint32 + var term, ip string + bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) + switch pkt.SearchType { + case 1: + maxResults = 1 + cid = bf.ReadUint32() + case 2: + bf.ReadUint16() // term length + maxResults = bf.ReadUint16() + bf.ReadUint8() // Unk + term = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + case 3: + _ip := bf.ReadBytes(4) + ip = fmt.Sprintf("%d.%d.%d.%d", _ip[3], _ip[2], _ip[1], _ip[0]) + port = bf.ReadUint16() + bf.ReadUint16() // term length + maxResults = bf.ReadUint16() + bf.ReadUint8() + term = string(bf.ReadNullTerminatedBytes()) + } + resp := byteframe.NewByteFrame() resp.WriteUint16(0) - var count uint16 switch pkt.SearchType { - case 1: // CID - bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - CharID := bf.ReadUint32() + case 1, 2, 3: // usersearchidx, usersearchname, lobbysearchname for _, c := range s.server.Channels { for _, session := range c.sessions { - if session.charID == CharID { - count++ - sessionName := stringsupport.UTF8ToSJIS(session.Name) - sessionStage := stringsupport.UTF8ToSJIS(session.stageID) - resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) - resp.WriteUint16(c.Port) - resp.WriteUint32(session.charID) - resp.WriteBool(true) - resp.WriteUint8(uint8(len(sessionName) + 1)) - resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}]))) - resp.WriteBytes(make([]byte, 40)) - resp.WriteUint8(uint8(len(sessionStage) + 1)) - resp.WriteBytes(make([]byte, 8)) - resp.WriteNullTerminatedBytes(sessionName) - resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}]) - resp.WriteNullTerminatedBytes(sessionStage) - } - } - } - case 2: // Name - bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - bf.ReadUint16() // lenSearchTerm - bf.ReadUint16() // maxResults - bf.ReadUint8() // Unk - searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) - for _, c := range s.server.Channels { - for _, session := range c.sessions { - if count == 100 { + if count == maxResults { break } - if strings.Contains(session.Name, searchTerm) { - count++ - sessionName := stringsupport.UTF8ToSJIS(session.Name) - sessionStage := stringsupport.UTF8ToSJIS(session.stageID) + if pkt.SearchType == 1 && session.charID != cid { + continue + } + if pkt.SearchType == 2 && !strings.Contains(session.Name, term) { + continue + } + if pkt.SearchType == 3 && session.server.IP != ip && session.server.Port != port && session.stage.id != term { + continue + } + count++ + sessionName := stringsupport.UTF8ToSJIS(session.Name) + sessionStage := stringsupport.UTF8ToSJIS(session.stage.id) + if !local { resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) - resp.WriteUint16(c.Port) - resp.WriteUint32(session.charID) - resp.WriteBool(true) - resp.WriteUint8(uint8(len(sessionName) + 1)) - resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{session.charID, 3}]))) - resp.WriteBytes(make([]byte, 40)) - resp.WriteUint8(uint8(len(sessionStage) + 1)) + } else { + resp.WriteUint32(0x0100007F) + } + resp.WriteUint16(c.Port) + resp.WriteUint32(session.charID) + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteUint8(uint8(len(sessionName) + 1)) + resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}]))) + + // TODO: This case might be <=G2 + if _config.ErupeConfig.RealClientMode <= _config.G1 { resp.WriteBytes(make([]byte, 8)) - resp.WriteNullTerminatedBytes(sessionName) - resp.WriteBytes(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}]) - resp.WriteNullTerminatedBytes(sessionStage) + } else { + resp.WriteBytes(make([]byte, 40)) } + resp.WriteBytes(make([]byte, 8)) + + resp.WriteNullTerminatedBytes(sessionStage) + resp.WriteNullTerminatedBytes(sessionName) + resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}]) } } - case 3: // Enumerate Party - bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - ip := bf.ReadBytes(4) - ipString := fmt.Sprintf("%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]) - port := bf.ReadUint16() - bf.ReadUint16() // lenStage - maxResults := bf.ReadUint16() - bf.ReadBytes(1) - stageID := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) - for _, c := range s.server.Channels { - if c.IP == ipString && c.Port == port { - for _, stage := range c.stages { - if stage.id == stageID { - if count == maxResults { - break - } - for session := range stage.clients { - count++ - hrp := uint16(1) - gr := uint16(0) - s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr) - sessionStage := stringsupport.UTF8ToSJIS(session.stageID) - sessionName := stringsupport.UTF8ToSJIS(session.Name) - resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) - resp.WriteUint16(c.Port) - resp.WriteUint32(session.charID) - resp.WriteUint8(uint8(len(sessionStage) + 1)) - resp.WriteUint8(uint8(len(sessionName) + 1)) - resp.WriteUint8(0) - resp.WriteUint8(7) // lenBinary - resp.WriteBytes(make([]byte, 48)) - resp.WriteNullTerminatedBytes(sessionStage) - resp.WriteNullTerminatedBytes(sessionName) - resp.WriteUint16(hrp) - resp.WriteUint16(gr) - resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk - } - } - } - } - } - case 4: // Find Party + case 4: // lobbysearch type FindPartyParams struct { StagePrefix string - RankRestriction uint16 - Targets []uint16 - Unk0 []uint16 - Unk1 []uint16 - QuestID []uint16 + RankRestriction int16 + Targets []int16 + Unk0 []int16 + Unk1 []int16 + QuestID []int16 } findPartyParams := FindPartyParams{ StagePrefix: "sl2Ls210", } - bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) - numParams := int(bf.ReadUint8()) - maxResults := bf.ReadUint16() - for i := 0; i < numParams; i++ { + numParams := bf.ReadUint8() + maxResults = bf.ReadUint16() + for i := uint8(0); i < numParams; i++ { switch bf.ReadUint8() { case 0: - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { if _config.ErupeConfig.RealClientMode >= _config.Z1 { - findPartyParams.RankRestriction = bf.ReadUint16() + findPartyParams.RankRestriction = bf.ReadInt16() } else { - findPartyParams.RankRestriction = uint16(bf.ReadInt8()) + findPartyParams.RankRestriction = int16(bf.ReadInt8()) } } case 1: - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { if _config.ErupeConfig.RealClientMode >= _config.Z1 { - findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadUint16()) + findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadInt16()) } else { - findPartyParams.Targets = append(findPartyParams.Targets, uint16(bf.ReadInt8())) + findPartyParams.Targets = append(findPartyParams.Targets, int16(bf.ReadInt8())) } } case 2: - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { var value int16 if _config.ErupeConfig.RealClientMode >= _config.Z1 { value = bf.ReadInt16() @@ -510,30 +496,30 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { } } case 3: // Unknown - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { if _config.ErupeConfig.RealClientMode >= _config.Z1 { - findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadUint16()) + findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadInt16()) } else { - findPartyParams.Unk0 = append(findPartyParams.Unk0, uint16(bf.ReadInt8())) + findPartyParams.Unk0 = append(findPartyParams.Unk0, int16(bf.ReadInt8())) } } case 4: // Looking for n or already have n - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { if _config.ErupeConfig.RealClientMode >= _config.Z1 { - findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadUint16()) + findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadInt16()) } else { - findPartyParams.Unk1 = append(findPartyParams.Unk1, uint16(bf.ReadInt8())) + findPartyParams.Unk1 = append(findPartyParams.Unk1, int16(bf.ReadInt8())) } } case 5: - values := int(bf.ReadUint8()) - for i := 0; i < values; i++ { + values := bf.ReadUint8() + for i := uint8(0); i < values; i++ { if _config.ErupeConfig.RealClientMode >= _config.Z1 { - findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadUint16()) + findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadInt16()) } else { - findPartyParams.QuestID = append(findPartyParams.QuestID, uint16(bf.ReadInt8())) + findPartyParams.QuestID = append(findPartyParams.QuestID, int16(bf.ReadInt8())) } } } @@ -546,47 +532,81 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) { sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) sb3.Seek(4, 0) - stageRankRestriction := sb3.ReadUint16() - stageTarget := sb3.ReadUint16() - if stageRankRestriction > findPartyParams.RankRestriction { - continue + + stageDataParams := 7 + if _config.ErupeConfig.RealClientMode <= _config.G10 { + stageDataParams = 4 + } else if _config.ErupeConfig.RealClientMode <= _config.Z1 { + stageDataParams = 6 } + + var stageData []int16 + for i := 0; i < stageDataParams; i++ { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + stageData = append(stageData, sb3.ReadInt16()) + } else { + stageData = append(stageData, int16(sb3.ReadInt8())) + } + } + + if findPartyParams.RankRestriction >= 0 { + if stageData[0] > findPartyParams.RankRestriction { + continue + } + } + + var hasTarget bool if len(findPartyParams.Targets) > 0 { for _, target := range findPartyParams.Targets { - if target == stageTarget { + if target == stageData[1] { + hasTarget = true break } } - continue + if !hasTarget { + continue + } } + count++ - sessionStage := stringsupport.UTF8ToSJIS(stage.id) - resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + if !local { + resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + } else { + resp.WriteUint32(0x0100007F) + } resp.WriteUint16(c.Port) + resp.WriteUint16(0) // Static? - resp.WriteUint16(0) // Unk - resp.WriteUint16(uint16(len(stage.clients))) - resp.WriteUint16(stage.maxPlayers) - resp.WriteUint16(0) // Num clients entered from stage + resp.WriteUint16(0) // Unk, [0 1 2] + resp.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots))) resp.WriteUint16(stage.maxPlayers) + // TODO: Retail returned the number of clients in quests, not workshop/my series + resp.WriteUint16(uint16(len(stage.reservedClientSlots))) + + resp.WriteUint8(0) // Static? + resp.WriteUint8(uint8(stage.maxPlayers)) resp.WriteUint8(1) // Static? - resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteUint8(uint8(len(stage.id) + 1)) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}]))) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) - resp.WriteUint16(stageRankRestriction) - resp.WriteUint16(stageTarget) - resp.WriteBytes(make([]byte, 12)) - resp.WriteNullTerminatedBytes(sessionStage) + + for i := range stageData { + if _config.ErupeConfig.RealClientMode >= _config.Z1 { + resp.WriteInt16(stageData[i]) + } else { + resp.WriteInt8(int8(stageData[i])) + } + } + resp.WriteUint8(0) // Unk + resp.WriteUint8(0) // Unk + + resp.WriteNullTerminatedBytes([]byte(stage.id)) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}]) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) } } } } - if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - return - } resp.Seek(0, io.SeekStart) resp.WriteUint16(count) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) @@ -598,7 +618,7 @@ func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAnnounce(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAnnounce) - s.server.BroadcastRaviente(pkt.IPAddress, pkt.Port, pkt.StageID, pkt.Type) + s.server.BroadcastRaviente(pkt.IPAddress, pkt.Port, pkt.StageID, pkt.Data.ReadUint8()) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -627,12 +647,167 @@ func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumeratePrice) - //resp := byteframe.NewByteFrame() - //resp.WriteUint16(0) // Entry type 1 count - //resp.WriteUint16(0) // Entry type 2 count - // directly lifted for now because lacking it crashes the counter on having actual events present - data, _ := hex.DecodeString("0000000066000003E800000000007300640100000320000000000006006401000003200000000000300064010000044C00000000007200640100000384000000000034006401000003840000000000140064010000051400000000006E006401000003E8000000000016006401000003E8000000000001006401000003200000000000430064010000057800000000006F006401000003840000000000330064010000044C00000000000B006401000003E800000000000F006401000006400000000000700064010000044C0000000000110064010000057800000000004C006401000003E8000000000059006401000006A400000000006D006401000005DC00000000004B006401000005DC000000000050006401000006400000000000350064010000070800000000006C0064010000044C000000000028006401000005DC00000000005300640100000640000000000060006401000005DC00000000005E0064010000051400000000007B006401000003E80000000000740064010000070800000000006B0064010000025800000000001B0064010000025800000000001C006401000002BC00000000001F006401000006A400000000007900640100000320000000000008006401000003E80000000000150064010000070800000000007A0064010000044C00000000000E00640100000640000000000055006401000007D0000000000002006401000005DC00000000002F0064010000064000000000002A0064010000076C00000000007E006401000002BC0000000000440064010000038400000000005C0064010000064000000000005B006401000006A400000000007D0064010000076C00000000007F006401000005DC0000000000540064010000064000000000002900640100000960000000000024006401000007D0000000000081006401000008340000000000800064010000038400000000001A006401000003E800000000002D0064010000038400000000004A006401000006A400000000005A00640100000384000000000027006401000007080000000000830064010000076C000000000040006401000006400000000000690064010000044C000000000025006401000004B000000000003100640100000708000000000082006401000003E800000000006500640100000640000000000051006401000007D000000000008C0064010000070800000000004D0064010000038400000000004E0064010000089800000000008B006401000004B000000000002E006401000009600000000000920064010000076C00000000008E00640100000514000000000068006401000004B000000000002B006401000003E800000000002C00640100000BB8000000000093006401000008FC00000000009000640100000AF0000000000094006401000006A400000000008D0064010000044C000000000052006401000005DC00000000004F006401000008980000000000970064010000070800000000006A0064010000064000000000005F00640100000384000000000026006401000008FC000000000096006401000007D00000000000980064010000076C000000000041006401000006A400000000003B006401000007080000000000360064010000083400000000009F00640100000A2800000000009A0064010000076C000000000021006401000007D000000000006300640100000A8C0000000000990064010000089800000000009E006401000007080000000000A100640100000C1C0000000000A200640100000C800000000000A400640100000DAC0000000000A600640100000C800000000000A50064010010") - doAckBufSucceed(s, pkt.AckHandle, data) + bf := byteframe.NewByteFrame() + var lbPrices []struct { + Unk0 uint16 + Unk1 uint16 + Unk2 uint32 + } + var wantedList []struct { + Unk0 uint32 + Unk1 uint32 + Unk2 uint32 + Unk3 uint16 + Unk4 uint16 + Unk5 uint16 + Unk6 uint16 + Unk7 uint16 + Unk8 uint16 + Unk9 uint16 + } + gzPrices := []struct { + Unk0 uint16 + Gz uint16 + Unk1 uint16 + Unk2 uint16 + MonID uint16 + Unk3 uint16 + Unk4 uint8 + }{ + {0, 1000, 0, 0, mhfmon.Pokaradon, 100, 1}, + {0, 800, 0, 0, mhfmon.YianKutKu, 100, 1}, + {0, 800, 0, 0, mhfmon.DaimyoHermitaur, 100, 1}, + {0, 1100, 0, 0, mhfmon.Farunokku, 100, 1}, + {0, 900, 0, 0, mhfmon.Congalala, 100, 1}, + {0, 900, 0, 0, mhfmon.Gypceros, 100, 1}, + {0, 1300, 0, 0, mhfmon.Hyujikiki, 100, 1}, + {0, 1000, 0, 0, mhfmon.Basarios, 100, 1}, + {0, 1000, 0, 0, mhfmon.Rathian, 100, 1}, + {0, 800, 0, 0, mhfmon.ShogunCeanataur, 100, 1}, + {0, 1400, 0, 0, mhfmon.Midogaron, 100, 1}, + {0, 900, 0, 0, mhfmon.Blangonga, 100, 1}, + {0, 1100, 0, 0, mhfmon.Rathalos, 100, 1}, + {0, 1000, 0, 0, mhfmon.Khezu, 100, 1}, + {0, 1600, 0, 0, mhfmon.Giaorugu, 100, 1}, + {0, 1100, 0, 0, mhfmon.Gravios, 100, 1}, + {0, 1400, 0, 0, mhfmon.Tigrex, 100, 1}, + {0, 1000, 0, 0, mhfmon.Pariapuria, 100, 1}, + {0, 1700, 0, 0, mhfmon.Anorupatisu, 100, 1}, + {0, 1500, 0, 0, mhfmon.Lavasioth, 100, 1}, + {0, 1500, 0, 0, mhfmon.Espinas, 100, 1}, + {0, 1600, 0, 0, mhfmon.Rajang, 100, 1}, + {0, 1800, 0, 0, mhfmon.Rebidiora, 100, 1}, + {0, 1100, 0, 0, mhfmon.YianGaruga, 100, 1}, + {0, 1500, 0, 0, mhfmon.AqraVashimu, 100, 1}, + {0, 1600, 0, 0, mhfmon.Gurenzeburu, 100, 1}, + {0, 1500, 0, 0, mhfmon.Dyuragaua, 100, 1}, + {0, 1300, 0, 0, mhfmon.Gougarf, 100, 1}, + {0, 1000, 0, 0, mhfmon.Shantien, 100, 1}, + {0, 1800, 0, 0, mhfmon.Disufiroa, 100, 1}, + {0, 600, 0, 0, mhfmon.Velocidrome, 100, 1}, + {0, 600, 0, 0, mhfmon.Gendrome, 100, 1}, + {0, 700, 0, 0, mhfmon.Iodrome, 100, 1}, + {0, 1700, 0, 0, mhfmon.Baruragaru, 100, 1}, + {0, 800, 0, 0, mhfmon.Cephadrome, 100, 1}, + {0, 1000, 0, 0, mhfmon.Plesioth, 100, 1}, + {0, 1800, 0, 0, mhfmon.Zerureusu, 100, 1}, + {0, 1100, 0, 0, mhfmon.Diablos, 100, 1}, + {0, 1600, 0, 0, mhfmon.Berukyurosu, 100, 1}, + {0, 2000, 0, 0, mhfmon.Fatalis, 100, 1}, + {0, 1500, 0, 0, mhfmon.BlackGravios, 100, 1}, + {0, 1600, 0, 0, mhfmon.GoldRathian, 100, 1}, + {0, 1900, 0, 0, mhfmon.Meraginasu, 100, 1}, + {0, 700, 0, 0, mhfmon.Bulldrome, 100, 1}, + {0, 900, 0, 0, mhfmon.NonoOrugaron, 100, 1}, + {0, 1600, 0, 0, mhfmon.KamuOrugaron, 100, 1}, + {0, 1700, 0, 0, mhfmon.Forokururu, 100, 1}, + {0, 1900, 0, 0, mhfmon.Diorex, 100, 1}, + {0, 1500, 0, 0, mhfmon.AqraJebia, 100, 1}, + {0, 1600, 0, 0, mhfmon.SilverRathalos, 100, 1}, + {0, 2400, 0, 0, mhfmon.CrimsonFatalis, 100, 1}, + {0, 2000, 0, 0, mhfmon.Inagami, 100, 1}, + {0, 2100, 0, 0, mhfmon.GarubaDaora, 100, 1}, + {0, 900, 0, 0, mhfmon.Monoblos, 100, 1}, + {0, 1000, 0, 0, mhfmon.RedKhezu, 100, 1}, + {0, 900, 0, 0, mhfmon.Hypnocatrice, 100, 1}, + {0, 1700, 0, 0, mhfmon.PearlEspinas, 100, 1}, + {0, 900, 0, 0, mhfmon.PurpleGypceros, 100, 1}, + {0, 1800, 0, 0, mhfmon.Poborubarumu, 100, 1}, + {0, 1900, 0, 0, mhfmon.Lunastra, 100, 1}, + {0, 1600, 0, 0, mhfmon.Kuarusepusu, 100, 1}, + {0, 1100, 0, 0, mhfmon.PinkRathian, 100, 1}, + {0, 1200, 0, 0, mhfmon.AzureRathalos, 100, 1}, + {0, 1800, 0, 0, mhfmon.Varusaburosu, 100, 1}, + {0, 1000, 0, 0, mhfmon.Gogomoa, 100, 1}, + {0, 1600, 0, 0, mhfmon.BurningEspinas, 100, 1}, + {0, 2000, 0, 0, mhfmon.Harudomerugu, 100, 1}, + {0, 1800, 0, 0, mhfmon.Akantor, 100, 1}, + {0, 900, 0, 0, mhfmon.BrightHypnoc, 100, 1}, + {0, 2200, 0, 0, mhfmon.Gureadomosu, 100, 1}, + {0, 1200, 0, 0, mhfmon.GreenPlesioth, 100, 1}, + {0, 2400, 0, 0, mhfmon.Zinogre, 100, 1}, + {0, 1900, 0, 0, mhfmon.Gasurabazura, 100, 1}, + {0, 1300, 0, 0, mhfmon.Abiorugu, 100, 1}, + {0, 1200, 0, 0, mhfmon.BlackDiablos, 100, 1}, + {0, 1000, 0, 0, mhfmon.WhiteMonoblos, 100, 1}, + {0, 3000, 0, 0, mhfmon.Deviljho, 100, 1}, + {0, 2300, 0, 0, mhfmon.YamaKurai, 100, 1}, + {0, 2800, 0, 0, mhfmon.Brachydios, 100, 1}, + {0, 1700, 0, 0, mhfmon.Toridcless, 100, 1}, + {0, 1100, 0, 0, mhfmon.WhiteHypnoc, 100, 1}, + {0, 1500, 0, 0, mhfmon.RedLavasioth, 100, 1}, + {0, 2200, 0, 0, mhfmon.Barioth, 100, 1}, + {0, 1800, 0, 0, mhfmon.Odibatorasu, 100, 1}, + {0, 1600, 0, 0, mhfmon.Doragyurosu, 100, 1}, + {0, 900, 0, 0, mhfmon.BlueYianKutKu, 100, 1}, + {0, 2300, 0, 0, mhfmon.ToaTesukatora, 100, 1}, + {0, 2000, 0, 0, mhfmon.Uragaan, 100, 1}, + {0, 1900, 0, 0, mhfmon.Teostra, 100, 1}, + {0, 1700, 0, 0, mhfmon.Chameleos, 100, 1}, + {0, 1800, 0, 0, mhfmon.KushalaDaora, 100, 1}, + {0, 2100, 0, 0, mhfmon.Nargacuga, 100, 1}, + {0, 2600, 0, 0, mhfmon.Guanzorumu, 100, 1}, + {0, 1900, 0, 0, mhfmon.Kirin, 100, 1}, + {0, 2000, 0, 0, mhfmon.Rukodiora, 100, 1}, + {0, 2700, 0, 0, mhfmon.StygianZinogre, 100, 1}, + {0, 2200, 0, 0, mhfmon.Voljang, 100, 1}, + {0, 1800, 0, 0, mhfmon.Zenaserisu, 100, 1}, + {0, 3100, 0, 0, mhfmon.GoreMagala, 100, 1}, + {0, 3200, 0, 0, mhfmon.ShagaruMagala, 100, 1}, + {0, 3500, 0, 0, mhfmon.Eruzerion, 100, 1}, + {0, 3200, 0, 0, mhfmon.Amatsu, 100, 1}, + } + + bf.WriteUint16(uint16(len(lbPrices))) + for _, lb := range lbPrices { + bf.WriteUint16(lb.Unk0) + bf.WriteUint16(lb.Unk1) + bf.WriteUint32(lb.Unk2) + } + bf.WriteUint16(uint16(len(wantedList))) + for _, wanted := range wantedList { + bf.WriteUint32(wanted.Unk0) + bf.WriteUint32(wanted.Unk1) + bf.WriteUint32(wanted.Unk2) + bf.WriteUint16(wanted.Unk3) + bf.WriteUint16(wanted.Unk4) + bf.WriteUint16(wanted.Unk5) + bf.WriteUint16(wanted.Unk6) + bf.WriteUint16(wanted.Unk7) + bf.WriteUint16(wanted.Unk8) + bf.WriteUint16(wanted.Unk9) + } + bf.WriteUint8(uint8(len(gzPrices))) + for _, gz := range gzPrices { + bf.WriteUint16(gz.Unk0) + bf.WriteUint16(gz.Gz) + bf.WriteUint16(gz.Unk1) + bf.WriteUint16(gz.Unk2) + bf.WriteUint16(gz.MonID) + bf.WriteUint16(gz.Unk3) + bf.WriteUint8(gz.Unk4) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -687,16 +862,16 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) { // Update item stacks newItems := make([]Item, len(oldItems)) copy(newItems, oldItems) - for i := 0; i < int(pkt.Amount); i++ { + for i := 0; i < len(pkt.Items); i++ { for j := 0; j <= len(oldItems); j++ { if j == len(oldItems) { var newItem Item - newItem.ItemId = pkt.Items[i].ItemId + newItem.ItemId = pkt.Items[i].ItemID newItem.Amount = pkt.Items[i].Amount newItems = append(newItems, newItem) break } - if pkt.Items[i].ItemId == oldItems[j].ItemId { + if pkt.Items[i].ItemID == oldItems[j].ItemId { newItems[j].Amount = pkt.Items[i].Amount break } @@ -779,782 +954,105 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func getGookData(s *Session, cid uint32) (uint16, []byte) { - var data []byte - var count uint16 - bf := byteframe.NewByteFrame() +func getGoocooData(s *Session, cid uint32) [][]byte { + var goocoo []byte + var goocoos [][]byte for i := 0; i < 5; i++ { - err := s.server.db.QueryRow(fmt.Sprintf("SELECT gook%d FROM gook WHERE id=$1", i), cid).Scan(&data) + err := s.server.db.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", i), cid).Scan(&goocoo) if err != nil { - s.server.db.Exec("INSERT INTO gook (id) VALUES ($1)", s.charID) - return 0, bf.Data() + s.server.db.Exec("INSERT INTO goocoo (id) VALUES ($1)", s.charID) + return goocoos } - if err == nil && data != nil { - count++ - if s.charID == cid && count == 1 { - gook := byteframe.NewByteFrameFromBytes(data) - bf.WriteBytes(gook.ReadBytes(4)) - d := gook.ReadBytes(2) - bf.WriteBytes(d) - bf.WriteBytes(d) - bf.WriteBytes(gook.DataFromCurrent()) - } else { - bf.WriteBytes(data) - } + if err == nil && goocoo != nil { + goocoos = append(goocoos, goocoo) } } - return count, bf.Data() + return goocoos } func handleMsgMhfEnumerateGuacot(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuacot) bf := byteframe.NewByteFrame() - count, data := getGookData(s, s.charID) - bf.WriteUint16(count) - bf.WriteBytes(data) + goocoos := getGoocooData(s, s.charID) + bf.WriteUint16(uint16(len(goocoos))) + bf.WriteUint16(0) + for _, goocoo := range goocoos { + bf.WriteBytes(goocoo) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateGuacot) - for _, gook := range pkt.Gooks { - if !gook.Exists { - s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=NULL WHERE id=$1", gook.Index), s.charID) + for _, goocoo := range pkt.Goocoos { + if goocoo.Data1[0] == 0 { + s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", goocoo.Index), s.charID) } else { bf := byteframe.NewByteFrame() - bf.WriteUint32(gook.Index) - bf.WriteUint16(gook.Type) - bf.WriteBytes(gook.Data) - bf.WriteUint8(gook.NameLen) - bf.WriteBytes(gook.Name) - s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=$1 WHERE id=$2", gook.Index), bf.Data(), s.charID) - dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", gook.Index)) + bf.WriteUint32(goocoo.Index) + for i := range goocoo.Data1 { + bf.WriteInt16(goocoo.Data1[i]) + } + for i := range goocoo.Data2 { + bf.WriteUint32(goocoo.Data2[i]) + } + bf.WriteUint8(uint8(len(goocoo.Name))) + bf.WriteBytes(goocoo.Name) + s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", goocoo.Index), bf.Data(), s.charID) + dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", goocoo.Index)) } } - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + +type Scenario struct { + MainID uint32 + // 0 = Basic + // 1 = Veteran + // 3 = Other + // 6 = Pallone + // 7 = Diva + CategoryID uint8 } func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfInfoScenarioCounter) - scenarioCounter := []struct { - MainID uint32 - Unk1 uint8 // Bool item exchange? - // 0 = basic, 1 = veteran, 3 = other, 6 = pallone, 7 = diva - CategoryID uint8 - }{ - //000000110000 - { - MainID: 0x00000011, Unk1: 0, CategoryID: 0, - }, - // 0000005D0001 - { - MainID: 0x0000005D, Unk1: 0, CategoryID: 1, - }, - // 0000005C0001 - { - MainID: 0x0000005C, Unk1: 0, CategoryID: 1, - }, - // 000000510001 - { - MainID: 0x00000051, Unk1: 0, CategoryID: 1, - }, - // 0000005B0001 - { - MainID: 0x0000005B, Unk1: 0, CategoryID: 1, - }, - // 0000005A0001 - { - MainID: 0x0000005A, Unk1: 0, CategoryID: 1, - }, - // 000000590001 - { - MainID: 0x00000059, Unk1: 0, CategoryID: 1, - }, - // 000000580001 - { - MainID: 0x00000058, Unk1: 0, CategoryID: 1, - }, - // 000000570001 - { - MainID: 0x00000057, Unk1: 0, CategoryID: 1, - }, - // 000000560001 - { - MainID: 0x00000056, Unk1: 0, CategoryID: 1, - }, - // 000000550001 - { - MainID: 0x00000055, Unk1: 0, CategoryID: 1, - }, - // 000000540001 - { - MainID: 0x00000054, Unk1: 0, CategoryID: 1, - }, - // 000000530001 - { - MainID: 0x00000053, Unk1: 0, CategoryID: 1, - }, - // 000000520001 - { - MainID: 0x00000052, Unk1: 0, CategoryID: 1, - }, - // 000000570103 - { - MainID: 0x00000057, Unk1: 1, CategoryID: 3, - }, - // 000000580103 - { - MainID: 0x00000058, Unk1: 1, CategoryID: 3, - }, - // 000000590103 - { - MainID: 0x00000059, Unk1: 1, CategoryID: 3, - }, - // 0000005A0103 - { - MainID: 0x0000005A, Unk1: 1, CategoryID: 3, - }, - // 0000005B0103 - { - MainID: 0x0000005B, Unk1: 1, CategoryID: 3, - }, - // 0000005C0103 - { - MainID: 0x0000005C, Unk1: 1, CategoryID: 3, - }, - // 000000530103 - { - MainID: 0x00000053, Unk1: 1, CategoryID: 3, - }, - // 000000560103 - { - MainID: 0x00000056, Unk1: 1, CategoryID: 3, - }, - // 0000003C0103 - { - MainID: 0x0000003C, Unk1: 1, CategoryID: 3, - }, - // 0000003A0103 - { - MainID: 0x0000003A, Unk1: 1, CategoryID: 3, - }, - // 0000003B0103 - { - MainID: 0x0000003B, Unk1: 1, CategoryID: 3, - }, - // 0000001B0103 - { - MainID: 0x0000001B, Unk1: 1, CategoryID: 3, - }, - // 000000190103 - { - MainID: 0x00000019, Unk1: 1, CategoryID: 3, - }, - // 0000001A0103 - { - MainID: 0x0000001A, Unk1: 1, CategoryID: 3, - }, - // 000000170103 - { - MainID: 0x00000017, Unk1: 1, CategoryID: 3, - }, - // 000000020103 - { - MainID: 0x00000002, Unk1: 1, CategoryID: 3, - }, - // 000000030103 - { - MainID: 0x00000003, Unk1: 1, CategoryID: 3, - }, - // 000000040103 - { - MainID: 0x00000004, Unk1: 1, CategoryID: 3, - }, - // 0000001F0103 - { - MainID: 0x0000001F, Unk1: 1, CategoryID: 3, - }, - // 000000200103 - { - MainID: 0x00000020, Unk1: 1, CategoryID: 3, - }, - // 000000210103 - { - MainID: 0x00000021, Unk1: 1, CategoryID: 3, - }, - // 000000220103 - { - MainID: 0x00000022, Unk1: 1, CategoryID: 3, - }, - // 000000230103 - { - MainID: 0x00000023, Unk1: 1, CategoryID: 3, - }, - // 000000240103 - { - MainID: 0x00000024, Unk1: 1, CategoryID: 3, - }, - // 000000250103 - { - MainID: 0x00000025, Unk1: 1, CategoryID: 3, - }, - // 000000280103 - { - MainID: 0x00000028, Unk1: 1, CategoryID: 3, - }, - // 000000260103 - { - MainID: 0x00000026, Unk1: 1, CategoryID: 3, - }, - // 000000270103 - { - MainID: 0x00000027, Unk1: 1, CategoryID: 3, - }, - // 000000300103 - { - MainID: 0x00000030, Unk1: 1, CategoryID: 3, - }, - // 0000000C0103 - { - MainID: 0x0000000C, Unk1: 1, CategoryID: 3, - }, - // 0000000D0103 - { - MainID: 0x0000000D, Unk1: 1, CategoryID: 3, - }, - // 0000001E0103 - { - MainID: 0x0000001E, Unk1: 1, CategoryID: 3, - }, - // 0000001D0103 - { - MainID: 0x0000001D, Unk1: 1, CategoryID: 3, - }, - // 0000002E0003 - { - MainID: 0x0000002E, Unk1: 0, CategoryID: 3, - }, - // 000000000004 - { - MainID: 0x00000000, Unk1: 0, CategoryID: 4, - }, - // 000000010004 - { - MainID: 0x00000001, Unk1: 0, CategoryID: 4, - }, - // 000000020004 - { - MainID: 0x00000002, Unk1: 0, CategoryID: 4, - }, - // 000000030004 - { - MainID: 0x00000003, Unk1: 0, CategoryID: 4, - }, - // 000000040004 - { - MainID: 0x00000004, Unk1: 0, CategoryID: 4, - }, - // 000000050004 - { - MainID: 0x00000005, Unk1: 0, CategoryID: 4, - }, - // 000000060004 - { - MainID: 0x00000006, Unk1: 0, CategoryID: 4, - }, - // 000000070004 - { - MainID: 0x00000007, Unk1: 0, CategoryID: 4, - }, - // 000000080004 - { - MainID: 0x00000008, Unk1: 0, CategoryID: 4, - }, - // 000000090004 - { - MainID: 0x00000009, Unk1: 0, CategoryID: 4, - }, - // 0000000A0004 - { - MainID: 0x0000000A, Unk1: 0, CategoryID: 4, - }, - // 0000000B0004 - { - MainID: 0x0000000B, Unk1: 0, CategoryID: 4, - }, - // 0000000C0004 - { - MainID: 0x0000000C, Unk1: 0, CategoryID: 4, - }, - // 0000000D0004 - { - MainID: 0x0000000D, Unk1: 0, CategoryID: 4, - }, - // 0000000E0004 - { - MainID: 0x0000000E, Unk1: 0, CategoryID: 4, - }, - // 000000320005 - { - MainID: 0x00000032, Unk1: 0, CategoryID: 5, - }, - // 000000330005 - { - MainID: 0x00000033, Unk1: 0, CategoryID: 5, - }, - // 000000340005 - { - MainID: 0x00000034, Unk1: 0, CategoryID: 5, - }, - // 000000350005 - { - MainID: 0x00000035, Unk1: 0, CategoryID: 5, - }, - // 000000360005 - { - MainID: 0x00000036, Unk1: 0, CategoryID: 5, - }, - // 000000370005 - { - MainID: 0x00000037, Unk1: 0, CategoryID: 5, - }, - // 000000380005 - { - MainID: 0x00000038, Unk1: 0, CategoryID: 5, - }, - // 0000003A0005 - { - MainID: 0x0000003A, Unk1: 0, CategoryID: 5, - }, - // 0000003F0005 - { - MainID: 0x0000003F, Unk1: 0, CategoryID: 5, - }, - // 000000400005 - { - MainID: 0x00000040, Unk1: 0, CategoryID: 5, - }, - // 000000410005 - { - MainID: 0x00000041, Unk1: 0, CategoryID: 5, - }, - // 000000430005 - { - MainID: 0x00000043, Unk1: 0, CategoryID: 5, - }, - // 000000470005 - { - MainID: 0x00000047, Unk1: 0, CategoryID: 5, - }, - // 0000004B0005 - { - MainID: 0x0000004B, Unk1: 0, CategoryID: 5, - }, - // 0000003D0005 - { - MainID: 0x0000003D, Unk1: 0, CategoryID: 5, - }, - // 000000440005 - { - MainID: 0x00000044, Unk1: 0, CategoryID: 5, - }, - // 000000420005 - { - MainID: 0x00000042, Unk1: 0, CategoryID: 5, - }, - // 0000004C0005 - { - MainID: 0x0000004C, Unk1: 0, CategoryID: 5, - }, - // 000000460005 - { - MainID: 0x00000046, Unk1: 0, CategoryID: 5, - }, - // 0000004D0005 - { - MainID: 0x0000004D, Unk1: 0, CategoryID: 5, - }, - // 000000480005 - { - MainID: 0x00000048, Unk1: 0, CategoryID: 5, - }, - // 0000004A0005 - { - MainID: 0x0000004A, Unk1: 0, CategoryID: 5, - }, - // 000000490005 - { - MainID: 0x00000049, Unk1: 0, CategoryID: 5, - }, - // 0000004E0005 - { - MainID: 0x0000004E, Unk1: 0, CategoryID: 5, - }, - // 000000450005 - { - MainID: 0x00000045, Unk1: 0, CategoryID: 5, - }, - // 0000003E0005 - { - MainID: 0x0000003E, Unk1: 0, CategoryID: 5, - }, - // 0000004F0005 - { - MainID: 0x0000004F, Unk1: 0, CategoryID: 5, - }, - // 000000000106 - { - MainID: 0x00000000, Unk1: 1, CategoryID: 6, - }, - // 000000010106 - { - MainID: 0x00000001, Unk1: 1, CategoryID: 6, - }, - // 000000020106 - { - MainID: 0x00000002, Unk1: 1, CategoryID: 6, - }, - // 000000030106 - { - MainID: 0x00000003, Unk1: 1, CategoryID: 6, - }, - // 000000040106 - { - MainID: 0x00000004, Unk1: 1, CategoryID: 6, - }, - // 000000050106 - { - MainID: 0x00000005, Unk1: 1, CategoryID: 6, - }, - // 000000060106 - { - MainID: 0x00000006, Unk1: 1, CategoryID: 6, - }, - // 000000070106 - { - MainID: 0x00000007, Unk1: 1, CategoryID: 6, - }, - // 000000080106 - { - MainID: 0x00000008, Unk1: 1, CategoryID: 6, - }, - // 000000090106 - { - MainID: 0x00000009, Unk1: 1, CategoryID: 6, - }, - // 000000110106 - { - MainID: 0x00000011, Unk1: 1, CategoryID: 6, - }, - // 0000000A0106 - { - MainID: 0x0000000A, Unk1: 1, CategoryID: 6, - }, - // 0000000B0106 - { - MainID: 0x0000000B, Unk1: 1, CategoryID: 6, - }, - // 0000000C0106 - { - MainID: 0x0000000C, Unk1: 1, CategoryID: 6, - }, - // 0000000D0106 - { - MainID: 0x0000000D, Unk1: 1, CategoryID: 6, - }, - // 0000000E0106 - { - MainID: 0x0000000E, Unk1: 1, CategoryID: 6, - }, - // 0000000F0106 - { - MainID: 0x0000000F, Unk1: 1, CategoryID: 6, - }, - // 000000100106 - { - MainID: 0x00000010, Unk1: 1, CategoryID: 6, - }, - // 000000320107 - { - MainID: 0x00000032, Unk1: 1, CategoryID: 7, - }, - // 000000350107 - { - MainID: 0x00000035, Unk1: 1, CategoryID: 7, - }, - // 0000003E0107 - { - MainID: 0x0000003E, Unk1: 1, CategoryID: 7, - }, - // 000000340107 - { - MainID: 0x00000034, Unk1: 1, CategoryID: 7, - }, - // 000000380107 - { - MainID: 0x00000038, Unk1: 1, CategoryID: 7, - }, - // 000000330107 - { - MainID: 0x00000033, Unk1: 1, CategoryID: 7, - }, - // 000000310107 - { - MainID: 0x00000031, Unk1: 1, CategoryID: 7, - }, - // 000000360107 - { - MainID: 0x00000036, Unk1: 1, CategoryID: 7, - }, - // 000000390107 - { - MainID: 0x00000039, Unk1: 1, CategoryID: 7, - }, - // 000000370107 - { - MainID: 0x00000037, Unk1: 1, CategoryID: 7, - }, - // 0000003D0107 - { - MainID: 0x0000003D, Unk1: 1, CategoryID: 7, - }, - // 0000003A0107 - { - MainID: 0x0000003A, Unk1: 1, CategoryID: 7, - }, - // 0000003C0107 - { - MainID: 0x0000003C, Unk1: 1, CategoryID: 7, - }, - // 0000003B0107 - { - MainID: 0x0000003B, Unk1: 1, CategoryID: 7, - }, - // 0000002A0107 - { - MainID: 0x0000002A, Unk1: 1, CategoryID: 7, - }, - // 000000300107 - { - MainID: 0x00000030, Unk1: 1, CategoryID: 7, - }, - // 000000280107 - { - MainID: 0x00000028, Unk1: 1, CategoryID: 7, - }, - // 000000270107 - { - MainID: 0x00000027, Unk1: 1, CategoryID: 7, - }, - // 0000002B0107 - { - MainID: 0x0000002B, Unk1: 1, CategoryID: 7, - }, - // 0000002E0107 - { - MainID: 0x0000002E, Unk1: 1, CategoryID: 7, - }, - // 000000290107 - { - MainID: 0x00000029, Unk1: 1, CategoryID: 7, - }, - // 0000002C0107 - { - MainID: 0x0000002C, Unk1: 1, CategoryID: 7, - }, - // 0000002D0107 - { - MainID: 0x0000002D, Unk1: 1, CategoryID: 7, - }, - // 0000002F0107 - { - MainID: 0x0000002F, Unk1: 1, CategoryID: 7, - }, - // 000000250107 - { - MainID: 0x00000025, Unk1: 1, CategoryID: 7, - }, - // 000000220107 - { - MainID: 0x00000022, Unk1: 1, CategoryID: 7, - }, - // 000000210107 - { - MainID: 0x00000021, Unk1: 1, CategoryID: 7, - }, - // 000000200107 - { - MainID: 0x00000020, Unk1: 1, CategoryID: 7, - }, - // 0000001C0107 - { - MainID: 0x0000001C, Unk1: 1, CategoryID: 7, - }, - // 0000001A0107 - { - MainID: 0x0000001A, Unk1: 1, CategoryID: 7, - }, - // 000000240107 - { - MainID: 0x00000024, Unk1: 1, CategoryID: 7, - }, - // 000000260107 - { - MainID: 0x00000026, Unk1: 1, CategoryID: 7, - }, - // 000000230107 - { - MainID: 0x00000023, Unk1: 1, CategoryID: 7, - }, - // 0000001B0107 - { - MainID: 0x0000001B, Unk1: 1, CategoryID: 7, - }, - // 0000001E0107 - { - MainID: 0x0000001E, Unk1: 1, CategoryID: 7, - }, - // 0000001F0107 - { - MainID: 0x0000001F, Unk1: 1, CategoryID: 7, - }, - // 0000001D0107 - { - MainID: 0x0000001D, Unk1: 1, CategoryID: 7, - }, - // 000000180107 - { - MainID: 0x00000018, Unk1: 1, CategoryID: 7, - }, - // 000000170107 - { - MainID: 0x00000017, Unk1: 1, CategoryID: 7, - }, - // 000000160107 - { - MainID: 0x00000016, Unk1: 1, CategoryID: 7, - }, - // 000000150107 - // Missing file - // { - // MainID: 0x00000015, Unk1: 1, CategoryID: 7, - // }, - // 000000190107 - { - MainID: 0x00000019, Unk1: 1, CategoryID: 7, - }, - // 000000140107 - // Missing file - // { - // MainID: 0x00000014, Unk1: 1, CategoryID: 7, - // }, - // 000000070107 - // Missing file - // { - // MainID: 0x00000007, Unk1: 1, CategoryID: 7, - // }, - // 000000090107 - // Missing file - // { - // MainID: 0x00000009, Unk1: 1, CategoryID: 7, - // }, - // 0000000D0107 - // Missing file - // { - // MainID: 0x0000000D, Unk1: 1, CategoryID: 7, - // }, - // 000000100107 - // Missing file - // { - // MainID: 0x00000010, Unk1: 1, CategoryID: 7, - // }, - // 0000000C0107 - // Missing file - // { - // MainID: 0x0000000C, Unk1: 1, CategoryID: 7, - // }, - // 0000000E0107 - // Missing file - // { - // MainID: 0x0000000E, Unk1: 1, CategoryID: 7, - // }, - // 0000000F0107 - // Missing file - // { - // MainID: 0x0000000F, Unk1: 1, CategoryID: 7, - // }, - // 000000130107 - // Missing file - // { - // MainID: 0x00000013, Unk1: 1, CategoryID: 7, - // }, - // 0000000A0107 - // Missing file - // { - // MainID: 0x0000000A, Unk1: 1, CategoryID: 7, - // }, - // 000000080107 - // Missing file - // { - // MainID: 0x00000008, Unk1: 1, CategoryID: 7, - // }, - // 0000000B0107 - // Missing file - // { - // MainID: 0x0000000B, Unk1: 1, CategoryID: 7, - // }, - // 000000120107 - // Missing file - // { - // MainID: 0x00000012, Unk1: 1, CategoryID: 7, - // }, - // 000000110107 - // Missing file - // { - // MainID: 0x00000011, Unk1: 1, CategoryID: 7, - // }, - // 000000060107 - // Missing file - // { - // MainID: 0x00000006, Unk1: 1, CategoryID: 7, - // }, - // 000000050107 - // Missing file - // { - // MainID: 0x00000005, Unk1: 1, CategoryID: 7, - // }, - // 000000040107 - // Missing file - // { - // MainID: 0x00000004, Unk1: 1, CategoryID: 7, - // }, - // 000000030107 - { - MainID: 0x00000003, Unk1: 1, CategoryID: 7, - }, - // 000000020107 - { - MainID: 0x00000002, Unk1: 1, CategoryID: 7, - }, - // 000000010107 - { - MainID: 0x00000001, Unk1: 1, CategoryID: 7, - }, - // 000000000107 - { - MainID: 0x00000000, Unk1: 1, CategoryID: 7, - }, + var scenarios []Scenario + var scenario Scenario + scenarioData, err := s.server.db.Queryx("SELECT scenario_id, category_id FROM scenario_counter") + if err != nil { + scenarioData.Close() + s.logger.Error("Failed to get scenario counter info from db", zap.Error(err)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + for scenarioData.Next() { + err = scenarioData.Scan(&scenario.MainID, &scenario.CategoryID) + if err != nil { + continue + } + scenarios = append(scenarios, scenario) } - resp := byteframe.NewByteFrame() - resp.WriteUint8(uint8(len(scenarioCounter))) // Entry count - for _, entry := range scenarioCounter { - resp.WriteUint32(entry.MainID) - resp.WriteUint8(entry.Unk1) - resp.WriteUint8(entry.CategoryID) + // Trim excess scenarios + if len(scenarios) > 128 { + scenarios = scenarios[:128] } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf := byteframe.NewByteFrame() + bf.WriteUint8(uint8(len(scenarios))) + for _, scenario := range scenarios { + bf.WriteUint32(scenario.MainID) + // If item exchange + switch scenario.CategoryID { + case 3, 6, 7: + bf.WriteBool(true) + default: + bf.WriteBool(false) + } + bf.WriteUint8(scenario.CategoryID) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) { @@ -1589,10 +1087,10 @@ func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) { column = "promo_points" } - var value int + var value int16 err := s.server.db.QueryRow(fmt.Sprintf(`SELECT %s FROM characters WHERE id = $1`, column), s.charID).Scan(&value) if err == nil { - if value-int(pkt.Delta) < 0 { + if value+pkt.Delta < 0 { s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = 0 WHERE id = $1`, column), s.charID) } else { s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = %s + $1 WHERE id = $2`, column, column), pkt.Delta, s.charID) @@ -1632,21 +1130,31 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUnreserveSrg) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetEarthStatus) bf := byteframe.NewByteFrame() - bf.WriteUint32(uint32(TimeWeekStart().Add(time.Hour * -24).Unix())) // Start - bf.WriteUint32(uint32(TimeWeekNext().Add(time.Hour * 24).Unix())) // End - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride) - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride) - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride) - bf.WriteInt32(0) - bf.WriteInt32(0) - bf.WriteInt32(0) + bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start + bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End + 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 + } + } + if i == 4 { + break + } + bf.WriteInt32(m) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1846,7 +1354,10 @@ func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) { doAckEarthSucceed(s, pkt.AckHandle, data) } -func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostSeibattle) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} func handleMsgMhfGetDailyMissionMaster(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 67b7c3807..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()) } @@ -220,7 +220,7 @@ func addPointNetcafe(s *Session, p int) error { func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStartBoostTime) bf := byteframe.NewByteFrame() - boostLimit := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Minute) + boostLimit := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Second) if s.server.erupeConfig.GameplayOptions.DisableBoostTime { bf.WriteUint32(0) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index a0b8b6ca9..67e6e3a3a 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -1,11 +1,14 @@ 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" + "erupe-ce/network" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "fmt" @@ -57,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) { @@ -74,7 +77,7 @@ func sendServerChatMessage(s *Session, message string) { msgBinChat.Build(bf) castedBin := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, + CharID: 0, MessageType: BinaryMessageTypeChat, RawDataPayload: bf.Data(), } @@ -83,30 +86,99 @@ func sendServerChatMessage(s *Session, message string) { } func parseChatCommand(s *Session, command string) { - args := strings.Split(command[1:], " ") + 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 { @@ -125,7 +197,7 @@ func parseChatCommand(s *Session, command string) { deleteNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(deleteNotif, s.clientContext) } - deleteNotif.WriteUint16(0x0010) + deleteNotif.WriteUint16(uint16(network.MSG_SYS_END)) s.QueueSend(deleteNotif.Data()) time.Sleep(500 * time.Millisecond) reloadNotif := byteframe.NewByteFrame() @@ -160,24 +232,28 @@ func parseChatCommand(s *Session, command string) { reloadNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(reloadNotif, s.clientContext) } - reloadNotif.WriteUint16(0x0010) + reloadNotif.WriteUint16(uint16(network.MSG_SYS_END)) s.QueueSend(reloadNotif.Data()) } else { sendDisabledCommandMessage(s, commands["Reload"]) } case commands["KeyQuest"].Prefix: - if commands["KeyQuest"].Enabled { - if len(args) > 1 { - if args[1] == "get" { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandKqfGet"], 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"]) - } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandKqfSetError"], commands["KeyQuest"].Prefix)) + if commands["KeyQuest"].Enabled || s.isOp() { + if s.server.erupeConfig.RealClientMode < _config.G10 { + sendServerChatMessage(s, s.server.i18n.commands.kqf.version) + } else { + if len(args) > 1 { + if args[1] == "get" { + 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.i18n.commands.kqf.set.success) + } else { + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.set.error, commands["KeyQuest"].Prefix)) + } } } } @@ -185,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() { @@ -219,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 { @@ -231,67 +307,74 @@ 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 getRaviSemaphore(s.server) != nil { + if s.server.getRaviSemaphore() != nil { switch args[1] { case "start": - if s.server.raviente.register.startTime == 0 { - s.server.raviente.register.startTime = s.server.raviente.register.postTime - sendServerChatMessage(s, s.server.dict["commandRaviStartSuccess"]) + if s.server.raviente.register[1] == 0 { + s.server.raviente.register[1] = s.server.raviente.register[3] + 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.raviente.GetRaviMultiplier(s.server))) - case "sr", "sendres", "resurrection": - if s.server.raviente.state.stateData[28] > 0 { - sendServerChatMessage(s, s.server.dict["commandRaviResSuccess"]) - s.server.raviente.state.stateData[28] = 0 + 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.i18n.commands.ravi.res.success) + s.server.raviente.state[28] = 0 + } else { + sendServerChatMessage(s, s.server.i18n.commands.ravi.res.error) + } + case "ss", "sendsed": + 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.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["commandRaviResError"]) + sendServerChatMessage(s, s.server.i18n.commands.ravi.version) } - case "ss", "sendsed": - sendServerChatMessage(s, s.server.dict["commandRaviSedSuccess"]) - // Total BerRavi HP - HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] - s.server.raviente.support.supportData[1] = HP - case "rs", "reqsed": - sendServerChatMessage(s, s.server.dict["commandRaviRequest"]) - // Total BerRavi HP - HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] - s.server.raviente.support.supportData[1] = HP + 12 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.Atoi(args[1]) - y, _ := strconv.Atoi(args[2]) + x, _ := strconv.ParseInt(args[1], 10, 16) + y, _ := strconv.ParseInt(args[2], 10, 16) payload := byteframe.NewByteFrame() payload.SetLE() payload.WriteUint8(2) // SetState type(position == 2) @@ -303,13 +386,37 @@ 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 || s.isOp() { + for _, command := range commands { + if command.Enabled || s.isOp() { + sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", s.server.erupeConfig.CommandPrefix, command.Prefix, command.Description)) + } + } + } else { + sendDisabledCommandMessage(s, commands["Help"]) + } } } @@ -319,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) @@ -340,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 { @@ -366,24 +473,24 @@ 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() chatMessage := &binpacket.MsgBinChat{} chatMessage.Parse(bf) - if strings.HasPrefix(chatMessage.Message, "!") { + if strings.HasPrefix(chatMessage.Message, s.server.erupeConfig.CommandPrefix) { parseChatCommand(s, chatMessage.Message) return } @@ -406,15 +513,16 @@ 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) } case BroadcastTypeServer: if pkt.MessageType == 1 { - if getRaviSemaphore(s.server) != nil { - s.server.BroadcastMHF(resp, s) + raviSema := s.server.getRaviSemaphore() + if raviSema != nil { + raviSema.BroadcastMHF(resp, s) } } else { s.server.BroadcastMHF(resp, s) diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index bdbd2f744..310fa0221 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -19,7 +19,7 @@ const ( pRP // +2 pHouseTier // +5 pHouseData // +195 - pBookshelfData // +5576 + pBookshelfData // +lBookshelfData pGalleryData // +1748 pToreData // +240 pGardenData // +68 @@ -28,6 +28,7 @@ const ( pHRP // +2 pGRP // +4 pKQF // +8 + lBookshelfData ) type CharacterSaveData struct { @@ -55,7 +56,7 @@ type CharacterSaveData struct { } func getPointers() map[SavePointer]int { - pointers := map[SavePointer]int{pGender: 81} + pointers := map[SavePointer]int{pGender: 81, lBookshelfData: 5576} switch _config.ErupeConfig.RealClientMode { case _config.ZZ: pointers[pWeaponID] = 128522 @@ -70,7 +71,9 @@ func getPointers() map[SavePointer]int { pointers[pGardenData] = 142424 pointers[pRP] = 142614 pointers[pKQF] = 146720 - case _config.Z2, _config.Z1, _config.G101, _config.G10: + case _config.Z2, _config.Z1, _config.G101, _config.G10, _config.G91, _config.G9, _config.G81, _config.G8, + _config.G7, _config.G61, _config.G6, _config.G52, _config.G51, _config.G5, _config.GG, _config.G32, _config.G31, + _config.G3, _config.G2, _config.G1: pointers[pWeaponID] = 92522 pointers[pWeaponType] = 92789 pointers[pHouseTier] = 93900 @@ -83,6 +86,33 @@ func getPointers() map[SavePointer]int { pointers[pGardenData] = 106424 pointers[pRP] = 106614 pointers[pKQF] = 110720 + case _config.F5, _config.F4: + pointers[pWeaponID] = 60522 + pointers[pWeaponType] = 60789 + pointers[pHouseTier] = 61900 + pointers[pToreData] = 62228 + pointers[pHRP] = 62550 + pointers[pHouseData] = 62561 + pointers[pBookshelfData] = 57118 // This pointer only half works + 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 + } else if _config.ErupeConfig.RealClientMode <= _config.GG { + pointers[lBookshelfData] = 4520 } return pointers } @@ -176,8 +206,10 @@ func (save *CharacterSaveData) Decompress() error { func (save *CharacterSaveData) updateSaveDataWithStruct() { rpBytes := make([]byte, 2) binary.LittleEndian.PutUint16(rpBytes, save.RP) - if _config.ErupeConfig.RealClientMode >= _config.G10 { + if _config.ErupeConfig.RealClientMode >= _config.F4 { copy(save.decompSave[save.Pointers[pRP]:save.Pointers[pRP]+2], rpBytes) + } + if _config.ErupeConfig.RealClientMode >= _config.G10 { copy(save.decompSave[save.Pointers[pKQF]:save.Pointers[pKQF]+8], save.KQF) } } @@ -191,21 +223,25 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.Gender = false } if !save.IsNewCharacter { - if _config.ErupeConfig.RealClientMode >= _config.G10 { + 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] - save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+5576] + save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+save.Pointers[lBookshelfData]] save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748] save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240] save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68] save.WeaponType = save.decompSave[save.Pointers[pWeaponType]] save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2]) save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2]) - if save.HRP == uint16(999) { - save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])) + if _config.ErupeConfig.RealClientMode >= _config.G1 { + if save.HRP == uint16(999) { + save.GR = grpToGR(int(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4]))) + } + } + if _config.ErupeConfig.RealClientMode >= _config.G10 { + save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8] } - save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8] } } return diff --git a/server/channelserver/handlers_clients.go b/server/channelserver/handlers_clients.go index 12e8540b3..4add1e9eb 100644 --- a/server/channelserver/handlers_clients.go +++ b/server/channelserver/handlers_clients.go @@ -29,6 +29,9 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { for _, cid := range stage.clients { clients = append(clients, cid) } + for cid := range stage.reservedClientSlots { + clients = append(clients, cid) + } case 1: // Not ready for cid, ready := range stage.reservedClientSlots { if !ready { @@ -60,20 +63,19 @@ func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) { resp := byteframe.NewByteFrame() resp.WriteUint32(0) // Blacklist count err := s.server.db.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.charID).Scan(&csv) - if err != nil { - panic(err) - } - cids := stringsupport.CSVElems(csv) - for _, cid := range cids { - var name string - err = s.server.db.QueryRow("SELECT name FROM characters WHERE id=$1", cid).Scan(&name) - if err != nil { - continue + if err == nil { + cids := stringsupport.CSVElems(csv) + for _, cid := range cids { + var name string + err = s.server.db.QueryRow("SELECT name FROM characters WHERE id=$1", cid).Scan(&name) + if err != nil { + continue + } + count++ + resp.WriteUint32(uint32(cid)) + resp.WriteUint32(16) + resp.WriteBytes(stringsupport.PaddedString(name, 16, true)) } - count++ - resp.WriteUint32(uint32(cid)) - resp.WriteUint32(16) - resp.WriteBytes(stringsupport.PaddedString(name, 16, true)) } resp.Seek(0, 0) resp.WriteUint32(count) @@ -83,28 +85,28 @@ func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfOprMember(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOprMember) var csv string - if pkt.Blacklist { - err := s.server.db.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.charID).Scan(&csv) - if err != nil { - panic(err) + for _, cid := range pkt.CharIDs { + if pkt.Blacklist { + err := s.server.db.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.charID).Scan(&csv) + if err == nil { + if pkt.Operation { + csv = stringsupport.CSVRemove(csv, int(cid)) + } else { + csv = stringsupport.CSVAdd(csv, int(cid)) + } + s.server.db.Exec("UPDATE characters SET blocked=$1 WHERE id=$2", csv, s.charID) + } + } else { // Friendlist + err := s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&csv) + if err == nil { + if pkt.Operation { + csv = stringsupport.CSVRemove(csv, int(cid)) + } else { + csv = stringsupport.CSVAdd(csv, int(cid)) + } + s.server.db.Exec("UPDATE characters SET friends=$1 WHERE id=$2", csv, s.charID) + } } - if pkt.Operation { - csv = stringsupport.CSVRemove(csv, int(pkt.CharID)) - } else { - csv = stringsupport.CSVAdd(csv, int(pkt.CharID)) - } - s.server.db.Exec("UPDATE characters SET blocked=$1 WHERE id=$2", csv, s.charID) - } else { // Friendlist - err := s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&csv) - if err != nil { - panic(err) - } - if pkt.Operation { - csv = stringsupport.CSVRemove(csv, int(pkt.CharID)) - } else { - csv = stringsupport.CSVAdd(csv, int(pkt.CharID)) - } - s.server.db.Exec("UPDATE characters SET friends=$1 WHERE id=$2", csv, s.charID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index 2d95e97d0..fd41e1366 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -1,7 +1,9 @@ package channelserver import ( + "erupe-ce/common/mhfmon" "erupe-ce/common/stringsupport" + _config "erupe-ce/config" "fmt" "io" "os" @@ -44,6 +46,9 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } + if s.server.erupeConfig.SaveDumps.RawEnabled { + dumpSaveData(s, saveData, "raw-savedata") + } s.logger.Info("Updating save with blob") characterSaveData.decompSave = saveData } @@ -54,7 +59,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { s.Name = characterSaveData.Name } - if characterSaveData.Name == s.Name { + if characterSaveData.Name == s.Name || _config.ErupeConfig.RealClientMode <= _config.S10 { characterSaveData.Save(s) s.logger.Info("Wrote recompressed savedata back to DB.") } else { @@ -72,170 +77,47 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -func grpToGR(n uint32) uint16 { - var gr uint16 - gr = 1 - switch grp := int(n); { - case grp < 208750: // Up to 50 - i := 0 - for { - grp -= 500 - if grp <= 500 { - if grp < 0 { - i-- +func grpToGR(n int) uint16 { + var gr int + a := []int{208750, 593400, 993400, 1400900, 2315900, 3340900, 4505900, 5850900, 7415900, 9230900, 11345900, 100000000} + b := []int{7850, 8000, 8150, 9150, 10250, 11650, 13450, 15650, 18150, 21150, 23950} + c := []int{51, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900} + + for i := 0; i < len(a); i++ { + if n < a[i] { + if i == 0 { + for { + n -= 500 + if n <= 500 { + if n < 0 { + i-- + } + break + } else { + i++ + for j := 0; j < i; j++ { + n -= 150 + } + } } - break + gr = i + 2 } else { - i++ - for j := 0; j < i; j++ { - grp -= 150 - } + n -= a[i-1] + gr = c[i-1] + gr += n / b[i-1] } + break } - gr = uint16(i + 2) - break - case grp < 593400: // 51-99 - grp -= 208750 - i := 51 - for { - if grp < 7850 { - break - } - i++ - grp -= 7850 - } - gr = uint16(i) - break - case grp < 993400: // 100-149 - grp -= 593400 - i := 100 - for { - if grp < 8000 { - break - } - i++ - grp -= 8000 - } - gr = uint16(i) - break - case grp < 1400900: // 150-199 - grp -= 993400 - i := 150 - for { - if grp < 8150 { - break - } - i++ - grp -= 8150 - } - gr = uint16(i) - break - case grp < 2315900: // 200-299 - grp -= 1400900 - i := 200 - for { - if grp < 9150 { - break - } - i++ - grp -= 9150 - } - gr = uint16(i) - break - case grp < 3340900: // 300-399 - grp -= 2315900 - i := 300 - for { - if grp < 10250 { - break - } - i++ - grp -= 10250 - } - gr = uint16(i) - break - case grp < 4505900: // 400-499 - grp -= 3340900 - i := 400 - for { - if grp < 11650 { - break - } - i++ - grp -= 11650 - } - gr = uint16(i) - break - case grp < 5850900: // 500-599 - grp -= 4505900 - i := 500 - for { - if grp < 13450 { - break - } - i++ - grp -= 13450 - } - gr = uint16(i) - break - case grp < 7415900: // 600-699 - grp -= 5850900 - i := 600 - for { - if grp < 15650 { - break - } - i++ - grp -= 15650 - } - gr = uint16(i) - break - case grp < 9230900: // 700-799 - grp -= 7415900 - i := 700 - for { - if grp < 18150 { - break - } - i++ - grp -= 18150 - } - gr = uint16(i) - break - case grp < 11345900: // 800-899 - grp -= 9230900 - i := 800 - for { - if grp < 21150 { - break - } - i++ - grp -= 21150 - } - gr = uint16(i) - break - default: // 900+ - grp -= 11345900 - i := 900 - for { - if grp < 23950 { - break - } - i++ - grp -= 23950 - } - gr = uint16(i) - break } - return gr + return uint16(gr) } 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) { @@ -301,7 +183,7 @@ func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) { var scenarioData []byte bf := byteframe.NewByteFrame() err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.charID).Scan(&scenarioData) - if err != nil { + if err != nil || len(scenarioData) < 10 { s.logger.Error("Failed to load scenariodata", zap.Error(err)) bf.WriteBytes(make([]byte, 10)) } else { @@ -1161,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_distitem.go b/server/channelserver/handlers_distitem.go index a2979756f..078598719 100644 --- a/server/channelserver/handlers_distitem.go +++ b/server/channelserver/handlers_distitem.go @@ -3,144 +3,183 @@ package channelserver import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" + _config "erupe-ce/config" "erupe-ce/network/mhfpacket" + "time" "go.uber.org/zap" ) -type ItemDist struct { - ID uint32 `db:"id"` - Deadline uint32 `db:"deadline"` - TimesAcceptable uint16 `db:"times_acceptable"` - TimesAccepted uint16 `db:"times_accepted"` - MinHR uint16 `db:"min_hr"` - MaxHR uint16 `db:"max_hr"` - MinSR uint16 `db:"min_sr"` - MaxSR uint16 `db:"max_sr"` - MinGR uint16 `db:"min_gr"` - MaxGR uint16 `db:"max_gr"` - EventName string `db:"event_name"` - Description string `db:"description"` - Data []byte `db:"data"` +type Distribution struct { + ID uint32 `db:"id"` + Deadline time.Time `db:"deadline"` + TimesAcceptable uint16 `db:"times_acceptable"` + TimesAccepted uint16 `db:"times_accepted"` + MinHR int16 `db:"min_hr"` + MaxHR int16 `db:"max_hr"` + MinSR int16 `db:"min_sr"` + MaxSR int16 `db:"max_sr"` + MinGR int16 `db:"min_gr"` + MaxGR int16 `db:"max_gr"` + EventName string `db:"event_name"` + Description string `db:"description"` + Data []byte `db:"data"` } func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateDistItem) + + var itemDists []Distribution bf := byteframe.NewByteFrame() - distCount := 0 - dists, err := s.server.db.Queryx(` + rows, err := s.server.db.Queryx(` SELECT d.id, event_name, description, times_acceptable, - min_hr, max_hr, min_sr, max_sr, min_gr, max_gr, + COALESCE(min_hr, -1) AS min_hr, COALESCE(max_hr, -1) AS max_hr, + COALESCE(min_sr, -1) AS min_sr, COALESCE(max_sr, -1) AS max_sr, + COALESCE(min_gr, -1) AS min_gr, COALESCE(max_gr, -1) AS max_gr, ( - SELECT count(*) - FROM distributions_accepted da - WHERE d.id = da.distribution_id - AND da.character_id = $1 + SELECT count(*) FROM distributions_accepted da + WHERE d.id = da.distribution_id AND da.character_id = $1 ) AS times_accepted, - CASE - WHEN (EXTRACT(epoch FROM deadline)::int) IS NULL THEN 0 - ELSE (EXTRACT(epoch FROM deadline)::int) - END deadline + COALESCE(deadline, TO_TIMESTAMP(0)) AS deadline FROM distribution d - WHERE character_id = $1 AND type = $2 OR character_id IS NULL AND type = $2 ORDER BY id DESC; - `, s.charID, pkt.Unk0) - if err != nil { - s.logger.Error("Error getting distribution data from db", zap.Error(err)) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - } else { - for dists.Next() { - distCount++ - distData := &ItemDist{} - err = dists.StructScan(&distData) + WHERE character_id = $1 AND type = $2 OR character_id IS NULL AND type = $2 ORDER BY id DESC + `, s.charID, pkt.DistType) + + if err == nil { + var itemDist Distribution + for rows.Next() { + err = rows.StructScan(&itemDist) if err != nil { - s.logger.Error("Error parsing item distribution data", zap.Error(err)) + continue } - bf.WriteUint32(distData.ID) - bf.WriteUint32(distData.Deadline) - bf.WriteUint32(0) // Unk - bf.WriteUint16(distData.TimesAcceptable) - bf.WriteUint16(distData.TimesAccepted) - bf.WriteUint16(0) // Unk - bf.WriteUint16(distData.MinHR) - bf.WriteUint16(distData.MaxHR) - bf.WriteUint16(distData.MinSR) - bf.WriteUint16(distData.MaxSR) - bf.WriteUint16(distData.MinGR) - bf.WriteUint16(distData.MaxGR) - bf.WriteUint32(0) // Unk - bf.WriteUint32(0) // Unk - ps.Uint16(bf, distData.EventName, true) - bf.WriteBytes(make([]byte, 391)) + itemDists = append(itemDists, itemDist) } - resp := byteframe.NewByteFrame() - resp.WriteUint16(uint16(distCount)) - resp.WriteBytes(bf.Data()) - resp.WriteUint8(0) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } + + bf.WriteUint16(uint16(len(itemDists))) + for _, dist := range itemDists { + bf.WriteUint32(dist.ID) + bf.WriteUint32(uint32(dist.Deadline.Unix())) + bf.WriteUint32(0) // Unk + bf.WriteUint16(dist.TimesAcceptable) + bf.WriteUint16(dist.TimesAccepted) + if _config.ErupeConfig.RealClientMode >= _config.G9 { + bf.WriteUint16(0) // Unk + } + bf.WriteInt16(dist.MinHR) + bf.WriteInt16(dist.MaxHR) + bf.WriteInt16(dist.MinSR) + bf.WriteInt16(dist.MaxSR) + bf.WriteInt16(dist.MinGR) + bf.WriteInt16(dist.MaxGR) + if _config.ErupeConfig.RealClientMode >= _config.G7 { + bf.WriteUint8(0) // Unk + } + if _config.ErupeConfig.RealClientMode >= _config.G6 { + bf.WriteUint16(0) // Unk + } + if _config.ErupeConfig.RealClientMode >= _config.G8 { + bf.WriteUint8(0) // Unk + } + if _config.ErupeConfig.RealClientMode >= _config.G7 { + bf.WriteUint16(0) // Unk + bf.WriteUint16(0) // Unk + } + if _config.ErupeConfig.RealClientMode >= _config.G10 { + bf.WriteUint8(0) // Unk + } + ps.Uint8(bf, dist.EventName, true) + k := 6 + if _config.ErupeConfig.RealClientMode >= _config.G8 { + k = 13 + } + for i := 0; i < 6; i++ { + for j := 0; j < k; j++ { + bf.WriteUint8(0) + bf.WriteUint32(0) + } + } + if _config.ErupeConfig.RealClientMode >= _config.Z2 { + i := uint8(0) + bf.WriteUint8(i) + if i <= 10 { + for j := uint8(0); j < i; j++ { + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(0) + } + } + } + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +type DistributionItem struct { + ItemType uint8 `db:"item_type"` + ID uint32 `db:"id"` + ItemID uint32 `db:"item_id"` + Quantity uint32 `db:"quantity"` +} + +func getDistributionItems(s *Session, i uint32) []DistributionItem { + var distItems []DistributionItem + rows, err := s.server.db.Queryx(`SELECT id, item_type, COALESCE(item_id, 0) AS item_id, COALESCE(quantity, 0) AS quantity FROM distribution_items WHERE distribution_id=$1`, i) + if err == nil { + var distItem DistributionItem + for rows.Next() { + err = rows.StructScan(&distItem) + if err != nil { + continue + } + distItems = append(distItems, distItem) + } + } + return distItems } func handleMsgMhfApplyDistItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfApplyDistItem) - - if pkt.DistributionID == 0 { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) - } else { - row := s.server.db.QueryRowx("SELECT data FROM distribution WHERE id = $1", pkt.DistributionID) - dist := &ItemDist{} - err := row.StructScan(dist) - if err != nil { - s.logger.Error("Error parsing item distribution data", zap.Error(err)) - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) - return - } - - if len(dist.Data) >= 2 { - distData := byteframe.NewByteFrameFromBytes(dist.Data) - distItems := int(distData.ReadUint16()) - for i := 0; i < distItems; i++ { - if len(dist.Data) >= 2+(i*13) { - itemType := distData.ReadUint8() - _ = distData.ReadBytes(6) - quantity := int(distData.ReadUint16()) - _ = distData.ReadBytes(4) - switch itemType { - case 17: - _ = addPointNetcafe(s, quantity) - case 19: - s.server.db.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID) - case 20: - s.server.db.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID) - case 21: - s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", quantity, s.charID) - case 23: - saveData, err := GetCharacterSaveData(s, s.charID) - if err == nil { - saveData.RP += uint16(quantity) - saveData.Save(s) - } - } - } - } - } - - bf := byteframe.NewByteFrame() - bf.WriteUint32(pkt.DistributionID) - bf.WriteBytes(dist.Data) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - - _, err = s.server.db.Exec(` - INSERT INTO public.distributions_accepted - VALUES ($1, $2) - `, pkt.DistributionID, s.charID) - if err != nil { - s.logger.Error("Error updating accepted dist count", zap.Error(err)) + bf := byteframe.NewByteFrame() + bf.WriteUint32(pkt.DistributionID) + distItems := getDistributionItems(s, pkt.DistributionID) + bf.WriteUint16(uint16(len(distItems))) + for _, item := range distItems { + bf.WriteUint8(item.ItemType) + bf.WriteUint32(item.ItemID) + bf.WriteUint32(item.Quantity) + if _config.ErupeConfig.RealClientMode >= _config.G8 { + bf.WriteUint32(item.ID) } } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireDistItem) + if pkt.DistributionID > 0 { + _, err := s.server.db.Exec(`INSERT INTO public.distributions_accepted VALUES ($1, $2)`, pkt.DistributionID, s.charID) + if err == nil { + distItems := getDistributionItems(s, pkt.DistributionID) + for _, item := range distItems { + switch item.ItemType { + case 17: + _ = addPointNetcafe(s, int(item.Quantity)) + case 19: + s.server.db.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID) + case 20: + s.server.db.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID) + case 21: + s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID) + case 23: + saveData, err := GetCharacterSaveData(s, s.charID) + if err == nil { + saveData.RP += uint16(item.Quantity) + saveData.Save(s) + } + } + } + } + } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_diva.go b/server/channelserver/handlers_diva.go index 1867bfacd..7f5b33992 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -70,21 +70,21 @@ 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.RealClientMode <= _config.Z1 { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32)) - } else { + 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 { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32)) } 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) } - if s.server.erupeConfig.RealClientMode <= _config.Z1 { + if s.server.erupeConfig.RealClientMode >= _config.Z2 { bf.WriteUint32(id) } for i := range timestamps { diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 74d0cef13..d39b629d9 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -10,44 +10,6 @@ import ( "erupe-ce/network/mhfpacket" ) -func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfRegisterEvent) - bf := byteframe.NewByteFrame() - bf.WriteUint8(pkt.Unk2) - bf.WriteUint8(pkt.Unk4) - bf.WriteUint16(0x1142) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) -} - -func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfReleaseEvent) - - // Do this ack manually because it uses a non-(0|1) error code - /* - _ACK_SUCCESS = 0 - _ACK_ERROR = 1 - - _ACK_EINPROGRESS = 16 - _ACK_ENOENT = 17 - _ACK_ENOSPC = 18 - _ACK_ETIMEOUT = 19 - - _ACK_EINVALID = 64 - _ACK_EFAILED = 65 - _ACK_ENOMEM = 66 - _ACK_ENOTEXIT = 67 - _ACK_ENOTREADY = 68 - _ACK_EALREADY = 69 - _ACK_DISABLE_WORK = 71 - */ - s.QueueSendMHF(&mhfpacket.MsgSysAck{ - AckHandle: pkt.AckHandle, - IsBufferResponse: false, - ErrorCode: 0x41, - AckData: []byte{0x00, 0x00, 0x00, 0x00}, - }) -} - type Event struct { EventType uint16 Unk1 uint16 @@ -237,15 +199,11 @@ func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() bf.WriteUint8(0) switch pkt.BoostWeekUsed { - case 1: - fallthrough - case 3: + case 1, 3: expiration = TimeAdjusted().Add(120 * time.Minute) case 4: expiration = TimeAdjusted().Add(180 * time.Minute) - case 2: - fallthrough - case 5: + case 2, 5: expiration = TimeAdjusted().Add(240 * time.Minute) } bf.WriteUint32(uint32(expiration.Unix())) diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 1521b9dfa..6fa497f11 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -92,8 +92,8 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { } var timestamps []uint32 - if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.TournamentEvent >= 0 { - if s.server.erupeConfig.DevModeOptions.TournamentEvent == 0 { + if s.server.erupeConfig.DebugOptions.TournamentOverride >= 0 { + if s.server.erupeConfig.DebugOptions.TournamentOverride == 0 { bf.WriteBytes(make([]byte, 16)) bf.WriteUint32(uint32(TimeAdjusted().Unix())) bf.WriteUint8(0) @@ -103,7 +103,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) return } - timestamps = generateTournamentTimestamps(uint32(s.server.erupeConfig.DevModeOptions.TournamentEvent), true) + timestamps = generateTournamentTimestamps(uint32(s.server.erupeConfig.DebugOptions.TournamentOverride), true) } else { timestamps = generateTournamentTimestamps(start, false) } @@ -124,33 +124,33 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(uint32(TimeAdjusted().Unix())) bf.WriteUint8(1) // TODO: Make this dynamic depending on timestamp - ps.Uint8(bf, "", true) + ps.Uint8(bf, "第150回公式狩猟大会", true) // Temp direct port tournamentEvents := []TournamentEvent{ - {2644, 16, 0, 62151, ""}, - {2645, 16, 1, 62151, ""}, - {2646, 16, 2, 62151, ""}, - {2647, 16, 3, 62151, ""}, - {2648, 16, 4, 62151, ""}, - {2649, 16, 5, 62151, ""}, - {2650, 16, 6, 62151, ""}, - {2651, 16, 7, 62151, ""}, - {2652, 16, 8, 62151, ""}, - {2653, 16, 9, 62151, ""}, - {2654, 16, 10, 62151, ""}, - {2655, 16, 11, 62151, ""}, - {2656, 16, 12, 62151, ""}, - {2657, 16, 13, 62151, ""}, - {2658, 17, -1, 62150, ""}, - {2659, 6, 234, 0, ""}, - {2660, 6, 237, 0, ""}, - {2661, 6, 239, 0, ""}, + {2644, 16, 0, 60691, "爆霧竜討伐!"}, + {2645, 16, 1, 60691, "爆霧竜討伐!"}, + {2646, 16, 2, 60691, "爆霧竜討伐!"}, + {2647, 16, 3, 60691, "爆霧竜討伐!"}, + {2648, 16, 4, 60691, "爆霧竜討伐!"}, + {2649, 16, 5, 60691, "爆霧竜討伐!"}, + {2650, 16, 6, 60691, "爆霧竜討伐!"}, + {2651, 16, 7, 60691, "爆霧竜討伐!"}, + {2652, 16, 8, 60691, "爆霧竜討伐!"}, + {2653, 16, 9, 60691, "爆霧竜討伐!"}, + {2654, 16, 10, 60691, "爆霧竜討伐!"}, + {2655, 16, 11, 60691, "爆霧竜討伐!"}, + {2656, 16, 12, 60691, "爆霧竜討伐!"}, + {2657, 16, 13, 60691, "爆霧竜討伐!"}, + {2658, 17, -1, 60690, "みんなで爆霧竜討伐!"}, + {2659, 6, 234, 0, "キレアジ"}, + {2660, 6, 237, 0, "ハリマグロ"}, + {2661, 6, 239, 0, "カクサンデメキン"}, } tournamentCups := []TournamentCup{ - {569, 6, 6, 0, "", ""}, - {570, 17, 7, 0, "", ""}, - {571, 16, 7, 0, "", ""}, + {569, 6, 6, 0, "個人 巨大魚杯", "~C05【競技内容】\n~C00クエストで釣った魚のサイズを競う\n~C04【対象魚】\n~C00キレアジ、\nハリマグロ、カクサンデメキン\n~C07【入賞賞品】\n~C00魚杯のしるし、タルネコ生産券、\nグーク生産券、グーク足生産券、\nグーク解放券(1〜3位)\n/猟団ポイント(1〜100位)\n/匠チケット+ハーフチケット白\n(1〜500位)\n~C03【開催期間】\n~C002019年11月22日 14:00から\n2019年11月25日 14:00まで"}, + {570, 17, 7, 0, "猟団 G級韋駄天杯", "~C05【競技内容】\n~C00≪みんなで爆霧竜討伐!≫を\n同じ猟団に所属する4人までの\n猟団員でいかに早くクリアするか\nを競う\n\n~C07【入賞賞品】\n~C00第147回狩人祭の魂(1〜200位)\n\n~C03【開催期間】\n~C002019年11月22日 14:00から\n2019年11月25日 14:00まで\n\n"}, + {571, 16, 7, 0, "個人 G級韋駄天杯", "~C05【競技内容】\n~C00≪爆霧竜討伐!≫を\nいかに早くクリアするかを競う\n\n~C07【入賞賞品】\n~C00王者のメダル(1位)\n/公式のしるし、タルネコ生産券、\nグーク生産券、グーク足生産券、\nグーク解放券(1〜3位)\n/猟団ポイント(1〜100位)\n/匠チケット+ハーフチケット白\n(1〜500位)\n~C03【開催期間】\n~C002019年11月22日 14:00から\n2019年11月25日 14:00まで"}, } bf.WriteUint16(uint16(len(tournamentEvents))) @@ -199,8 +199,9 @@ func handleMsgMhfEnumerateOrder(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") + s.server.db.Exec("UPDATE guild_characters SET trial_vote=NULL") } func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { @@ -245,13 +246,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 uint16 + 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 } @@ -277,12 +278,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) } @@ -293,8 +294,8 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { } var blueSouls, redSouls uint32 - s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'blue'").Scan(&blueSouls) - s.server.db.QueryRow("SELECT SUM(gc.souls) FROM guild_characters gc INNER JOIN festa_registrations fr ON fr.guild_id = gc.guild_id WHERE fr.team = 'red'").Scan(&redSouls) + s.server.db.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'blue'`).Scan(&blueSouls) + s.server.db.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'red'`).Scan(&redSouls) bf.WriteUint32(id) for _, timestamp := range timestamps { @@ -309,7 +310,19 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { var trials []FestaTrial var trial FestaTrial - rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials") + rows, _ = s.server.db.Queryx(`SELECT ft.*, + COALESCE(CASE + WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id) > + COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id) + THEN CAST('blue' AS public.festival_color) + WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id) > + COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id) + THEN CAST('red' AS public.festival_color) + END, CAST('none' AS public.festival_color)) AS monopoly + FROM public.festa_trials ft + LEFT JOIN public.guild_characters gc ON ft.id = gc.trial_vote + LEFT JOIN public.festa_registrations fr ON gc.guild_id = fr.guild_id + GROUP BY ft.id`) for rows.Next() { err := rows.StructScan(&trial) if err != nil { @@ -325,12 +338,14 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(trial.TimesReq) bf.WriteUint16(trial.Locale) bf.WriteUint16(trial.Reward) - trial.Monopoly = 0xFFFF // NYI - bf.WriteUint16(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 + // Item 7011 may not exist in older versions, remove to prevent crashes rewards := []FestaReward{ {1, 0, 7, 350, 1520, 0, 0, 0}, {1, 0, 7, 1000, 7011, 0, 0, 1}, @@ -358,6 +373,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { {5, 0, 13, 0, 0, 0, 0, 0}, //{5, 0, 1, 0, 0, 0, 0, 0}, } + bf.WriteUint16(uint16(len(rewards))) for _, reward := range rewards { bf.WriteUint8(reward.Unk0) @@ -365,11 +381,13 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(reward.ItemType) bf.WriteUint16(reward.Quantity) bf.WriteUint16(reward.ItemID) - bf.WriteUint16(reward.Unk5) - bf.WriteUint16(reward.Unk6) - bf.WriteUint8(reward.Unk7) + // Not confirmed to be G1 but exists in G3 + if _config.ErupeConfig.RealClientMode >= _config.G1 { + bf.WriteUint16(reward.Unk5) + bf.WriteUint16(reward.Unk6) + bf.WriteUint8(reward.Unk7) + } } - if _config.ErupeConfig.RealClientMode <= _config.G61 { if s.server.erupeConfig.GameplayOptions.MaximumFP > 0xFFFF { s.server.erupeConfig.GameplayOptions.MaximumFP = 0xFFFF @@ -380,37 +398,62 @@ 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 + 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) } - 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 + bf.WriteUint32(0) // Clan goal + // Final bonus rates + bf.WriteUint32(5000) // 5000+ souls + bf.WriteUint32(2000) // 2000-4999 souls + bf.WriteUint32(1000) // 1000-1999 souls + bf.WriteUint32(100) // 100-999 souls + bf.WriteUint16(300) // 300% bonus + bf.WriteUint16(200) // 200% bonus + bf.WriteUint16(150) // 150% bonus + bf.WriteUint16(100) // Normal rate + bf.WriteUint16(50) // 50% penalty + + if _config.ErupeConfig.RealClientMode >= _config.G52 { + ps.Uint16(bf, "", false) } - - // Unknown values - bf.WriteUint32(1) - bf.WriteUint32(5000) - bf.WriteUint32(2000) - bf.WriteUint32(1000) - bf.WriteUint32(100) - bf.WriteUint16(300) - bf.WriteUint16(200) - bf.WriteUint16(150) - bf.WriteUint16(100) - bf.WriteUint16(50) - - ps.Uint16(bf, "", false) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -427,7 +470,7 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { return } var souls, exists uint32 - s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) + s.server.db.QueryRow(`SELECT COALESCE((SELECT SUM(souls) FROM festa_submissions WHERE character_id=$1), 0)`, s.charID).Scan(&souls) err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists) bf := byteframe.NewByteFrame() bf.WriteUint32(souls) @@ -438,7 +481,6 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(false) bf.WriteBool(true) } - bf.WriteUint16(0) // Unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -453,18 +495,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()) } @@ -480,21 +522,33 @@ 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()) } func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfVoteFesta) + s.server.db.Exec(`UPDATE guild_characters SET trial_vote=$1 WHERE character_id=$2`, pkt.TrialID, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -519,7 +573,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 5fdd2dac1..9df9dd2c6 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -4,7 +4,6 @@ import ( "database/sql" "database/sql/driver" "encoding/binary" - "encoding/hex" "encoding/json" "errors" _config "erupe-ce/config" @@ -22,18 +21,18 @@ import ( "go.uber.org/zap" ) -type FestivalColour string +type FestivalColor string const ( - FestivalColourNone FestivalColour = "none" - FestivalColourRed FestivalColour = "red" - FestivalColourBlue FestivalColour = "blue" + FestivalColorNone FestivalColor = "none" + FestivalColorBlue FestivalColor = "blue" + FestivalColorRed FestivalColor = "red" ) -var FestivalColourCodes = map[FestivalColour]uint8{ - FestivalColourBlue: 0x00, - FestivalColourRed: 0x01, - FestivalColourNone: 0xFF, +var FestivalColorCodes = map[FestivalColor]int16{ + FestivalColorNone: -1, + FestivalColorBlue: 0, + FestivalColorRed: 1, } type GuildApplicationType string @@ -44,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_color"` + Souls uint32 `db:"souls"` + AllianceID uint32 `db:"alliance_id"` + Icon *GuildIcon `db:"icon"` GuildLeader } @@ -158,7 +157,7 @@ SELECT sub_motto, created_at, leader_id, - lc.name as leader_name, + c.name AS leader_name, comment, COALESCE(pugi_name_1, '') AS pugi_name_1, COALESCE(pugi_name_2, '') AS pugi_name_2, @@ -168,8 +167,8 @@ SELECT pugi_outfit_3, pugi_outfits, recruiting, - COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour, - (SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls, + COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_color, + COALESCE((SELECT SUM(fs.souls) FROM festa_submissions fs WHERE fs.guild_id=g.id), 0) AS souls, COALESCE(( SELECT id FROM guild_alliances ga WHERE ga.parent_id = g.id OR @@ -179,8 +178,8 @@ SELECT icon, (SELECT count(1) FROM guild_characters gc WHERE gc.guild_id = g.id) AS member_count FROM guilds g - JOIN guild_characters lgc ON lgc.character_id = leader_id - JOIN characters lc on leader_id = lc.id + JOIN guild_characters gc ON gc.character_id = leader_id + JOIN characters c on leader_id = c.id ` func (guild *Guild) Save(s *Session) error { @@ -968,7 +967,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(uint8(len(guildLeaderName))) bf.WriteBytes(guildName) bf.WriteBytes(guildComment) - bf.WriteUint8(FestivalColourCodes[guild.FestivalColour]) + bf.WriteInt8(int8(FestivalColorCodes[guild.FestivalColor])) bf.WriteUint32(guild.RankRP) bf.WriteBytes(guildLeaderName) bf.WriteUint32(0) // Unk @@ -990,20 +989,21 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } bf.WriteUint32(guild.PugiOutfits) - if guild.Rank() >= 3 { - bf.WriteUint8(40) - } else if guild.Rank() >= 7 { - bf.WriteUint8(50) - } else if guild.Rank() >= 10 { - bf.WriteUint8(60) - } else { - bf.WriteUint8(30) + limit := s.server.erupeConfig.GameplayOptions.ClanMemberLimits[0][1] + for _, j := range s.server.erupeConfig.GameplayOptions.ClanMemberLimits { + if guild.Rank() >= uint16(j[0]) { + limit = j[1] + } } + if limit > 100 { + limit = 100 + } + bf.WriteUint8(limit) bf.WriteUint32(55000) bf.WriteUint32(0) bf.WriteUint16(0) // Changing Room RP - bf.WriteUint16(0) + bf.WriteUint16(0) // Ignored if guild.AllianceID > 0 { alliance, err := GetAllianceData(s, guild.AllianceID) @@ -1013,7 +1013,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(alliance.ID) bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) bf.WriteUint16(alliance.TotalMembers) - bf.WriteUint8(0) + bf.WriteUint8(0) // Ignored bf.WriteUint8(0) ps.Uint16(bf, alliance.Name, true) if alliance.SubGuild1ID > 0 { @@ -1148,7 +1148,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { var alliances []*GuildAlliance var rows *sqlx.Rows var err error - bf := byteframe.NewByteFrameFromBytes(pkt.Data1) if pkt.Type <= 8 { var tempGuilds []*Guild @@ -1165,20 +1164,20 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { switch pkt.Type { case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME: for _, guild := range tempGuilds { - if strings.Contains(guild.Name, pkt.Data2) { + if strings.Contains(guild.Name, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { guilds = append(guilds, guild) } } case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME: for _, guild := range tempGuilds { - if strings.Contains(guild.LeaderName, pkt.Data2) { + if strings.Contains(guild.LeaderName, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { guilds = append(guilds, guild) } } case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID: - ID := bf.ReadUint32() + CID := pkt.Data1.ReadUint32() for _, guild := range tempGuilds { - if guild.LeaderCharID == ID { + if guild.LeaderCharID == CID { guilds = append(guilds, guild) } } @@ -1216,15 +1215,15 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } guilds = tempGuilds case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO: - mainMotto := uint8(bf.ReadUint16()) - subMotto := uint8(bf.ReadUint16()) + mainMotto := uint8(pkt.Data1.ReadUint16()) + subMotto := uint8(pkt.Data1.ReadUint16()) for _, guild := range tempGuilds { if guild.MainMotto == mainMotto && guild.SubMotto == subMotto { guilds = append(guilds, guild) } } case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING: - recruitingMotto := uint8(bf.ReadUint16()) + recruitingMotto := uint8(pkt.Data1.ReadUint16()) for _, guild := range tempGuilds { if guild.MainMotto == recruitingMotto { guilds = append(guilds, guild) @@ -1245,20 +1244,20 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { switch pkt.Type { case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME: for _, alliance := range tempAlliances { - if strings.Contains(alliance.Name, pkt.Data2) { + if strings.Contains(alliance.Name, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { alliances = append(alliances, alliance) } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME: for _, alliance := range tempAlliances { - if strings.Contains(alliance.ParentGuild.LeaderName, pkt.Data2) { + if strings.Contains(alliance.ParentGuild.LeaderName, stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())) { alliances = append(alliances, alliance) } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID: - ID := bf.ReadUint32() + CID := pkt.Data1.ReadUint32() for _, alliance := range tempAlliances { - if alliance.ParentGuild.LeaderCharID == ID { + if alliance.ParentGuild.LeaderCharID == CID { alliances = append(alliances, alliance) } } @@ -1292,7 +1291,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { return } - bf = byteframe.NewByteFrame() + bf := byteframe.NewByteFrame() if pkt.Type > 8 { hasNextPage := false @@ -1428,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 @@ -1437,7 +1436,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { for _, member := range guildMembers { bf.WriteUint32(member.CharID) bf.WriteUint16(member.HRP) - if s.server.erupeConfig.RealClientMode > _config.G7 { + if s.server.erupeConfig.RealClientMode >= _config.G10 { bf.WriteUint16(member.GR) } if s.server.erupeConfig.RealClientMode < _config.ZZ { @@ -1461,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 { @@ -1559,7 +1558,7 @@ func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem) var boxContents []byte bf := byteframe.NewByteFrame() - err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", int(pkt.GuildId)).Scan(&boxContents) + err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents) if err != nil { s.logger.Error("Failed to get guild item box contents from db", zap.Error(err)) bf.WriteBytes(make([]byte, 4)) @@ -1593,7 +1592,7 @@ func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) { // Get item cache from DB var boxContents []byte var oldItems []Item - err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", int(pkt.GuildId)).Scan(&boxContents) + err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents) if err != nil { s.logger.Error("Failed to get guild item box contents from db", zap.Error(err)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) @@ -1610,16 +1609,16 @@ func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) { // Update item stacks newItems := make([]Item, len(oldItems)) copy(newItems, oldItems) - for i := 0; i < int(pkt.Amount); i++ { + for i := 0; i < len(pkt.Items); i++ { for j := 0; j <= len(oldItems); j++ { if j == len(oldItems) { var newItem Item - newItem.ItemId = pkt.Items[i].ItemId + newItem.ItemId = pkt.Items[i].ItemID newItem.Amount = pkt.Items[i].Amount newItems = append(newItems, newItem) break } - if pkt.Items[i].ItemId == oldItems[j].ItemId { + if pkt.Items[i].ItemID == oldItems[j].ItemId { newItems[j].Amount = pkt.Items[i].Amount break } @@ -1643,7 +1642,7 @@ func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) { } // Upload new item cache - _, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), int(pkt.GuildId)) + _, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), pkt.GuildID) if err != nil { s.logger.Error("Failed to update guild item box contents in db", zap.Error(err)) } @@ -1678,7 +1677,7 @@ func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) { icon := &GuildIcon{} - icon.Parts = make([]GuildIconPart, pkt.PartCount) + icon.Parts = make([]GuildIconPart, len(pkt.IconParts)) for i, p := range pkt.IconParts { icon.Parts[i] = GuildIconPart{ @@ -1723,16 +1722,51 @@ func handleMsgMhfReadGuildcard(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } +type GuildMission struct { + ID uint32 + Unk uint32 + Type uint16 + Goal uint16 + Quantity uint16 + SkipTickets uint16 + GR bool + RewardType uint16 + RewardLevel uint16 +} + func handleMsgMhfGetGuildMissionList(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildMissionList) - - decoded, err := hex.DecodeString("000694610000023E000112990023000100000200015DDD232100069462000002F30000005F000C000200000300025DDD232100069463000002EA0000005F0006000100000100015DDD23210006946400000245000000530010000200000400025DDD232100069465000002B60001129B0019000100000200015DDD232100069466000003DC0000001B0010000100000600015DDD232100069467000002DA000112A00019000100000400015DDD232100069468000002A800010DEF0032000200000200025DDD2321000694690000045500000022003C000200000600025DDD23210006946A00000080000122D90046000200000300025DDD23210006946B000001960000003B000A000100000100015DDD23210006946C0000049200000046005A000300000600035DDD23210006946D000000A4000000260018000200000600025DDD23210006946E0000017A00010DE40096000300000100035DDD23210006946F000001BE0000005E0014000200000400025DDD2355000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") - - if err != nil { - panic(err) + bf := byteframe.NewByteFrame() + missions := []GuildMission{ + {431201, 574, 1, 4761, 35, 1, false, 2, 1}, + {431202, 755, 0, 95, 12, 2, false, 3, 2}, + {431203, 746, 0, 95, 6, 1, false, 1, 1}, + {431204, 581, 0, 83, 16, 2, false, 4, 2}, + {431205, 694, 1, 4763, 25, 1, false, 2, 1}, + {431206, 988, 0, 27, 16, 1, false, 6, 1}, + {431207, 730, 1, 4768, 25, 1, false, 4, 1}, + {431208, 680, 1, 3567, 50, 2, false, 2, 2}, + {431209, 1109, 0, 34, 60, 2, false, 6, 2}, + {431210, 128, 1, 8921, 70, 2, false, 3, 2}, + {431211, 406, 0, 59, 10, 1, false, 1, 1}, + {431212, 1170, 0, 70, 90, 3, false, 6, 3}, + {431213, 164, 0, 38, 24, 2, false, 6, 2}, + {431214, 378, 1, 3556, 150, 3, false, 1, 3}, + {431215, 446, 0, 94, 20, 2, false, 4, 2}, } - - doAckBufSucceed(s, pkt.AckHandle, decoded) + for _, mission := range missions { + bf.WriteUint32(mission.ID) + bf.WriteUint32(mission.Unk) + bf.WriteUint16(mission.Type) + bf.WriteUint16(mission.Goal) + bf.WriteUint16(mission.Quantity) + bf.WriteUint16(mission.SkipTickets) + bf.WriteBool(mission.GR) + bf.WriteUint16(mission.RewardType) + bf.WriteUint16(mission.RewardLevel) + bf.WriteUint32(uint32(TimeAdjusted().Unix())) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetGuildMissionRecord(s *Session, p mhfpacket.MHFPacket) { @@ -1798,18 +1832,18 @@ func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking) guild, _ := GetGuildInfoByCharacterId(s, s.charID) - currentTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.GuildMealDuration-60) * time.Minute) + startTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.ClanMealDuration-3600) * time.Second) if pkt.OverwriteID != 0 { - s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, currentTime, pkt.OverwriteID) + s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, startTime, pkt.OverwriteID) } else { - s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, currentTime).Scan(&pkt.OverwriteID) + s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, startTime).Scan(&pkt.OverwriteID) } bf := byteframe.NewByteFrame() bf.WriteUint16(1) bf.WriteUint32(pkt.OverwriteID) bf.WriteUint32(uint32(pkt.MealID)) bf.WriteUint32(uint32(pkt.Success)) - bf.WriteUint32(uint32(currentTime.Unix())) + bf.WriteUint32(uint32(startTime.Unix())) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1817,14 +1851,14 @@ func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildWeeklyBonusMaster) // Values taken from brand new guild capture - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x28)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 40)) } func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildWeeklyBonusActiveCount) bf := byteframe.NewByteFrame() - bf.WriteUint8(0x3C) // Active count - bf.WriteUint8(0x3C) // Current active count - bf.WriteUint8(0x00) // New active count + bf.WriteUint8(60) // Active count + bf.WriteUint8(60) // Current active count + bf.WriteUint8(0) // New active count doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1832,18 +1866,54 @@ func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGuildHuntdata) bf := byteframe.NewByteFrame() switch pkt.Operation { - case 0: // Unk - doAckBufSucceed(s, pkt.AckHandle, []byte{}) - case 1: // Get Huntdata + case 0: // Acquire + s.server.db.Exec(`UPDATE guild_characters SET box_claimed=$1 WHERE character_id=$2`, TimeAdjusted(), s.charID) + case 1: // Enumerate bf.WriteUint8(0) // Entries - /* Entry format - uint32 UnkID - uint32 MonID - */ - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - case 2: // Unk, controls glow - doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00}) + rows, err := s.server.db.Query(`SELECT kl.id, kl.monster FROM kill_logs kl + INNER JOIN guild_characters gc ON kl.character_id = gc.character_id + WHERE gc.guild_id=$1 + AND kl.timestamp >= (SELECT box_claimed FROM guild_characters WHERE character_id=$2) + `, pkt.GuildID, s.charID) + if err == nil { + var count uint8 + var huntID, monID uint32 + for rows.Next() { + err = rows.Scan(&huntID, &monID) + if err != nil { + continue + } + count++ + if count > 255 { + count = 255 + rows.Close() + break + } + bf.WriteUint32(huntID) + bf.WriteUint32(monID) + } + bf.Seek(0, 0) + bf.WriteUint8(count) + } + case 2: // Check + guild, err := GetGuildInfoByCharacterId(s, s.charID) + if err == nil { + var count uint8 + err = s.server.db.QueryRow(`SELECT COUNT(*) FROM kill_logs kl + INNER JOIN guild_characters gc ON kl.character_id = gc.character_id + WHERE gc.guild_id=$1 + AND kl.timestamp >= (SELECT box_claimed FROM guild_characters WHERE character_id=$2) + `, guild.ID, s.charID).Scan(&count) + if err == nil && count > 0 { + bf.WriteBool(true) + } else { + bf.WriteBool(false) + } + } else { + bf.WriteBool(false) + } } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } type MessageBoardPost struct { diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go index f27d67026..39dbe13f6 100644 --- a/server/channelserver/handlers_guild_alliance.go +++ b/server/channelserver/handlers_guild_alliance.go @@ -162,8 +162,7 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) { } case mhfpacket.OPERATE_JOINT_KICK: if alliance.ParentGuild.LeaderCharID == s.charID { - _ = pkt.UnkData.ReadUint8() - kickedGuildID := pkt.UnkData.ReadUint32() + kickedGuildID := pkt.Data1.ReadUint32() if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 { s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID) } else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 { 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_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index 3d0918a84..f3f4815e6 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -4,72 +4,79 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/common/stringsupport" "erupe-ce/network/mhfpacket" + "time" ) type TreasureHunt struct { - HuntID uint32 `db:"id"` - HostID uint32 `db:"host_id"` - Destination uint32 `db:"destination"` - Level uint32 `db:"level"` - Return uint32 `db:"return"` - Acquired bool `db:"acquired"` - Claimed bool `db:"claimed"` - Hunters string `db:"hunters"` - Treasure string `db:"treasure"` - HuntData []byte `db:"hunt_data"` + HuntID uint32 `db:"id"` + HostID uint32 `db:"host_id"` + Destination uint32 `db:"destination"` + Level uint32 `db:"level"` + Start time.Time `db:"start"` + Acquired bool `db:"acquired"` + Collected bool `db:"collected"` + HuntData []byte `db:"hunt_data"` + Hunters uint32 `db:"hunters"` + Claimed bool `db:"claimed"` } func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure) guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { - panic(err) + if err != nil || guild == nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + var hunts []TreasureHunt + var hunt TreasureHunt + + switch pkt.MaxHunts { + case 1: + err = s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt) + if err == nil { + hunts = append(hunts, hunt) + } + case 30: + rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data, + (SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters, + CASE + WHEN ghc.character_id IS NOT NULL THEN true + ELSE false + END AS claimed + FROM guild_hunts gh + LEFT JOIN guild_hunts_claimed ghc ON gh.id = ghc.hunt_id AND ghc.character_id = $1 + WHERE gh.guild_id=$2 AND gh.level=2 AND gh.acquired=TRUE + `, s.charID, guild.ID) + if err != nil { + rows.Close() + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } else { + for rows.Next() { + err = rows.StructScan(&hunt) + if err == nil && hunt.Start.Add(time.Second*time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry)).After(TimeAdjusted()) { + hunts = append(hunts, hunt) + } + } + } + if len(hunts) > 30 { + hunts = hunts[:30] + } } bf := byteframe.NewByteFrame() - hunts := 0 - rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, TimeAdjusted().Unix()) - for rows.Next() { - hunt := &TreasureHunt{} - err = rows.StructScan(&hunt) - // Remove self from other hunter count - hunt.Hunters = stringsupport.CSVRemove(hunt.Hunters, int(s.charID)) - if err != nil { - panic(err) - } - if pkt.MaxHunts == 1 { - if hunt.HostID != s.charID || hunt.Acquired { - continue - } - hunts++ - bf.WriteUint32(hunt.HuntID) - bf.WriteUint32(hunt.Destination) - bf.WriteUint32(hunt.Level) - bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters))) - bf.WriteUint32(hunt.Return) - bf.WriteBool(false) - bf.WriteBool(false) - bf.WriteBytes(hunt.HuntData) - break - } else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 { - if hunts == 30 { - break - } - hunts++ - bf.WriteUint32(hunt.HuntID) - bf.WriteUint32(hunt.Destination) - bf.WriteUint32(hunt.Level) - bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters))) - bf.WriteUint32(hunt.Return) - bf.WriteBool(hunt.Claimed) - bf.WriteBool(stringsupport.CSVContains(hunt.Treasure, int(s.charID))) - bf.WriteBytes(hunt.HuntData) - } + bf.WriteUint16(uint16(len(hunts))) + bf.WriteUint16(uint16(len(hunts))) + for _, h := range hunts { + bf.WriteUint32(h.HuntID) + bf.WriteUint32(h.Destination) + bf.WriteUint32(h.Level) + bf.WriteUint32(h.Hunters) + bf.WriteUint32(uint32(h.Start.Unix())) + bf.WriteBool(h.Collected) + bf.WriteBool(h.Claimed) + bf.WriteBytes(h.HuntData) } - resp := byteframe.NewByteFrame() - resp.WriteUint16(uint16(hunts)) - resp.WriteUint16(uint16(hunts)) - resp.WriteBytes(bf.Data()) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { @@ -77,8 +84,9 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrameFromBytes(pkt.Data) huntData := byteframe.NewByteFrame() guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil { - panic(err) + if err != nil || guild == nil { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return } guildCats := getGuildAirouList(s) destination := bf.ReadUint32() @@ -92,87 +100,55 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { if catID > 0 { catsUsed = stringsupport.CSVAdd(catsUsed, int(catID)) for _, cat := range guildCats { - if cat.CatID == catID { - huntData.WriteBytes(cat.CatName) + if cat.ID == catID { + huntData.WriteBytes(cat.Name) break } } huntData.WriteBytes(bf.ReadBytes(9)) } } - _, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6, $7)", - guild.ID, s.charID, destination, level, TimeAdjusted().Unix(), huntData.Data(), catsUsed) - if err != nil { - panic(err) - } + s.server.db.Exec(`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6) + `, guild.ID, s.charID, destination, level, huntData.Data(), catsUsed) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure) - _, err := s.server.db.Exec("UPDATE guild_hunts SET acquired=true WHERE id=$1", pkt.HuntID) - if err != nil { - panic(err) - } + s.server.db.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, pkt.HuntID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -func treasureHuntUnregister(s *Session) { - guild, err := GetGuildInfoByCharacterId(s, s.charID) - if err != nil || guild == nil { - return - } - var huntID int - var hunters string - rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) - if err != nil { - return - } - for rows.Next() { - rows.Scan(&huntID, &hunters) - hunters = stringsupport.CSVRemove(hunters, int(s.charID)) - s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", hunters, huntID) - } -} - func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) - var csv string - if pkt.State == 0 { // Report registration - // Unregister from all other hunts - treasureHuntUnregister(s) - if pkt.HuntID != 0 { - // Register to selected hunt - err := s.server.db.QueryRow("SELECT hunters FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) - if err != nil { - panic(err) - } - csv = stringsupport.CSVAdd(csv, int(s.charID)) - _, err = s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", csv, pkt.HuntID) - if err != nil { - panic(err) - } - } - } else if pkt.State == 1 { // Collected by hunter - s.server.db.Exec("UPDATE guild_hunts SET hunters='', claimed=true WHERE id=$1", pkt.HuntID) - } else if pkt.State == 2 { // Claim treasure - err := s.server.db.QueryRow("SELECT treasure FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) - if err != nil { - panic(err) - } - csv = stringsupport.CSVAdd(csv, int(s.charID)) - _, err = s.server.db.Exec("UPDATE guild_hunts SET treasure=$1 WHERE id=$2", csv, pkt.HuntID) - if err != nil { - panic(err) - } + switch pkt.State { + case 0: // Report registration + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.charID) + case 1: // Collected by hunter + s.server.db.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, pkt.HuntID) + s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID) + case 2: // Claim treasure + s.server.db.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, pkt.HuntID, s.charID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } +type TreasureSouvenir struct { + Destination uint32 + Quantity uint32 +} + func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir) - - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) + bf := byteframe.NewByteFrame() + bf.WriteUint32(0) + souvenirs := []TreasureSouvenir{} + bf.WriteUint16(uint16(len(souvenirs))) + for _, souvenir := range souvenirs { + bf.WriteUint32(souvenir.Destination) + bf.WriteUint32(souvenir.Quantity) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go index 4cc53d303..3f6188750 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -119,7 +119,9 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(0) } bf.WriteUint16(house.HRP) - bf.WriteUint16(house.GR) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + bf.WriteUint16(house.GR) + } ps.Uint8(bf, house.Name, true) } bf.Seek(0, 0) @@ -213,10 +215,12 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) { bf.WriteBytes(houseFurniture) case 10: // Garden bf.WriteBytes(garden) - c, d := getGookData(s, pkt.CharID) - bf.WriteUint16(c) + goocoos := getGoocooData(s, pkt.CharID) + bf.WriteUint16(uint16(len(goocoos))) bf.WriteUint16(0) - bf.WriteBytes(d) + for _, goocoo := range goocoos { + bf.WriteBytes(goocoo) + } } if len(bf.Data()) == 0 { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) @@ -238,8 +242,8 @@ func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfUpdateMyhouseInfo) - s.server.db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Unk0, s.charID) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + s.server.db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Data, s.charID) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) { @@ -250,71 +254,65 @@ func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) { s.logger.Error("Failed to load decomyset", zap.Error(err)) } if len(data) == 0 { + data = []byte{0x01, 0x00} if s.server.erupeConfig.RealClientMode < _config.G10 { data = []byte{0x00, 0x00} } - data = []byte{0x01, 0x00} } doAckBufSucceed(s, pkt.AckHandle, data) } func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveDecoMyset) - // https://gist.github.com/Andoryuuta/9c524da7285e4b5ca7e52e0fc1ca1daf - var loadData []byte - bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload[1:]) // skip first unk byte - err := s.server.db.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.charID).Scan(&loadData) + var temp []byte + err := s.server.db.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.charID).Scan(&temp) if err != nil { s.logger.Error("Failed to load decomyset", zap.Error(err)) - } else { - numSets := bf.ReadUint8() // sets being written - // empty save - if len(loadData) == 0 { - loadData = []byte{0x01, 0x00} - } - - savedSets := loadData[1] // existing saved sets - // no sets, new slice with just first 2 bytes for appends later - if savedSets == 0 { - loadData = []byte{0x01, 0x00} - } - for i := 0; i < int(numSets); i++ { - writeSet := bf.ReadUint16() - dataChunk := bf.ReadBytes(76) - setBytes := append([]byte{uint8(writeSet >> 8), uint8(writeSet & 0xff)}, dataChunk...) - for x := 0; true; x++ { - if x == int(savedSets) { - // appending set - if loadData[len(loadData)-1] == 0x10 { - // sanity check for if there was a messy manual import - loadData = append(loadData[:len(loadData)-2], setBytes...) - } else { - loadData = append(loadData, setBytes...) - } - savedSets++ - break - } - currentSet := loadData[3+(x*78)] - if int(currentSet) == int(writeSet) { - // replacing a set - loadData = append(loadData[:2+(x*78)], append(setBytes, loadData[2+((x+1)*78):]...)...) - break - } else if int(currentSet) > int(writeSet) { - // inserting before current set - loadData = append(loadData[:2+((x)*78)], append(setBytes, loadData[2+((x)*78):]...)...) - savedSets++ - break - } - } - loadData[1] = savedSets // update set count - } - dumpSaveData(s, loadData, "decomyset") - _, err := s.server.db.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", loadData, s.charID) - if err != nil { - s.logger.Error("Failed to save decomyset", zap.Error(err)) - } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + return } - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + + // Version handling + bf := byteframe.NewByteFrame() + var size uint + if s.server.erupeConfig.RealClientMode >= _config.G10 { + size = 76 + bf.WriteUint8(1) + } else { + size = 68 + bf.WriteUint8(0) + } + + // Handle nil data + if len(temp) == 0 { + temp = append(bf.Data(), uint8(0)) + } + + // Build a map of set data + sets := make(map[uint16][]byte) + oldSets := byteframe.NewByteFrameFromBytes(temp[2:]) + for i := uint8(0); i < temp[1]; i++ { + index := oldSets.ReadUint16() + sets[index] = oldSets.ReadBytes(size) + } + + // Overwrite existing sets + newSets := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload[2:]) + for i := uint8(0); i < pkt.RawDataPayload[1]; i++ { + index := newSets.ReadUint16() + sets[index] = newSets.ReadBytes(size) + } + + // Serialise the set data + bf.WriteUint8(uint8(len(sets))) + for u, b := range sets { + bf.WriteUint16(u) + bf.WriteBytes(b) + } + + dumpSaveData(s, bf.Data(), "decomyset") + s.server.db.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", bf.Data(), s.charID) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } type Title struct { @@ -353,12 +351,14 @@ func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireTitle) - var exists int - err := s.server.db.QueryRow("SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2", pkt.TitleID, s.charID).Scan(&exists) - if err != nil || exists == 0 { - s.server.db.Exec("INSERT INTO titles VALUES ($1, $2, now(), now())", pkt.TitleID, s.charID) - } else { - s.server.db.Exec("UPDATE titles SET updated_at=now()") + for _, title := range pkt.TitleIDs { + var exists int + err := s.server.db.QueryRow(`SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2`, title, s.charID).Scan(&exists) + if err != nil || exists == 0 { + s.server.db.Exec(`INSERT INTO titles VALUES ($1, $2, now(), now())`, title, s.charID) + } else { + s.server.db.Exec(`UPDATE titles SET updated_at=now() WHERE id=$1 AND char_id=$2`, title, s.charID) + } } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_mail.go b/server/channelserver/handlers_mail.go index 7e9784ee3..a596d927b 100644 --- a/server/channelserver/handlers_mail.go +++ b/server/channelserver/handlers_mail.go @@ -79,57 +79,6 @@ func (m *Mail) MarkRead(s *Session) error { return nil } -func (m *Mail) MarkDeleted(s *Session) error { - _, err := s.server.db.Exec(` - UPDATE mail SET deleted = true WHERE id = $1 - `, m.ID) - - if err != nil { - s.logger.Error( - "failed to mark mail as deleted", - zap.Error(err), - zap.Int("mailID", m.ID), - ) - return err - } - - return nil -} - -func (m *Mail) MarkAcquired(s *Session) error { - _, err := s.server.db.Exec(` - UPDATE mail SET attached_item_received = true WHERE id = $1 - `, m.ID) - - if err != nil { - s.logger.Error( - "failed to mark mail item as claimed", - zap.Error(err), - zap.Int("mailID", m.ID), - ) - return err - } - - return nil -} - -func (m *Mail) MarkLocked(s *Session, locked bool) error { - _, err := s.server.db.Exec(` - UPDATE mail SET locked = $1 WHERE id = $2 - `, locked, m.ID) - - if err != nil { - s.logger.Error( - "failed to mark mail as locked", - zap.Error(err), - zap.Int("mailID", m.ID), - ) - return err - } - - return nil -} - func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) { rows, err := s.server.db.Queryx(` SELECT @@ -256,26 +205,21 @@ func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMail) mailId := s.mailList[pkt.AccIndex] - if mailId == 0 { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic("attempting to read mail that doesn't exist in session map") + doAckBufSucceed(s, pkt.AckHandle, []byte{0}) + return } mail, err := GetMailByID(s, mailId) - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic(err) + doAckBufSucceed(s, pkt.AckHandle, []byte{0}) + return } - _ = mail.MarkRead(s) - + s.server.db.Exec(`UPDATE mail SET read = true WHERE id = $1`, mail.ID) bf := byteframe.NewByteFrame() - body := stringsupport.UTF8ToSJIS(mail.Body) bf.WriteNullTerminatedBytes(body) - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -283,10 +227,9 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfListMail) mail, err := GetMailListForCharacter(s, s.charID) - if err != nil { - doAckBufFail(s, pkt.AckHandle, make([]byte, 4)) - panic(err) + doAckBufSucceed(s, pkt.AckHandle, []byte{0}) + return } if s.mailList == nil { @@ -354,24 +297,20 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) { mail, err := GetMailByID(s, s.mailList[pkt.AccIndex]) if err != nil { - panic(err) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + return } switch pkt.Operation { - case mhfpacket.OPERATE_MAIL_DELETE: - err = mail.MarkDeleted(s) - case mhfpacket.OPERATE_MAIL_LOCK: - err = mail.MarkLocked(s, true) - case mhfpacket.OPERATE_MAIL_UNLOCK: - err = mail.MarkLocked(s, false) - case mhfpacket.OPERATE_MAIL_ACQUIRE_ITEM: - err = mail.MarkAcquired(s) + case mhfpacket.OperateMailDelete: + s.server.db.Exec(`UPDATE mail SET deleted = true WHERE id = $1`, mail.ID) + case mhfpacket.OperateMailLock: + s.server.db.Exec(`UPDATE mail SET locked = TRUE WHERE id = $1`, mail.ID) + case mhfpacket.OperateMailUnlock: + s.server.db.Exec(`UPDATE mail SET locked = FALSE WHERE id = $1`, mail.ID) + case mhfpacket.OperateMailAcquireItem: + s.server.db.Exec(`UPDATE mail SET attached_item_received = TRUE WHERE id = $1`, mail.ID) } - - if err != nil { - panic(err) - } - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 810786751..19c358c52 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -9,8 +9,6 @@ import ( "erupe-ce/server/channelserver/compression/nullcomp" "go.uber.org/zap" "io" - "os" - "path/filepath" "time" ) @@ -299,18 +297,12 @@ func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateAiroulist(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateAiroulist) resp := byteframe.NewByteFrame() - if _, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, "airoulist.bin")); err == nil { - data, _ := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, "airoulist.bin")) - resp.WriteBytes(data) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - return - } airouList := getGuildAirouList(s) resp.WriteUint16(uint16(len(airouList))) resp.WriteUint16(uint16(len(airouList))) for _, cat := range airouList { - resp.WriteUint32(cat.CatID) - resp.WriteBytes(cat.CatName) + resp.WriteUint32(cat.ID) + resp.WriteBytes(cat.Name) resp.WriteUint32(cat.Experience) resp.WriteUint8(cat.Personality) resp.WriteUint8(cat.Class) @@ -321,11 +313,10 @@ func handleMsgMhfEnumerateAiroulist(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -// CatDefinition holds values needed to populate the guild cat list -type CatDefinition struct { - CatID uint32 - CatName []byte - CurrentTask uint8 +type Airou struct { + ID uint32 + Name []byte + Task uint8 Personality uint8 Class uint8 Experience uint32 @@ -333,46 +324,39 @@ type CatDefinition struct { WeaponID uint16 } -func getGuildAirouList(s *Session) []CatDefinition { - var guild *Guild - var err error - var guildCats []CatDefinition - - // returning 0 cats on any guild issues - // can probably optimise all of the guild queries pretty heavily - guild, err = GetGuildInfoByCharacterId(s, s.charID) +func getGuildAirouList(s *Session) []Airou { + var guildCats []Airou + bannedCats := make(map[uint32]int) + guild, err := GetGuildInfoByCharacterId(s, s.charID) if err != nil { return guildCats } - - // Get cats used recently - // Retail reset at midday, 12 hours is a midpoint - tempBanDuration := 43200 - (1800) // Minus hunt time - bannedCats := make(map[uint32]int) - var csvTemp string - rows, err := s.server.db.Query(`SELECT cats_used - FROM guild_hunts gh - INNER JOIN characters c - ON gh.host_id = c.id - WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, TimeAdjusted().Unix()) + rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh + INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1 + `, s.charID) if err != nil { s.logger.Warn("Failed to get recently used airous", zap.Error(err)) + return guildCats } + + var csvTemp string + var startTemp time.Time for rows.Next() { - rows.Scan(&csvTemp) - for i, j := range stringsupport.CSVElems(csvTemp) { - bannedCats[uint32(j)] = i + err = rows.Scan(&csvTemp, &startTemp) + if err != nil { + continue + } + if startTemp.Add(time.Second * time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntPartnyaCooldown)).Before(TimeAdjusted()) { + for i, j := range stringsupport.CSVElems(csvTemp) { + bannedCats[uint32(j)] = i + } } } - // ellie's GetGuildMembers didn't seem to pull leader? - rows, err = s.server.db.Query(`SELECT c.otomoairou - FROM characters c - INNER JOIN guild_characters gc - ON gc.character_id = c.id + rows, err = s.server.db.Query(`SELECT c.otomoairou FROM characters c + INNER JOIN guild_characters gc ON gc.character_id = c.id WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL - ORDER BY c.id ASC - LIMIT 60;`, guild.ID) + ORDER BY c.id LIMIT 60`, guild.ID) if err != nil { s.logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err)) return guildCats @@ -381,11 +365,7 @@ func getGuildAirouList(s *Session) []CatDefinition { for rows.Next() { var data []byte err = rows.Scan(&data) - if err != nil { - s.logger.Warn("select failure", zap.Error(err)) - continue - } else if len(data) == 0 { - // non extant cats that aren't null in DB + if err != nil || len(data) == 0 { continue } // first byte has cat existence in general, can skip if 0 @@ -396,10 +376,10 @@ func getGuildAirouList(s *Session) []CatDefinition { continue } bf := byteframe.NewByteFrameFromBytes(decomp) - cats := GetCatDetails(bf) + cats := GetAirouDetails(bf) for _, cat := range cats { - _, exists := bannedCats[cat.CatID] - if cat.CurrentTask == 4 && !exists { + _, exists := bannedCats[cat.ID] + if cat.Task == 4 && !exists { guildCats = append(guildCats, cat) } } @@ -408,20 +388,20 @@ func getGuildAirouList(s *Session) []CatDefinition { return guildCats } -func GetCatDetails(bf *byteframe.ByteFrame) []CatDefinition { +func GetAirouDetails(bf *byteframe.ByteFrame) []Airou { catCount := bf.ReadUint8() - cats := make([]CatDefinition, catCount) + cats := make([]Airou, catCount) for x := 0; x < int(catCount); x++ { - var catDef CatDefinition + var catDef Airou // cat sometimes has additional bytes for whatever reason, gift items? timestamp? // until actual variance is known we can just seek to end based on start catDefLen := bf.ReadUint32() catStart, _ := bf.Seek(0, io.SeekCurrent) - catDef.CatID = bf.ReadUint32() - bf.Seek(1, io.SeekCurrent) // unknown value, probably a bool - catDef.CatName = bf.ReadBytes(18) // always 18 len, reads first null terminated string out of section and discards rest - catDef.CurrentTask = bf.ReadUint8() + catDef.ID = bf.ReadUint32() + bf.Seek(1, io.SeekCurrent) // unknown value, probably a bool + catDef.Name = bf.ReadBytes(18) // always 18 len, reads first null terminated string out of section and discards rest + catDef.Task = bf.ReadUint8() bf.Seek(16, io.SeekCurrent) // appearance data and what is seemingly null bytes catDef.Personality = bf.ReadUint8() catDef.Class = bf.ReadUint8() 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_plate.go b/server/channelserver/handlers_plate.go index 3f5688184..19fdd84a2 100644 --- a/server/channelserver/handlers_plate.go +++ b/server/channelserver/handlers_plate.go @@ -42,7 +42,7 @@ func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) { } } else { // create empty save if absent - data = make([]byte, 0x1AF20) + data = make([]byte, 140000) } // Perform diff and compress it to write back to db @@ -110,7 +110,7 @@ func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) { } } else { // create empty save if absent - data = make([]byte, 0x820) + data = make([]byte, 4800) } // Perform diff and compress it to write back to db @@ -147,7 +147,7 @@ func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) { err := s.server.db.QueryRow("SELECT platemyset FROM characters WHERE id = $1", s.charID).Scan(&data) if len(data) == 0 { s.logger.Error("Failed to load platemyset", zap.Error(err)) - data = make([]byte, 0x780) + data = make([]byte, 1920) } doAckBufSucceed(s, pkt.AckHandle, data) } diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index d9ec2bc08..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,29 +130,37 @@ 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) } } -func questSuffix(s *Session) string { - // Determine the letter to append for day / night - var timeSet string - if TimeGameAbsolute() > 2880 { - timeSet = "d" - } else { - timeSet = "n" - } - return fmt.Sprintf("%s%d", timeSet, s.server.Season()) -} - func seasonConversion(s *Session, questFile string) string { - filename := fmt.Sprintf("%s%s", questFile[:5], questSuffix(s)) + filename := fmt.Sprintf("%s%d", questFile[:6], s.server.Season()) - // Return original file if file doesn't exist - if _, err := os.Stat(filename); err == nil { + // Return the seasonal file + if _, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", filename))); err == nil { return filename } else { - return questFile + // Attempt to return the requested quest file if the seasonal file doesn't exist + if _, err = os.Stat(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", questFile))); err == nil { + return questFile + } + + // If the code reaches this point, it's most likely a custom quest with no seasonal variations in the files. + // Since event quests when seasonal pick day or night and the client requests either one, we need to differentiate between the two to prevent issues. + var _time string + + if TimeGameAbsolute() > 2880 { + _time = "d" + } else { + _time = "n" + } + + // Request a d0 or n0 file depending on the time of day. The time of day matters and issues will occur if it's different to the one it requests. + return fmt.Sprintf("%s%s%d", questFile[:5], _time, 0) } } @@ -103,31 +183,50 @@ func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { } func loadQuestFile(s *Session, questId int) []byte { + data, exists := s.server.questCacheData[questId] + if exists && s.server.questCacheTime[questId].Add(time.Duration(s.server.erupeConfig.QuestCacheExpiry)*time.Second).After(time.Now()) { + return data + } + file, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%05dd0.bin", questId))) if err != nil { return nil } 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()) @@ -139,24 +238,27 @@ func loadQuestFile(s *Session, questId int) []byte { } questBody.WriteBytes(newStrings.Data()) + s.server.questCacheData[questId] = questBody.Data() + s.server.questCacheTime[questId] = time.Now() return questBody.Data() } func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) { var id, mark uint32 - var questId int + var questId, activeDuration, inactiveDuration, flags int var maxPlayers, questType uint8 - rows.Scan(&id, &maxPlayers, &questType, &questId, &mark) + var startTime time.Time + rows.Scan(&id, &maxPlayers, &questType, &questId, &mark, &flags, &startTime, &activeDuration, &inactiveDuration) 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() bf.WriteUint32(id) - bf.WriteUint32(0) - bf.WriteUint8(0) // Indexer + bf.WriteUint32(0) // Unk + bf.WriteUint8(0) // Unk switch questType { case 16: bf.WriteUint8(s.server.erupeConfig.GameplayOptions.RegularRavienteMaxPlayers) @@ -177,15 +279,43 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) { } else { bf.WriteBool(true) } - bf.WriteUint16(0) + bf.WriteUint16(0) // Unk if _config.ErupeConfig.RealClientMode >= _config.G1 { bf.WriteUint32(mark) } - bf.WriteUint16(0) + bf.WriteUint16(0) // Unk bf.WriteUint16(uint16(len(data))) bf.WriteBytes(data) - ps.Uint8(bf, "", true) // What is this string for? + // Time Flag Replacement + // Bitset Structure: b8 UNK, b7 Required Objective, b6 UNK, b5 Night, b4 Day, b3 Cold, b2 Warm, b1 Spring + // if the byte is set to 0 the game choses the quest file corresponding to whatever season the game is on + bf.Seek(25, 0) + flagByte := bf.ReadUint8() + bf.Seek(25, 0) + if s.server.erupeConfig.GameplayOptions.SeasonOverride { + bf.WriteUint8(flagByte & 0b11100000) + } else { + // Allow for seasons to be specified in database, otherwise use the one in the file. + if flags < 0 { + bf.WriteUint8(flagByte) + } else { + bf.WriteUint8(uint8(flags)) + } + } + + // Bitset Structure Quest Variant 1: b8 UL Fixed, b7 UNK, b6 UNK, b5 UNK, b4 G Rank, b3 HC to UL, b2 Fix HC, b1 Hiden + // Bitset Structure Quest Variant 2: b8 Road, b7 High Conquest, b6 Fixed Difficulty, b5 No Active Feature, b4 Timer, b3 No Cuff, b2 No Halk Pots, b1 Low Conquest + // Bitset Structure Quest Variant 3: b8 No Sigils, b7 UNK, b6 Interception, b5 Zenith, b4 No GP Skills, b3 No Simple Mode?, b2 GSR to GR, b1 No Reward Skills + + bf.Seek(175, 0) + questVariant3 := bf.ReadUint8() + questVariant3 &= 0b11011111 // disable Interception flag + bf.Seek(175, 0) + bf.WriteUint8(questVariant3) + + bf.Seek(0, 2) + ps.Uint8(bf, "", true) // Debug/Notes string for quest return bf.Data(), nil } @@ -195,23 +325,62 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() bf.WriteUint16(0) - rows, _ := s.server.db.Query("SELECT id, COALESCE(max_players, 4) AS max_players, quest_type, quest_id, COALESCE(mark, 0) AS mark FROM event_quests ORDER BY quest_id") - for rows.Next() { - data, err := makeEventQuest(s, rows) - if err != nil { - continue - } else { - if len(data) > 896 || len(data) < 352 { + rows, err := s.server.db.Query("SELECT id, COALESCE(max_players, 4) AS max_players, quest_type, quest_id, COALESCE(mark, 0) AS mark, COALESCE(flags, -1), start_time, COALESCE(active_days, 0) AS active_days, COALESCE(inactive_days, 0) AS inactive_days FROM event_quests ORDER BY quest_id") + if err == nil { + currentTime := time.Now() + tx, _ := s.server.db.Begin() + + for rows.Next() { + var id, mark uint32 + var questId, flags, activeDays, inactiveDays int + var maxPlayers, questType uint8 + var startTime time.Time + + 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 + } + + // Use the Event Cycling system + if activeDays > 0 { + cycleLength := (time.Duration(activeDays) + time.Duration(inactiveDays)) * 24 * time.Hour + + // Count the number of full cycles elapsed since the last rotation. + extraCycles := int(currentTime.Sub(startTime) / cycleLength) + + if extraCycles > 0 { + // Calculate the rotation time based on start time, active duration, and inactive duration. + rotationTime := startTime.Add(time.Duration(activeDays+inactiveDays) * 24 * time.Hour * time.Duration(extraCycles)) + if currentTime.After(rotationTime) { + // Normalize rotationTime to 12PM JST to align with the in-game events update notification. + newRotationTime := time.Date(rotationTime.Year(), rotationTime.Month(), rotationTime.Day(), 12, 0, 0, 0, TimeAdjusted().Location()) + + _, err = tx.Exec("UPDATE event_quests SET start_time = $1 WHERE id = $2", newRotationTime, id) + if err != nil { + tx.Rollback() // Rollback if an error occurs + break + } + startTime = newRotationTime // Set the new start time so the quest can be used/removed immediately. + } + } + + // Check if the quest is currently active + if currentTime.Before(startTime) || currentTime.After(startTime.Add(time.Duration(activeDays)*24*time.Hour)) { + continue + } + } + + data, err := makeEventQuest(s, rows) + if err != nil { + s.logger.Error("Failed to make event quest", zap.Error(err)) continue } else { - totalCount++ - if _config.ErupeConfig.RealClientMode == _config.F5 { - if totalCount > pkt.Offset && len(bf.Data()) < 21550 { - returnedCount++ - bf.WriteBytes(data) - continue - } + if len(data) > 896 || len(data) < 352 { + s.logger.Error("Invalid quest data length", zap.Int("len", len(data))) + continue } else { + totalCount++ if totalCount > pkt.Offset && len(bf.Data()) < 60000 { returnedCount++ bf.WriteBytes(data) @@ -220,11 +389,9 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { } } } - } - type tuneValue struct { - ID uint16 - Value uint16 + rows.Close() + tx.Commit() } tuneValues := []tuneValue{ @@ -239,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}, @@ -300,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}, @@ -327,245 +500,11 @@ 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)}) - tuneValues = append(tuneValues, tuneValue{1029, s.server.erupeConfig.GameplayOptions.GUrgentRate}) + tuneValues = append(tuneValues, tuneValue{1029, uint16(s.server.erupeConfig.GameplayOptions.GUrgentRate * 100)}) if s.server.erupeConfig.GameplayOptions.DisableHunterNavi { tuneValues = append(tuneValues, tuneValue{1037, 1}) @@ -573,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 { @@ -657,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) @@ -698,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_register.go b/server/channelserver/handlers_register.go index 33261a94e..3d47c3633 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -6,256 +6,124 @@ import ( "strings" ) +func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfRegisterEvent) + bf := byteframe.NewByteFrame() + // Some kind of check if there's already a session + if pkt.Unk1 && s.server.getRaviSemaphore() == nil { + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + return + } + bf.WriteUint8(uint8(pkt.WorldID)) + bf.WriteUint8(uint8(pkt.LandID)) + bf.WriteUint16(s.server.raviente.id) + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReleaseEvent) + + // Do this ack manually because it uses a non-(0|1) error code + /* + _ACK_SUCCESS = 0 + _ACK_ERROR = 1 + + _ACK_EINPROGRESS = 16 + _ACK_ENOENT = 17 + _ACK_ENOSPC = 18 + _ACK_ETIMEOUT = 19 + + _ACK_EINVALID = 64 + _ACK_EFAILED = 65 + _ACK_ENOMEM = 66 + _ACK_ENOTEXIT = 67 + _ACK_ENOTREADY = 68 + _ACK_EALREADY = 69 + _ACK_DISABLE_WORK = 71 + */ + s.QueueSendMHF(&mhfpacket.MsgSysAck{ + AckHandle: pkt.AckHandle, + IsBufferResponse: false, + ErrorCode: 0x41, + AckData: []byte{0x00, 0x00, 0x00, 0x00}, + }) +} + +type RaviUpdate struct { + Op uint8 + Dest uint8 + Data uint32 +} + func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysOperateRegister) - bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload) - s.server.raviente.Lock() - switch pkt.SemaphoreID { - case 4: - resp := byteframe.NewByteFrame() - size := 6 - for i := 0; i < len(bf.Data())-1; i += size { - op := bf.ReadUint8() - dest := bf.ReadUint8() - data := bf.ReadUint32() - resp.WriteUint8(1) - resp.WriteUint8(dest) - ref := &s.server.raviente.state.stateData[dest] - damageMultiplier := s.server.raviente.GetRaviMultiplier(s.server) - switch op { - case 2: - resp.WriteUint32(*ref) - if dest == 28 { // Berserk resurrection tracker - resp.WriteUint32(*ref + data) - *ref += data - } else if dest == 17 { // Berserk poison tracker - if damageMultiplier == 1 { - resp.WriteUint32(*ref + data) - *ref += data - } else { - resp.WriteUint32(*ref) - } - } else { - data = uint32(float64(data) * damageMultiplier) - resp.WriteUint32(*ref + data) - *ref += data - } - case 13: - fallthrough - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - } - } - resp.WriteUint8(0) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 5: - resp := byteframe.NewByteFrame() - size := 6 - for i := 0; i < len(bf.Data())-1; i += size { - op := bf.ReadUint8() - dest := bf.ReadUint8() - data := bf.ReadUint32() - resp.WriteUint8(1) - resp.WriteUint8(dest) - ref := &s.server.raviente.support.supportData[dest] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - fallthrough - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - } - } - resp.WriteUint8(0) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 6: - resp := byteframe.NewByteFrame() - size := 6 - for i := 0; i < len(bf.Data())-1; i += size { - op := bf.ReadUint8() - dest := bf.ReadUint8() - data := bf.ReadUint32() - resp.WriteUint8(1) - resp.WriteUint8(dest) - switch dest { - case 0: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.nextTime = data - case 1: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.startTime = data - case 2: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.killedTime = data - case 3: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.postTime = data - case 4: - ref := &s.server.raviente.register.register[0] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - } - case 5: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.carveQuest = data - case 6: - ref := &s.server.raviente.register.register[1] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - } - case 7: - ref := &s.server.raviente.register.register[2] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - } - case 8: - ref := &s.server.raviente.register.register[3] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - } - case 9: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.maxPlayers = data - case 10: - resp.WriteUint32(0) - resp.WriteUint32(data) - s.server.raviente.register.ravienteType = data - case 11: - ref := &s.server.raviente.register.register[4] - switch op { - case 2: - resp.WriteUint32(*ref) - resp.WriteUint32(*ref + data) - *ref += data - case 13: - resp.WriteUint32(0) - resp.WriteUint32(data) - *ref = data - case 14: - resp.WriteUint32(0) - resp.WriteUint32(data) - } - default: - resp.WriteUint32(0) - resp.WriteUint32(0) - } - } - resp.WriteUint8(0) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + + var raviUpdates []RaviUpdate + var raviUpdate RaviUpdate + // Strip null terminator + bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload[:len(pkt.RawDataPayload)-1]) + for i := len(pkt.RawDataPayload) / 6; i > 0; i-- { + raviUpdate.Op = bf.ReadUint8() + raviUpdate.Dest = bf.ReadUint8() + raviUpdate.Data = bf.ReadUint32() + raviUpdates = append(raviUpdates, raviUpdate) } + bf = byteframe.NewByteFrame() + + var _old, _new uint32 + s.server.raviente.Lock() + for _, update := range raviUpdates { + switch update.Op { + case 2: + _old, _new = s.server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, true) + case 13, 14: + _old, _new = s.server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, false) + } + bf.WriteUint8(1) + bf.WriteUint8(update.Dest) + bf.WriteUint32(_old) + bf.WriteUint32(_new) + } + s.server.raviente.Unlock() + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + if s.server.erupeConfig.GameplayOptions.LowLatencyRaviente { s.notifyRavi() } - s.server.raviente.Unlock() } func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysLoadRegister) - r := pkt.Unk1 - switch r { - case 12: - resp := byteframe.NewByteFrame() - resp.WriteUint8(0) - resp.WriteUint8(12) - resp.WriteUint32(s.server.raviente.register.nextTime) - resp.WriteUint32(s.server.raviente.register.startTime) - resp.WriteUint32(s.server.raviente.register.killedTime) - resp.WriteUint32(s.server.raviente.register.postTime) - resp.WriteUint32(s.server.raviente.register.register[0]) - resp.WriteUint32(s.server.raviente.register.carveQuest) - resp.WriteUint32(s.server.raviente.register.register[1]) - resp.WriteUint32(s.server.raviente.register.register[2]) - resp.WriteUint32(s.server.raviente.register.register[3]) - resp.WriteUint32(s.server.raviente.register.maxPlayers) - resp.WriteUint32(s.server.raviente.register.ravienteType) - resp.WriteUint32(s.server.raviente.register.register[4]) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 29: - resp := byteframe.NewByteFrame() - resp.WriteUint8(0) - resp.WriteUint8(29) - for _, v := range s.server.raviente.state.stateData { - resp.WriteUint32(v) + bf := byteframe.NewByteFrame() + bf.WriteUint8(0) + bf.WriteUint8(pkt.Values) + for i := uint8(0); i < pkt.Values; i++ { + switch pkt.RegisterID { + case 0x40000: + bf.WriteUint32(s.server.raviente.state[i]) + case 0x50000: + bf.WriteUint32(s.server.raviente.support[i]) + case 0x60000: + bf.WriteUint32(s.server.raviente.register[i]) } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - case 25: - resp := byteframe.NewByteFrame() - resp.WriteUint8(0) - resp.WriteUint8(25) - for _, v := range s.server.raviente.support.supportData { - resp.WriteUint32(v) - } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func (s *Session) notifyRavi() { - sema := getRaviSemaphore(s.server) + sema := s.server.getRaviSemaphore() if sema == nil { return } var temp mhfpacket.MHFPacket raviNotif := byteframe.NewByteFrame() - temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4} + temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 0x40000} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) - temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5} + temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 0x50000} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) - temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 6} + temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 0x60000} raviNotif.WriteUint16(uint16(temp.Opcode())) temp.Build(raviNotif, s.clientContext) raviNotif.WriteUint16(0x0010) // End it. @@ -272,28 +140,13 @@ func (s *Session) notifyRavi() { } } -func getRaviSemaphore(s *Server) *Semaphore { +func (s *Server) getRaviSemaphore() *Semaphore { for _, semaphore := range s.semaphore { - if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "3") { + if strings.HasPrefix(semaphore.name, "hs_l0") && strings.HasSuffix(semaphore.name, "3") { return semaphore } } return nil } -func resetRavi(s *Session) { - s.server.raviente.Lock() - s.server.raviente.register.nextTime = 0 - s.server.raviente.register.startTime = 0 - s.server.raviente.register.killedTime = 0 - s.server.raviente.register.postTime = 0 - s.server.raviente.register.ravienteType = 0 - s.server.raviente.register.maxPlayers = 0 - s.server.raviente.register.carveQuest = 0 - s.server.raviente.register.register = []uint32{0, 0, 0, 0, 0} - s.server.raviente.state.stateData = []uint32{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.raviente.support.supportData = []uint32{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.raviente.Unlock() -} - func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index 503df5110..19925c6d6 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -2,7 +2,6 @@ package channelserver import ( "erupe-ce/common/byteframe" - "fmt" "go.uber.org/zap" "strconv" "strings" @@ -13,9 +12,6 @@ import ( func removeSessionFromSemaphore(s *Session) { s.server.semaphoreLock.Lock() for _, semaphore := range s.server.semaphore { - if _, exists := semaphore.reservedClientSlots[s.charID]; exists { - delete(semaphore.reservedClientSlots, s.charID) - } if _, exists := semaphore.clients[s]; exists { delete(semaphore.clients, s) } @@ -31,48 +27,38 @@ func handleMsgSysCreateSemaphore(s *Session, p mhfpacket.MHFPacket) { func destructEmptySemaphores(s *Session) { s.server.semaphoreLock.Lock() for id, sema := range s.server.semaphore { - if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 { - s.server.semaphoreLock.Unlock() + if len(sema.clients) == 0 { delete(s.server.semaphore, id) - s.server.semaphoreLock.Lock() - if strings.HasPrefix(id, "hs_l0u3B5") { - releaseRaviSemaphore(s, sema) + if strings.HasPrefix(id, "hs_l0") { + s.server.resetRaviente() } - s.logger.Debug("Destructed semaphore", zap.String("sema.id_semaphore", id)) + s.logger.Debug("Destructed semaphore", zap.String("sema.name", id)) } } s.server.semaphoreLock.Unlock() } -func releaseRaviSemaphore(s *Session, sema *Semaphore) { - delete(sema.reservedClientSlots, s.charID) - delete(sema.clients, s) - if strings.HasSuffix(sema.id_semaphore, "2") && len(sema.clients) == 0 { - s.logger.Debug("Main raviente semaphore is empty, resetting") - resetRavi(s) - } -} - func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysDeleteSemaphore) - if s.server.semaphore != nil { - destructEmptySemaphores(s) - s.server.semaphoreLock.Lock() - for id, sema := range s.server.semaphore { - if sema.id == pkt.SemaphoreID { - if strings.HasPrefix(id, "hs_l0u3B5") { - releaseRaviSemaphore(s, sema) - s.server.semaphoreLock.Unlock() - return + destructEmptySemaphores(s) + s.server.semaphoreLock.Lock() + for id, sema := range s.server.semaphore { + if sema.id == pkt.SemaphoreID { + for session := range sema.clients { + if s == session { + delete(sema.clients, s) } - s.server.semaphoreLock.Unlock() + } + if len(sema.clients) == 0 { delete(s.server.semaphore, id) - s.logger.Debug("Destructed semaphore", zap.String("sema.id_semaphore", id)) - return + if strings.HasPrefix(id, "hs_l0") { + s.server.resetRaviente() + } + s.logger.Debug("Destructed semaphore", zap.String("sema.name", id)) } } - s.server.semaphoreLock.Unlock() } + s.server.semaphoreLock.Unlock() } func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { @@ -80,18 +66,15 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { SemaphoreID := pkt.SemaphoreID newSemaphore, exists := s.server.semaphore[SemaphoreID] - - fmt.Printf("Got reserve stage req, StageID: %v\n\n", SemaphoreID) if !exists { s.server.semaphoreLock.Lock() - if strings.HasPrefix(SemaphoreID, "hs_l0u3B5") { + if strings.HasPrefix(SemaphoreID, "hs_l0") { suffix, _ := strconv.Atoi(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:]) s.server.semaphore[SemaphoreID] = &Semaphore{ - id_semaphore: pkt.SemaphoreID, - id: uint32(suffix + 1), - clients: make(map[*Session]uint32), - reservedClientSlots: make(map[uint32]interface{}), - maxPlayers: 127, + name: pkt.SemaphoreID, + id: uint32((suffix + 1) * 0x10000), + clients: make(map[*Session]uint32), + maxPlayers: 127, } } else { s.server.semaphore[SemaphoreID] = NewSemaphore(s.server, SemaphoreID, 1) @@ -102,22 +85,19 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { newSemaphore.Lock() defer newSemaphore.Unlock() - if _, exists := newSemaphore.reservedClientSlots[s.charID]; exists { - bf := byteframe.NewByteFrame() + bf := byteframe.NewByteFrame() + if _, exists := newSemaphore.clients[s]; exists { bf.WriteUint32(newSemaphore.id) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) - } else if uint16(len(newSemaphore.reservedClientSlots)) < newSemaphore.maxPlayers { - newSemaphore.reservedClientSlots[s.charID] = nil + } else if uint16(len(newSemaphore.clients)) < newSemaphore.maxPlayers { newSemaphore.clients[s] = s.charID s.Lock() s.semaphore = newSemaphore s.Unlock() - bf := byteframe.NewByteFrame() bf.WriteUint32(newSemaphore.id) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } else { - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + bf.WriteUint32(0) } + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { @@ -130,7 +110,6 @@ func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { } else { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } - } func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index 38b036f7f..3058fb632 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -10,13 +10,13 @@ import ( type ShopItem struct { ID uint32 `db:"id"` - ItemID uint16 `db:"item_id"` + ItemID uint32 `db:"item_id"` Cost uint32 `db:"cost"` Quantity uint16 `db:"quantity"` MinHR uint16 `db:"min_hr"` MinSR uint16 `db:"min_sr"` MinGR uint16 `db:"min_gr"` - StoreLevel uint16 `db:"store_level"` + StoreLevel uint8 `db:"store_level"` MaxQuantity uint16 `db:"max_quantity"` UsedQuantity uint16 `db:"used_quantity"` RoadFloors uint16 `db:"road_floors"` @@ -61,19 +61,30 @@ func writeShopItems(bf *byteframe.ByteFrame, items []ShopItem) { bf.WriteUint16(uint16(len(items))) bf.WriteUint16(uint16(len(items))) for _, item := range items { - bf.WriteUint32(item.ID) - bf.WriteUint16(0) - bf.WriteUint16(item.ItemID) + if _config.ErupeConfig.RealClientMode >= _config.Z2 { + bf.WriteUint32(item.ID) + } + bf.WriteUint32(item.ItemID) bf.WriteUint32(item.Cost) bf.WriteUint16(item.Quantity) bf.WriteUint16(item.MinHR) bf.WriteUint16(item.MinSR) - bf.WriteUint16(item.MinGR) - bf.WriteUint16(item.StoreLevel) - bf.WriteUint16(item.MaxQuantity) - bf.WriteUint16(item.UsedQuantity) - bf.WriteUint16(item.RoadFloors) - bf.WriteUint16(item.RoadFatalis) + if _config.ErupeConfig.RealClientMode >= _config.Z2 { + bf.WriteUint16(item.MinGR) + } + bf.WriteUint8(0) // Unk + bf.WriteUint8(item.StoreLevel) + if _config.ErupeConfig.RealClientMode >= _config.Z2 { + bf.WriteUint16(item.MaxQuantity) + bf.WriteUint16(item.UsedQuantity) + } + if _config.ErupeConfig.RealClientMode == _config.Z1 { + bf.WriteUint8(uint8(item.RoadFloors)) + bf.WriteUint8(uint8(item.RoadFatalis)) + } else if _config.ErupeConfig.RealClientMode >= _config.Z2 { + bf.WriteUint16(item.RoadFloors) + bf.WriteUint16(item.RoadFatalis) + } } } @@ -116,103 +127,113 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { return } - var count uint16 - shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop") + rows, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop") if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) + bf := byteframe.NewByteFrame() var gacha Gacha - for shopEntries.Next() { - err = shopEntries.StructScan(&gacha) - if err != nil { - continue + var gachas []Gacha + for rows.Next() { + err = rows.StructScan(&gacha) + if err == nil { + gachas = append(gachas, gacha) } - resp.WriteUint32(gacha.ID) - resp.WriteBytes(make([]byte, 16)) // Rank restriction - resp.WriteUint32(gacha.MinGR) - resp.WriteUint32(gacha.MinHR) - resp.WriteUint32(0) // only 0 in known packet - ps.Uint8(resp, gacha.Name, true) - ps.Uint8(resp, gacha.URLBanner, false) - ps.Uint8(resp, gacha.URLFeature, false) - resp.WriteBool(gacha.Wide) - ps.Uint8(resp, gacha.URLThumbnail, false) - resp.WriteUint8(0) // Unk - if gacha.Recommended { - resp.WriteUint8(2) - } else { - resp.WriteUint8(0) - } - resp.WriteUint8(gacha.GachaType) - resp.WriteBool(gacha.Hidden) - count++ } - resp.Seek(0, 0) - resp.WriteUint16(count) - resp.WriteUint16(count) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf.WriteUint16(uint16(len(gachas))) + bf.WriteUint16(uint16(len(gachas))) + for _, g := range gachas { + bf.WriteUint32(g.ID) + bf.WriteUint32(0) // Unknown rank restrictions + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(g.MinGR) + bf.WriteUint32(g.MinHR) + bf.WriteUint32(0) // only 0 in known packet + ps.Uint8(bf, g.Name, true) + ps.Uint8(bf, g.URLBanner, false) + ps.Uint8(bf, g.URLFeature, false) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + bf.WriteBool(g.Wide) + ps.Uint8(bf, g.URLThumbnail, false) + } + if g.Recommended { + bf.WriteUint16(2) + } else { + bf.WriteUint16(0) + } + bf.WriteUint8(g.GachaType) + if _config.ErupeConfig.RealClientMode >= _config.G10 { + bf.WriteBool(g.Hidden) + } + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) case 2: // Actual gacha bf := byteframe.NewByteFrame() bf.WriteUint32(pkt.ShopID) var gachaType int s.server.db.QueryRow(`SELECT gacha_type FROM gacha_shop WHERE id = $1`, pkt.ShopID).Scan(&gachaType) - entries, err := s.server.db.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points, name FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID) + rows, err := s.server.db.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points, COALESCE(name, '') AS name FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } var divisor float64 s.server.db.QueryRow(`SELECT COALESCE(SUM(weight) / 100000.0, 0) AS chance FROM gacha_entries WHERE gacha_id = $1`, pkt.ShopID).Scan(&divisor) - var entryCount uint16 - bf.WriteUint16(0) - gachaEntry := GachaEntry{} - gachaItem := GachaItem{} - for entries.Next() { - entryCount++ - entries.StructScan(&gachaEntry) - bf.WriteUint8(gachaEntry.EntryType) - bf.WriteUint32(gachaEntry.ID) - bf.WriteUint8(gachaEntry.ItemType) - bf.WriteUint32(gachaEntry.ItemNumber) - bf.WriteUint16(gachaEntry.ItemQuantity) + + var entry GachaEntry + var entries []GachaEntry + var item GachaItem + for rows.Next() { + err = rows.StructScan(&entry) + if err == nil { + entries = append(entries, entry) + } + } + bf.WriteUint16(uint16(len(entries))) + for _, ge := range entries { + var items []GachaItem + bf.WriteUint8(ge.EntryType) + bf.WriteUint32(ge.ID) + bf.WriteUint8(ge.ItemType) + bf.WriteUint32(ge.ItemNumber) + bf.WriteUint16(ge.ItemQuantity) if gachaType >= 4 { // If box bf.WriteUint16(1) } else { - bf.WriteUint16(uint16(gachaEntry.Weight / divisor)) + bf.WriteUint16(uint16(ge.Weight / divisor)) } - bf.WriteUint8(gachaEntry.Rarity) - bf.WriteUint8(gachaEntry.Rolls) + bf.WriteUint8(ge.Rarity) + bf.WriteUint8(ge.Rolls) - var itemCount uint8 - temp := byteframe.NewByteFrame() - items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, gachaEntry.ID) + rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, ge.ID) if err != nil { bf.WriteUint8(0) } else { - for items.Next() { - itemCount++ - items.StructScan(&gachaItem) - temp.WriteUint16(uint16(gachaItem.ItemType)) - temp.WriteUint16(gachaItem.ItemID) - temp.WriteUint16(gachaItem.Quantity) + for rows.Next() { + err = rows.StructScan(&item) + if err == nil { + items = append(items, item) + } } - bf.WriteUint8(itemCount) + bf.WriteUint8(uint8(len(items))) } - bf.WriteUint16(gachaEntry.FrontierPoints) - bf.WriteUint8(gachaEntry.DailyLimit) - if gachaEntry.EntryType < 10 { - ps.Uint8(bf, gachaEntry.Name, true) + bf.WriteUint16(ge.FrontierPoints) + bf.WriteUint8(ge.DailyLimit) + if ge.EntryType < 10 { + ps.Uint8(bf, ge.Name, true) } else { bf.WriteUint8(0) } - bf.WriteBytes(temp.Data()) + for _, gi := range items { + bf.WriteUint16(uint16(gi.ItemType)) + bf.WriteUint16(gi.ItemID) + bf.WriteUint16(gi.Quantity) + } } - bf.Seek(4, 0) - bf.WriteUint16(entryCount) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) case 3: // Hunting Festival Exchange fallthrough @@ -231,6 +252,9 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { case 10: // Item shop, 0-8 bf := byteframe.NewByteFrame() items := getShopItems(s, pkt.ShopType, pkt.ShopID) + if len(items) > int(pkt.Limit) { + items = items[:pkt.Limit] + } writeShopItems(bf, items) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -424,7 +448,7 @@ func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPlayNormalGacha) bf := byteframe.NewByteFrame() - var gachaEntries []GachaEntry + var entries []GachaEntry var entry GachaEntry var rewards []GachaItem var reward GachaItem @@ -433,31 +457,40 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) return } - temp := byteframe.NewByteFrame() - entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) + + rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) return } - for entries.Next() { - entries.StructScan(&entry) - gachaEntries = append(gachaEntries, entry) - } - rewardEntries, err := getRandomEntries(gachaEntries, rolls, false) - for i := range rewardEntries { - items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + for rows.Next() { + err = rows.StructScan(&entry) if err != nil { continue } - for items.Next() { - items.StructScan(&reward) + entries = append(entries, entry) + } + + rewardEntries, err := getRandomEntries(entries, rolls, false) + temp := byteframe.NewByteFrame() + for i := range rewardEntries { + rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + if err != nil { + continue + } + for rows.Next() { + err = rows.StructScan(&reward) + if err != nil { + continue + } rewards = append(rewards, reward) temp.WriteUint8(reward.ItemType) temp.WriteUint16(reward.ItemID) temp.WriteUint16(reward.Quantity) - temp.WriteUint8(entry.Rarity) + temp.WriteUint8(rewardEntries[i].Rarity) } } + bf.WriteUint8(uint8(len(rewards))) bf.WriteBytes(temp.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) @@ -467,7 +500,7 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPlayStepupGacha) bf := byteframe.NewByteFrame() - var gachaEntries []GachaEntry + var entries []GachaEntry var entry GachaEntry var rewards []GachaItem var reward GachaItem @@ -479,40 +512,49 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) { s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+(SELECT frontier_points FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.GachaID, pkt.RollType, s.charID) s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID) s.server.db.Exec(`INSERT INTO gacha_stepup (gacha_id, step, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, pkt.RollType+1, s.charID) - temp := byteframe.NewByteFrame() - guaranteedItems := getGuaranteedItems(s, pkt.GachaID, pkt.RollType) - for _, item := range guaranteedItems { - temp.WriteUint8(item.ItemType) - temp.WriteUint16(item.ItemID) - temp.WriteUint16(item.Quantity) - temp.WriteUint8(0) - } - entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) + + rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) return } - for entries.Next() { - entries.StructScan(&entry) - gachaEntries = append(gachaEntries, entry) - } - rewardEntries, err := getRandomEntries(gachaEntries, rolls, false) - for i := range rewardEntries { - items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + for rows.Next() { + err = rows.StructScan(&entry) if err != nil { continue } - for items.Next() { - items.StructScan(&reward) + entries = append(entries, entry) + } + + guaranteedItems := getGuaranteedItems(s, pkt.GachaID, pkt.RollType) + rewardEntries, err := getRandomEntries(entries, rolls, false) + temp := byteframe.NewByteFrame() + for i := range rewardEntries { + rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + if err != nil { + continue + } + for rows.Next() { + err = rows.StructScan(&reward) + if err != nil { + continue + } rewards = append(rewards, reward) temp.WriteUint8(reward.ItemType) temp.WriteUint16(reward.ItemID) temp.WriteUint16(reward.Quantity) - temp.WriteUint8(entry.Rarity) + temp.WriteUint8(rewardEntries[i].Rarity) } } + bf.WriteUint8(uint8(len(rewards) + len(guaranteedItems))) bf.WriteUint8(uint8(len(rewards))) + for _, item := range guaranteedItems { + bf.WriteUint8(item.ItemType) + bf.WriteUint16(item.ItemID) + bf.WriteUint16(item.Quantity) + bf.WriteUint8(0) + } bf.WriteBytes(temp.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) addGachaItem(s, rewards) @@ -561,7 +603,7 @@ func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPlayBoxGacha) bf := byteframe.NewByteFrame() - var gachaEntries []GachaEntry + var entries []GachaEntry var entry GachaEntry var rewards []GachaItem var reward GachaItem @@ -570,17 +612,18 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) return } - temp := byteframe.NewByteFrame() - entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) + rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) return } - for entries.Next() { - entries.StructScan(&entry) - gachaEntries = append(gachaEntries, entry) + for rows.Next() { + err = rows.StructScan(&entry) + if err == nil { + entries = append(entries, entry) + } } - rewardEntries, err := getRandomEntries(gachaEntries, rolls, true) + rewardEntries, err := getRandomEntries(entries, rolls, true) for i := range rewardEntries { items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) if err != nil { @@ -588,16 +631,19 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) { } s.server.db.Exec(`INSERT INTO gacha_box (gacha_id, entry_id, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, rewardEntries[i].ID, s.charID) for items.Next() { - items.StructScan(&reward) - rewards = append(rewards, reward) - temp.WriteUint8(reward.ItemType) - temp.WriteUint16(reward.ItemID) - temp.WriteUint16(reward.Quantity) - temp.WriteUint8(0) + err = items.StructScan(&reward) + if err == nil { + rewards = append(rewards, reward) + } } } bf.WriteUint8(uint8(len(rewards))) - bf.WriteBytes(temp.Data()) + for _, r := range rewards { + bf.WriteUint8(r.ItemType) + bf.WriteUint16(r.ItemID) + bf.WriteUint16(r.Quantity) + bf.WriteUint8(0) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) addGachaItem(s, rewards) } @@ -632,59 +678,59 @@ func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } +type FPointExchange struct { + ID uint32 `db:"id"` + ItemType uint8 `db:"item_type"` + ItemID uint16 `db:"item_id"` + Quantity uint16 `db:"quantity"` + FPoints uint16 `db:"fpoints"` + Buyable bool `db:"buyable"` +} + func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetFpointExchangeList) - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) - var buyables, sellables uint16 - var id uint32 - var itemType uint8 - var itemID, quantity, fPoints uint16 - buyRows, err := s.server.db.Query("SELECT id,item_type,item_id,quantity,fpoints FROM fpoint_items WHERE trade_type=0") + bf := byteframe.NewByteFrame() + var exchange FPointExchange + var exchanges []FPointExchange + var buyables uint16 + rows, err := s.server.db.Queryx(`SELECT id, item_type, item_id, quantity, fpoints, buyable FROM fpoint_items ORDER BY buyable DESC`) if err == nil { - for buyRows.Next() { - err = buyRows.Scan(&id, &itemType, &itemID, &quantity, &fPoints) + for rows.Next() { + err = rows.StructScan(&exchange) if err != nil { continue } - resp.WriteUint32(id) - resp.WriteUint16(0) - resp.WriteUint16(0) - resp.WriteUint16(0) - resp.WriteUint8(itemType) - resp.WriteUint16(itemID) - resp.WriteUint16(quantity) - resp.WriteUint16(fPoints) - buyables++ - } - } - - sellRows, err := s.server.db.Query("SELECT id,item_type,item_id,quantity,fpoints FROM fpoint_items WHERE trade_type=1") - if err == nil { - for sellRows.Next() { - err = sellRows.Scan(&id, &itemType, &itemID, &quantity, &fPoints) - if err != nil { - continue + if exchange.Buyable { + buyables++ } - resp.WriteUint32(id) - resp.WriteUint16(0) - resp.WriteUint16(0) - resp.WriteUint16(0) - resp.WriteUint8(itemType) - resp.WriteUint16(itemID) - resp.WriteUint16(quantity) - resp.WriteUint16(fPoints) - sellables++ + exchanges = append(exchanges, exchange) } } - resp.Seek(0, 0) - resp.WriteUint16(buyables) - resp.WriteUint16(sellables) + if _config.ErupeConfig.RealClientMode <= _config.Z2 { + bf.WriteUint8(uint8(len(exchanges))) + bf.WriteUint8(uint8(buyables)) + } else { + bf.WriteUint16(uint16(len(exchanges))) + bf.WriteUint16(buyables) + } + for _, e := range exchanges { + bf.WriteUint32(e.ID) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint16(0) + bf.WriteUint8(e.ItemType) + bf.WriteUint16(e.ItemID) + bf.WriteUint16(e.Quantity) + bf.WriteUint16(e.FPoints) + } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfPlayFreeGacha(s *Session, p mhfpacket.MHFPacket) { - // not sure this is used anywhere, free gachas use the MSG_MHF_PLAY_NORMAL_GACHA method in captures + pkt := p.(*mhfpacket.MsgMhfPlayFreeGacha) + bf := byteframe.NewByteFrame() + bf.WriteUint32(1) + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index e411365d3..bc0b3689e 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -55,7 +55,6 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { // Save our new stage ID and pointer to the new stage itself. s.Lock() - s.stageID = stageID s.stage = s.server.stages[stageID] s.Unlock() @@ -153,17 +152,13 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) // Push our current stage ID to the movement stack before entering another one. - if s.stageID == "" { - s.stageMoveStack.Set(pkt.StageID) - } else { + if s.stage != nil { s.stage.Lock() s.stage.reservedClientSlots[s.charID] = false s.stage.Unlock() - s.stageMoveStack.Push(s.stageID) - s.stageMoveStack.Lock() + s.stageMoveStack.Push(s.stage.id) } - s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) if s.reservationStage != nil { s.reservationStage = nil } @@ -175,10 +170,9 @@ 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 err != nil { - panic(err) + if backStage == "" || err != nil { + backStage = "sl1Ns200p0a0u0" } if _, exists := s.stage.reservedClientSlots[s.charID]; exists { @@ -194,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 unlocked - if !s.stageMoveStack.Locked { - s.stageMoveStack.Set(pkt.StageID) - } - doStageTransfer(s, pkt.AckHandle, pkt.StageID) } @@ -207,9 +195,12 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysLockStage) - // TODO(Andoryuuta): What does this packet _actually_ do? - // I think this is supposed to mark a stage as no longer able to accept client reservations - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + if stage, exists := s.server.stages[pkt.StageID]; exists { + stage.Lock() + stage.locked = true + stage.Unlock() + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { @@ -219,7 +210,9 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { for charID := range s.reservationStage.reservedClientSlots { session := s.server.FindSessionByCharID(charID) - session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + if session != nil { + session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + } } delete(s.server.stages, s.reservationStage.id) @@ -242,6 +235,10 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers { + if stage.locked { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + return + } if len(stage.password) > 0 { if stage.password != s.stagePass { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) @@ -367,39 +364,43 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { defer s.server.stagesLock.RUnlock() // Build the response - resp := byteframe.NewByteFrame() bf := byteframe.NewByteFrame() - var joinable int + var joinable uint16 + bf.WriteUint16(0) for sid, stage := range s.server.stages { stage.RLock() - defer stage.RUnlock() if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 { + stage.RUnlock() continue } - if !strings.Contains(stage.id, pkt.StagePrefix) { + stage.RUnlock() continue } - joinable++ - resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players. - resp.WriteUint16(0) // Unk - resp.WriteUint8(0) // Unk - resp.WriteBool(len(stage.clients) > 0) // Has departed. - resp.WriteUint16(stage.maxPlayers) // Max players. - if len(stage.password) > 0 { - // This byte has also been seen as 1 - // The quest is also recognised as locked when this is 2 - resp.WriteUint8(3) + bf.WriteUint16(uint16(len(stage.reservedClientSlots))) + bf.WriteUint16(uint16(len(stage.clients))) + if strings.HasPrefix(stage.id, "sl2Ls") { + bf.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots))) } else { - resp.WriteUint8(0) + bf.WriteUint16(uint16(len(stage.clients))) } - ps.Uint8(resp, sid, false) + bf.WriteUint16(stage.maxPlayers) + var flags uint8 + if stage.locked { + flags |= 1 + } + if len(stage.password) > 0 { + flags |= 2 + } + bf.WriteUint8(flags) + ps.Uint8(bf, sid, false) + stage.RUnlock() } - bf.WriteUint16(uint16(joinable)) - bf.WriteBytes(resp.Data()) + bf.Seek(0, 0) + bf.WriteUint16(joinable) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index a9bc1421c..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,11 +136,12 @@ 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 7: - s.server.db.Exec(`UPDATE tower SET tr=$1, trp=trp+$2, block1=block1+$3 WHERE char_id=$4`, pkt.TR, pkt.TRP, pkt.Block1, s.charID) + case 1, 7: + // This might give too much TSP? No idea what the rate is supposed to be + s.server.db.Exec(`UPDATE tower SET tr=$1, trp=COALESCE(trp, 0)+$2, tsp=COALESCE(tsp, 0)+$3, block1=COALESCE(block1, 0)+$4 WHERE char_id=$5`, pkt.TR, pkt.TRP, pkt.Cost, pkt.Block1, s.charID) } doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -327,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), @@ -411,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 { @@ -441,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), @@ -454,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 cad64bbe4..19bd04123 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -3,11 +3,13 @@ package channelserver import ( "fmt" "net" + "strings" "sync" + "time" "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" - "erupe-ce/config" + _config "erupe-ce/config" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "erupe-ce/server/discordbot" @@ -55,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 @@ -72,65 +74,37 @@ type Server struct { name string raviente *Raviente + + questCacheData map[int][]byte + questCacheTime map[int]time.Time } type Raviente struct { sync.Mutex - - register *RavienteRegister - state *RavienteState - support *RavienteSupport + id uint16 + register []uint32 + state []uint32 + support []uint32 } -type RavienteRegister struct { - nextTime uint32 - startTime uint32 - postTime uint32 - killedTime uint32 - ravienteType uint32 - maxPlayers uint32 - carveQuest uint32 - register []uint32 -} - -type RavienteState struct { - stateData []uint32 -} - -type RavienteSupport struct { - supportData []uint32 -} - -// Set up the Raviente variables for the server -func NewRaviente() *Raviente { - ravienteRegister := &RavienteRegister{ - nextTime: 0, - startTime: 0, - killedTime: 0, - postTime: 0, - ravienteType: 0, - maxPlayers: 0, - carveQuest: 0, +func (s *Server) resetRaviente() { + for _, semaphore := range s.semaphore { + if strings.HasPrefix(semaphore.name, "hs_l0") { + return + } } - ravienteState := &RavienteState{} - ravienteSupport := &RavienteSupport{} - ravienteRegister.register = []uint32{0, 0, 0, 0, 0} - ravienteState.stateData = []uint32{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} - ravienteSupport.supportData = []uint32{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} - - raviente := &Raviente{ - register: ravienteRegister, - state: ravienteState, - support: ravienteSupport, - } - return raviente + s.logger.Debug("All Raviente Semaphores empty, resetting") + s.raviente.id = s.raviente.id + 1 + s.raviente.register = make([]uint32, 30) + s.raviente.state = make([]uint32, 30) + s.raviente.support = make([]uint32, 30) } -func (r *Raviente) GetRaviMultiplier(s *Server) float64 { - raviSema := getRaviSemaphore(s) +func (s *Server) GetRaviMultiplier() float64 { + raviSema := s.getRaviSemaphore() if raviSema != nil { var minPlayers int - if r.register.maxPlayers > 8 { + if s.raviente.register[9] > 8 { minPlayers = 24 } else { minPlayers = 4 @@ -143,6 +117,33 @@ func (r *Raviente) GetRaviMultiplier(s *Server) float64 { return 0 } +func (s *Server) UpdateRavi(semaID uint32, index uint8, value uint32, update bool) (uint32, uint32) { + var prev uint32 + var dest *[]uint32 + switch semaID { + case 0x40000: + switch index { + case 17, 28: // Ignore res and poison + break + default: + value = uint32(float64(value) * s.GetRaviMultiplier()) + } + dest = &s.raviente.state + case 0x50000: + dest = &s.raviente.support + case 0x60000: + dest = &s.raviente.register + default: + return 0, 0 + } + if update { + (*dest)[index] += value + } else { + (*dest)[index] = value + } + return prev, (*dest)[index] +} + // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server{ @@ -160,7 +161,14 @@ func NewServer(config *Config) *Server { semaphoreIndex: 7, discordBot: config.DiscordBot, name: config.Name, - raviente: NewRaviente(), + raviente: &Raviente{ + id: 1, + register: make([]uint32, 30), + state: make([]uint32, 30), + support: make([]uint32, 30), + }, + questCacheData: make(map[int][]byte), + questCacheTime: make(map[int]time.Time), } // Mezeporta @@ -184,7 +192,7 @@ func NewServer(config *Config) *Server { // MezFes s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0") - s.dict = getLangStrings(s) + s.i18n = getLangStrings(s) return s } @@ -203,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 @@ -314,7 +323,6 @@ func (s *Server) BroadcastChatMessage(message string) { msgBinChat.Build(bf) s.BroadcastMHF(&mhfpacket.MsgSysCastedBinary{ - CharID: 0xFFFFFFFF, MessageType: BinaryMessageTypeChat, RawDataPayload: bf.Data(), }, nil) @@ -329,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)) } @@ -346,7 +354,6 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u bf.WriteUint16(0) // Unk bf.WriteBytes(stage) s.WorldcastMHF(&mhfpacket.MsgSysCastedBinary{ - CharID: 0x00000000, BroadcastType: BroadcastTypeServer, MessageType: BinaryMessageTypeChat, RawDataPayload: bf.Data(), @@ -371,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() @@ -393,15 +420,16 @@ func (s *Server) NextSemaphoreID() uint32 { for { exists := false s.semaphoreIndex = s.semaphoreIndex + 1 - if s.semaphoreIndex == 0 { - s.semaphoreIndex = 7 // Skip reserved indexes + if s.semaphoreIndex > 0xFFFF { + s.semaphoreIndex = 1 } for _, semaphore := range s.semaphore { if semaphore.id == s.semaphoreIndex { exists = true + break } } - if exists == false { + if !exists { break } } diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index 418e1cecb..aae8706bb 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -1,110 +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["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"] = "誰も大討伐に参加していません" + 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["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!" + 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_semaphore.go b/server/channelserver/sys_semaphore.go index 369e481b6..78ff963b5 100644 --- a/server/channelserver/sys_semaphore.go +++ b/server/channelserver/sys_semaphore.go @@ -7,55 +7,35 @@ import ( "sync" ) -// Stage holds stage-specific information +// Semaphore holds Semaphore-specific information type Semaphore struct { sync.RWMutex - // Stage ID string - id_semaphore string + // Semaphore ID string + name string id uint32 // Map of session -> charID. - // These are clients that are CURRENTLY in the stage + // These are clients that are registered to the Semaphore clients map[*Session]uint32 - // Map of charID -> interface{}, only the key is used, value is always nil. - reservedClientSlots map[uint32]interface{} - // Max Players for Semaphore maxPlayers uint16 } -// NewStage creates a new stage with intialized values. +// NewSemaphore creates a new Semaphore with intialized values func NewSemaphore(s *Server, ID string, MaxPlayers uint16) *Semaphore { sema := &Semaphore{ - id_semaphore: ID, - id: s.NextSemaphoreID(), - clients: make(map[*Session]uint32), - reservedClientSlots: make(map[uint32]interface{}), - maxPlayers: MaxPlayers, + name: ID, + id: s.NextSemaphoreID(), + clients: make(map[*Session]uint32), + maxPlayers: MaxPlayers, } return sema } -func (s *Semaphore) BroadcastRavi(pkt mhfpacket.MHFPacket) { - // Broadcast the data. - for session := range s.clients { - - // Make the header - bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(pkt.Opcode())) - - // Build the packet onto the byteframe. - pkt.Build(bf, session.clientContext) - - // Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full. - session.QueueSendNonBlocking(bf.Data()) - } -} - -// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage. +// BroadcastMHF queues a MHFPacket to be sent to all sessions in the Semaphore func (s *Semaphore) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { // Broadcast the data. for session := range s.clients { diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 406ea4384..d7e55d621 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -36,7 +36,6 @@ type Session struct { objectIndex uint16 userEnteredStage bool // If the user has entered a stage before - stageID string stage *Stage reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. stagePass string // Temporary storage @@ -62,8 +61,9 @@ type Session struct { mailList []int // For Debuging - Name string - closed bool + Name string + closed bool + ackStart map[uint32]time.Time } // NewSession creates a new Session type. @@ -78,6 +78,7 @@ func NewSession(server *Server, conn net.Conn) *Session { lastPacket: time.Now(), sessionStart: TimeAdjusted().Unix(), stageMoveStack: stringstack.New(), + ackStart: make(map[uint32]time.Time), } s.SetObjectID() return s @@ -85,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. @@ -149,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) } } @@ -177,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) } } @@ -192,6 +193,10 @@ func (s *Session) handlePacketGroup(pktGroup []byte) { s.lastPacket = time.Now() bf := byteframe.NewByteFrameFromBytes(pktGroup) opcodeUint16 := bf.ReadUint16() + if len(bf.Data()) >= 6 { + s.ackStart[bf.ReadUint32()] = time.Now() + bf.Seek(2, io.SeekStart) + } opcode := network.PacketID(opcodeUint16) // This shouldn't be needed, but it's better to recover and let the connection die than to panic the server. @@ -248,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 !s.server.erupeConfig.DevModeOptions.LogInboundMessages { + } else if sender != "Server" && !s.server.erupeConfig.DebugOptions.LogInboundMessages { return } @@ -262,12 +263,24 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien if ignored(opcodePID) { return } - fmt.Printf("[%s] -> [%s]\n", sender, recipient) - fmt.Printf("Opcode: %s\n", opcodePID) - if len(data) <= s.server.erupeConfig.DevModeOptions.MaxHexdumpLength { - fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data)) + var ackHandle uint32 + if len(data) >= 6 { + ackHandle = binary.BigEndian.Uint32(data[2:6]) + } + if t, ok := s.ackStart[ackHandle]; ok { + fmt.Printf("[%s] -> [%s] (%fs)\n", sender, recipient, float64(time.Now().UnixNano()-t.UnixNano())/1000000000) } else { - fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data)) + fmt.Printf("[%s] -> [%s]\n", sender, recipient) + } + fmt.Printf("Opcode: %s\n", opcodePID) + 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)) + } + } else { + fmt.Printf("\n") } } @@ -296,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/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index ae8ddd149..dbfcbb7c3 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -47,6 +47,7 @@ type Stage struct { host *Session maxPlayers uint16 password string + locked bool } // NewStage creates a new stage with intialized values. diff --git a/server/channelserver/sys_time.go b/server/channelserver/sys_time.go index a41b18b2e..bae61a1c6 100644 --- a/server/channelserver/sys_time.go +++ b/server/channelserver/sys_time.go @@ -16,8 +16,11 @@ func TimeMidnight() time.Time { func TimeWeekStart() time.Time { midnight := TimeMidnight() - offset := (int(midnight.Weekday()) - 1) * -24 - return midnight.Add(time.Hour * time.Duration(offset)) + offset := int(midnight.Weekday()) - int(time.Monday) + if offset < 0 { + offset += 7 + } + return midnight.Add(-time.Duration(offset) * 24 * time.Hour) } func TimeWeekNext() time.Time { 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 eaa023c5b..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,27 +62,31 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } for channelIdx, ci := range si.Channels { - sid = (4096 + serverIdx*256) + (16 + channelIdx) - bf.WriteUint16(ci.Port) - bf.WriteUint16(16 + uint16(channelIdx)) + 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(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() } @@ -132,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)) } @@ -157,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 77ac6468d..715299f8f 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -9,6 +9,7 @@ import ( "fmt" "go.uber.org/zap" "strings" + "time" ) func (s *Session) makeSignResponse(uid uint32) []byte { @@ -71,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) @@ -143,46 +144,59 @@ 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) - mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent - alt := s.server.erupeConfig.DevModeOptions.MezFesAlt - if mezfes { - // We can just use the start timestamp as the event ID - bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) - // Start time - bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) - // End time - bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) - bf.WriteUint8(2) // Unk - bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesSoloTickets) - bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesGroupTickets) - bf.WriteUint8(8) // Stalls open - bf.WriteUint8(10) // Stall Map - bf.WriteUint8(3) // Pachinko - bf.WriteUint8(6) // Nyanrendo - bf.WriteUint8(9) // Point stall - if alt { - bf.WriteUint8(2) // Tokotoko Partnya - } else { - bf.WriteUint8(4) // Volpakkun Together - } - bf.WriteUint8(8) // Dokkan Battle Cats - bf.WriteUint8(5) // Goocoo Scoop - bf.WriteUint8(7) // Honey Panic - } else { - bf.WriteUint32(0) - bf.WriteUint32(0) - bf.WriteUint32(0) + tickets := []uint32{ + s.server.erupeConfig.GameplayOptions.MezFesSoloTickets, + s.server.erupeConfig.GameplayOptions.MezFesGroupTickets, + } + stalls := []uint8{ + 10, 3, 6, 9, 4, 8, 5, 7, + } + if s.server.erupeConfig.GameplayOptions.MezFesSwitchMinigame { + stalls[4] = 2 + } + + // We can just use the start timestamp as the event ID + bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) + // Start time + bf.WriteUint32(uint32(channelserver.TimeWeekNext().Add(-time.Duration(s.server.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix())) + // End time + bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) + bf.WriteUint8(uint8(len(tickets))) + for i := range tickets { + bf.WriteUint32(tickets[i]) + } + bf.WriteUint8(uint8(len(stalls))) + for i := range stalls { + bf.WriteUint8(stalls[i]) } return bf.Data() } 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/dbutils.go b/server/signv2server/dbutils.go index 3c8494c95..f5ea57846 100644 --- a/server/signv2server/dbutils.go +++ b/server/signv2server/dbutils.go @@ -10,36 +10,40 @@ import ( "golang.org/x/crypto/bcrypt" ) -func (s *Server) createNewUser(ctx context.Context, username string, password string) (int, error) { +func (s *Server) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) { // Create salted hash of user password passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { - return 0, err + return 0, 0, err } - var id int + var ( + id uint32 + rights uint32 + ) err = s.db.QueryRowContext( ctx, ` INSERT INTO users (username, password, return_expires) VALUES ($1, $2, $3) - RETURNING id + RETURNING id, rights `, username, string(passwordHash), time.Now().Add(time.Hour*24*30), - ).Scan(&id) - return id, err + ).Scan(&id, &rights) + return id, rights, err } -func (s *Server) createLoginToken(ctx context.Context, uid int) (string, error) { +func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) { loginToken := token.Generate(16) - _, err := s.db.ExecContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2)", uid, loginToken) + var tid uint32 + err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid) if err != nil { - return "", err + return 0, "", err } - return loginToken, nil + return tid, loginToken, nil } -func (s *Server) userIDFromToken(ctx context.Context, token string) (int, error) { - var userID int +func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) { + var userID uint32 err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID) if err == sql.ErrNoRows { return 0, fmt.Errorf("invalid login token") @@ -49,65 +53,47 @@ func (s *Server) userIDFromToken(ctx context.Context, token string) (int, error) return userID, nil } -func (s *Server) createCharacter(ctx context.Context, userID int) (int, error) { - var charID int - err := s.db.QueryRowContext(ctx, - "SELECT id FROM characters WHERE is_new_character = true AND user_id = $1", +func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character, error) { + var character Character + err := s.db.GetContext(ctx, &character, + "SELECT id, name, is_female, weapon_type, hrp, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1", userID, - ).Scan(&charID) + ) if err == sql.ErrNoRows { - err = s.db.QueryRowContext(ctx, ` + var count int + s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM characters WHERE user_id = $1", userID).Scan(&count) + if count >= 16 { + return character, fmt.Errorf("cannot have more than 16 characters") + } + err = s.db.GetContext(ctx, &character, ` INSERT INTO characters ( user_id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login ) VALUES ($1, false, true, '', '', 0, 0, 0, $2) - RETURNING id`, + RETURNING id, name, is_female, weapon_type, hrp, gr, last_login`, userID, uint32(time.Now().Unix()), - ).Scan(&charID) + ) } - return charID, err + return character, err } -func (s *Server) deleteCharacter(ctx context.Context, userID int, charID int) error { - tx, err := s.db.BeginTx(ctx, nil) +func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error { + var isNew bool + err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew) if err != nil { return err } - defer tx.Rollback() - - _, err = tx.ExecContext( - ctx, ` - DELETE FROM login_boost_state - WHERE char_id = $1`, - charID, - ) - if err != nil { - return err + if isNew { + _, err = s.db.Exec("DELETE FROM characters WHERE id = $1", charID) + } else { + _, err = s.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", charID) } - _, err = tx.ExecContext( - ctx, ` - DELETE FROM guild_characters - WHERE character_id = $1`, - charID, - ) - if err != nil { - return err - } - _, err = tx.ExecContext( - ctx, ` - DELETE FROM characters - WHERE user_id = $1 AND id = $2`, - userID, charID, - ) - if err != nil { - return err - } - return tx.Commit() + return err } -func (s *Server) getCharactersForUser(ctx context.Context, uid int) ([]Character, error) { - characters := make([]Character, 0) +func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) { + var characters []Character err := s.db.SelectContext( ctx, &characters, ` SELECT id, name, is_female, weapon_type, hrp, gr, last_login @@ -120,3 +106,30 @@ func (s *Server) getCharactersForUser(ctx context.Context, uid int) ([]Character } return characters, nil } + +func (s *Server) getReturnExpiry(uid uint32) time.Time { + var returnExpiry, lastLogin time.Time + s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid) + if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) { + returnExpiry = time.Now().Add(time.Hour * 24 * 30) + s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid) + } else { + err := s.db.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid) + if err != nil { + returnExpiry = time.Now() + s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid) + } + } + s.db.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid) + return returnExpiry +} + +func (s *Server) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) { + row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid) + result := make(map[string]interface{}) + err := row.MapScan(result) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index eeb7442de..5a0a6c8e7 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -4,7 +4,10 @@ import ( "database/sql" "encoding/json" "errors" + _config "erupe-ce/config" + "erupe-ce/server/channelserver" "net/http" + "strings" "time" "github.com/lib/pq" @@ -12,52 +15,99 @@ import ( "golang.org/x/crypto/bcrypt" ) -type LauncherMessage struct { - Message string `json:"message"` - Date int64 `json:"date"` - Link string `json:"link"` +const ( + NotificationDefault = iota + NotificationNew +) + +type LauncherResponse struct { + Banners []_config.SignV2Banner `json:"banners"` + Messages []_config.SignV2Message `json:"messages"` + Links []_config.SignV2Link `json:"links"` +} + +type User struct { + TokenID uint32 `json:"tokenId"` + Token string `json:"token"` + Rights uint32 `json:"rights"` } type Character struct { - ID int `json:"id"` + ID uint32 `json:"id"` Name string `json:"name"` IsFemale bool `json:"isFemale" db:"is_female"` - Weapon int `json:"weapon" db:"weapon_type"` - HR int `json:"hr" db:"hrp"` - GR int `json:"gr"` - LastLogin int64 `json:"lastLogin" db:"last_login"` + Weapon uint32 `json:"weapon" db:"weapon_type"` + HR uint32 `json:"hr" db:"hrp"` + GR uint32 `json:"gr"` + LastLogin int32 `json:"lastLogin" db:"last_login"` +} + +type MezFes struct { + ID uint32 `json:"id"` + Start uint32 `json:"start"` + End uint32 `json:"end"` + SoloTickets uint32 `json:"soloTickets"` + GroupTickets uint32 `json:"groupTickets"` + Stalls []uint32 `json:"stalls"` +} + +type AuthData struct { + CurrentTS uint32 `json:"currentTs"` + ExpiryTS uint32 `json:"expiryTs"` + EntranceCount uint32 `json:"entranceCount"` + Notices []string `json:"notices"` + User User `json:"user"` + Characters []Character `json:"characters"` + MezFes *MezFes `json:"mezFes"` + PatchServer string `json:"patchServer"` +} + +type ExportData struct { + Character map[string]interface{} `json:"character"` +} + +func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData { + resp := AuthData{ + CurrentTS: uint32(channelserver.TimeAdjusted().Unix()), + ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()), + EntranceCount: 1, + User: User{ + Rights: userRights, + TokenID: userTokenID, + Token: userToken, + }, + Characters: characters, + PatchServer: s.erupeConfig.SignV2.PatchServer, + Notices: []string{}, + } + if s.erupeConfig.DebugOptions.MaxLauncherHR { + for i := range resp.Characters { + resp.Characters[i].HR = 7 + } + } + stalls := []uint32{10, 3, 6, 9, 4, 8, 5, 7} + if s.erupeConfig.GameplayOptions.MezFesSwitchMinigame { + stalls[4] = 2 + } + resp.MezFes = &MezFes{ + 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, + Stalls: stalls, + } + if !s.erupeConfig.HideLoginNotice { + resp.Notices = append(resp.Notices, strings.Join(s.erupeConfig.LoginNotices[:], "")) + } + return resp } func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) { - var respData struct { - Important []LauncherMessage `json:"important"` - Normal []LauncherMessage `json:"normal"` - } - respData.Important = []LauncherMessage{ - { - Message: "Server Update 9 Released!", - Date: time.Date(2022, 8, 2, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/1003985850255818762", - }, - { - Message: "Eng 2.0 & Ravi Patch Released!", - Date: time.Date(2022, 5, 3, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/969305400795078656", - }, - { - Message: "Launcher Patch V1.0 Released!", - Date: time.Date(2022, 4, 24, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050", - }, - } - respData.Normal = []LauncherMessage{ - { - Message: "Join the community Discord for updates!", - Date: time.Date(2022, 4, 24, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - }, - } - w.WriteHeader(200) + var respData LauncherResponse + respData.Banners = s.erupeConfig.SignV2.Banners + respData.Messages = s.erupeConfig.SignV2.Messages + respData.Links = s.erupeConfig.SignV2.Links w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -71,17 +121,17 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } var ( - userID int - password string + userID uint32 + userRights uint32 + password string ) - err := s.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqData.Username).Scan(&userID, &password) + err := s.db.QueryRow("SELECT id, password, rights FROM users WHERE username = $1", reqData.Username).Scan(&userID, &password, &userRights) if err == sql.ErrNoRows { w.WriteHeader(400) - w.Write([]byte("Username does not exist")) + w.Write([]byte("username-error")) return } else if err != nil { s.logger.Warn("SQL query error", zap.Error(err)) @@ -90,27 +140,26 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { } if bcrypt.CompareHashAndPassword([]byte(password), []byte(reqData.Password)) != nil { w.WriteHeader(400) - w.Write([]byte("Your password is incorrect")) + w.Write([]byte("password-error")) return } - var respData struct { - Token string `json:"token"` - Characters []Character `json:"characters"` - } - respData.Token, err = s.createLoginToken(ctx, userID) + userTokenID, userToken, err := s.createLoginToken(ctx, userID) if err != nil { s.logger.Warn("Error registering login token", zap.Error(err)) w.WriteHeader(500) return } - respData.Characters, err = s.getCharactersForUser(ctx, userID) + characters, err := s.getCharactersForUser(ctx, userID) if err != nil { s.logger.Warn("Error getting characters from DB", zap.Error(err)) w.WriteHeader(500) return } - w.WriteHeader(200) + if characters == nil { + characters = []Character{} + } + respData := s.newAuthData(userID, userRights, userTokenID, userToken, characters) w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -124,16 +173,19 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) + return + } + if reqData.Username == "" || reqData.Password == "" { + w.WriteHeader(400) return } s.logger.Info("Creating account", zap.String("username", reqData.Username)) - userID, err := s.createNewUser(ctx, reqData.Username, reqData.Password) + userID, userRights, err := s.createNewUser(ctx, reqData.Username, reqData.Password) if err != nil { var pqErr *pq.Error if errors.As(err, &pqErr) && pqErr.Constraint == "users_username_key" { w.WriteHeader(400) - w.Write([]byte("User already exists")) + w.Write([]byte("username-exists-error")) return } s.logger.Error("Error checking user", zap.Error(err), zap.String("username", reqData.Username)) @@ -141,15 +193,14 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { return } - var respData struct { - Token string `json:"token"` - } - respData.Token, err = s.createLoginToken(ctx, userID) + userTokenID, userToken, err := s.createLoginToken(ctx, userID) if err != nil { s.logger.Error("Error registering login token", zap.Error(err)) w.WriteHeader(500) return } + respData := s.newAuthData(userID, userRights, userTokenID, userToken, []Character{}) + w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -161,37 +212,36 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } - var respData struct { - CharID int `json:"id"` - } userID, err := s.userIDFromToken(ctx, reqData.Token) if err != nil { w.WriteHeader(401) return } - respData.CharID, err = s.createCharacter(ctx, userID) + character, err := s.createCharacter(ctx, userID) if err != nil { s.logger.Error("Failed to create character", zap.Error(err), zap.String("token", reqData.Token)) w.WriteHeader(500) return } - json.NewEncoder(w).Encode(respData) + if s.erupeConfig.DebugOptions.MaxLauncherHR { + character.HR = 7 + } + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(character) } func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var reqData struct { Token string `json:"token"` - CharID int `json:"id"` + CharID uint32 `json:"charId"` } if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } userID, err := s.userIDFromToken(ctx, reqData.Token) @@ -200,9 +250,39 @@ func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { return } if err := s.deleteCharacter(ctx, userID, reqData.CharID); err != nil { - s.logger.Error("Failed to delete character", zap.Error(err), zap.String("token", reqData.Token), zap.Int("charID", reqData.CharID)) + s.logger.Error("Failed to delete character", zap.Error(err), zap.String("token", reqData.Token), zap.Uint32("charID", reqData.CharID)) w.WriteHeader(500) return } + w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(struct{}{}) } + +func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var reqData struct { + Token string `json:"token"` + CharID uint32 `json:"charId"` + } + if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { + s.logger.Error("JSON decode error", zap.Error(err)) + w.WriteHeader(400) + return + } + userID, err := s.userIDFromToken(ctx, reqData.Token) + if err != nil { + w.WriteHeader(401) + return + } + character, err := s.exportSave(ctx, userID, reqData.CharID) + if err != nil { + s.logger.Error("Failed to export save", zap.Error(err), zap.String("token", reqData.Token), zap.Uint32("charID", reqData.CharID)) + w.WriteHeader(500) + return + } + save := ExportData{ + Character: character, + } + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(save) +} diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index c6db00b09..fedbabba2 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -51,6 +51,7 @@ func (s *Server) Start() error { r.HandleFunc("/register", s.Register) r.HandleFunc("/character/create", s.CreateCharacter) r.HandleFunc("/character/delete", s.DeleteCharacter) + r.HandleFunc("/character/export", s.ExportSave) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port)