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/README.md b/README.md index ede542a20..034e2a4ea 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 @@ -32,116 +35,4 @@ If you want to modify or compile Erupe yourself, please read on. ## 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/bundled-schema/DistributionDemo.sql b/bundled-schema/DistributionDemo.sql new file mode 100644 index 000000000..d5da8688e --- /dev/null +++ b/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/FPointItems.sql b/bundled-schema/FPointItems.sql index 918d81e54..7012e6f25 100644 --- a/bundled-schema/FPointItems.sql +++ b/bundled-schema/FPointItems.sql @@ -1,391 +1,391 @@ 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); +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/OtherShops.sql b/bundled-schema/OtherShops.sql index d25e453b0..3c88bb896 100644 --- a/bundled-schema/OtherShops.sql +++ b/bundled-schema/OtherShops.sql @@ -5,6 +5,42 @@ INSERT INTO public.shop_items 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); diff --git a/bundled-schema/ScenarioDefaults.sql b/bundled-schema/ScenarioDefaults.sql new file mode 100644 index 000000000..ec7b3d99e --- /dev/null +++ b/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/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..9f6b646ae 100644 --- a/common/stringstack/stringstack.go +++ b/common/stringstack/stringstack.go @@ -12,7 +12,7 @@ type StringStack struct { // New creates a new instance of StringStack func New() *StringStack { - return &StringStack{Locked: false} + return &StringStack{} } // Set sets up a new StringStack @@ -41,11 +41,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 5ed2ab9fd..5d66b9e33 100644 --- a/config.json +++ b/config.json @@ -12,26 +12,29 @@ "ScreenshotAPIURL": "", "DeleteOnSaveCorruption": false, "ClientMode": "ZZ", + "QuestCacheExpiry": 300, + "ProxyPort": 0, + "CommandPrefix": "!", "DevMode": true, "DevModeOptions": { "AutoCreateAccount": true, "CleanDB": false, "MaxLauncherHR": false, - "LogInboundMessages": true, - "LogOutboundMessages": true, + "LogInboundMessages": false, + "LogOutboundMessages": false, + "LogMessageData": false, "MaxHexdumpLength": 256, "DivaEvent": 0, "FestaEvent": -1, "TournamentEvent": 0, - "MezFesEvent": true, - "MezFesAlt": false, "DisableTokenCheck": false, "QuestDebugTools": false, "EarthStatusOverride": 0, "EarthIDOverride": 0, - "EarthMonsterOverride": 0, + "EarthMonsterOverride": [0, 0, 0, 0], "SaveDumps": { "Enabled": true, + "RawEnabled": false, "OutputDir": "save-backups" } }, @@ -40,10 +43,12 @@ "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, @@ -54,7 +59,7 @@ "BerserkRavienteMaxPlayers": 32, "ExtremeRavienteMaxPlayers": 32, "SmallBerserkRavienteMaxPlayers": 8, - "GUrgentRate": 10, + "GUrgentRate": 0.10, "GCPMultiplier": 1.00, "GRPMultiplier": 1.00, "GSRPMultiplier": 1.00, @@ -62,6 +67,8 @@ "MaterialMultiplier": 1.00, "ExtraCarves": 0, "DisableHunterNavi": false, + "MezFesDuration": 172800, + "MezFesSwitchMinigame": false, "EnableKaijiEvent": false, "EnableHiganjimaEvent": false, "EnableNierEvent": false, @@ -75,32 +82,44 @@ }, "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" } ], @@ -130,7 +149,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..153cfedb3 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,6 +79,9 @@ type Config struct { DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion ClientMode string RealClientMode Mode + QuestCacheExpiry int // Number of seconds to keep quest data cached + ProxyPort uint16 // Forces the game to connect to a channel server proxy + CommandPrefix string // The prefix for commands DevMode bool DevModeOptions DevModeOptions @@ -100,46 +103,49 @@ type DevModeOptions struct { MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. LogInboundMessages bool // Log all messages sent to the server LogOutboundMessages bool // Log all messages sent to the clients + LogMessageData bool // Log all bytes transferred as a hexdump MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled DivaEvent int // Diva Defense event status FestaEvent int // Hunter's Festa event status TournamentEvent int // VS Tournament event status - 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 + EarthMonsterOverride []int32 SaveDumps SaveDumpOptions } type SaveDumpOptions struct { - Enabled bool - OutputDir string + Enabled bool + RawEnabled bool + OutputDir string } // 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 + MezfesSoloTickets uint32 // Number of solo tickets given weekly + MezfesGroupTickets uint32 // Number of group tickets given weekly + LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive RegularRavienteMaxPlayers uint8 ViolentRavienteMaxPlayers uint8 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 GRPMultiplier float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion GSRPMultiplier float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion @@ -147,6 +153,8 @@ type GameplayOptions struct { MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses DisableHunterNavi bool // Disables the Hunter Navi + 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 @@ -163,9 +171,10 @@ type Discord struct { // 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 +200,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/go.mod b/go.mod index 93c8f83ad..410eb88b0 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.15.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.14.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..1c6fbeb53 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.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= 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.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.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..c56d90b0f 100644 --- a/main.go +++ b/main.go @@ -22,11 +22,13 @@ import ( ) // Temporary DB auto clean on startup for quick development & testing. -func cleanDB(db *sqlx.DB) { +func cleanDB(db *sqlx.DB, config *_config.Config) { _ = db.MustExec("DELETE FROM guild_characters") _ = db.MustExec("DELETE FROM guilds") _ = db.MustExec("DELETE FROM characters") - _ = db.MustExec("DELETE FROM sign_sessions") + if config.ProxyPort == 0 { + _ = db.MustExec("DELETE FROM sign_sessions") + } _ = db.MustExec("DELETE FROM users") } @@ -124,13 +126,16 @@ func main() { logger.Info("Database: Started successfully") // Clear stale data - _ = db.MustExec("DELETE FROM sign_sessions") + if config.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 { logger.Info("Database: Started clearing...") - cleanDB(db) + cleanDB(db, config) logger.Info("Database: Finished clearing") } 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_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 2f970f938..71cae7adf 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guacot.go +++ b/network/mhfpacket/msg_mhf_enumerate_guacot.go @@ -10,8 +10,7 @@ import ( // MsgMhfEnumerateGuacot represents the MSG_MHF_ENUMERATE_GUACOT type MsgMhfEnumerateGuacot struct { AckHandle uint32 - Unk0 uint32 // Hardcoded 0 in binary - Unk1 uint16 // Hardcoded 0 in binary + Unk0 uint32 } // Opcode returns the ID associated with this packet type. @@ -23,7 +22,7 @@ func (m *MsgMhfEnumerateGuacot) Opcode() network.PacketID { func (m *MsgMhfEnumerateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint16() + 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 956c4a399..46afb1a2e 100644 --- a/network/mhfpacket/msg_mhf_register_event.go +++ b/network/mhfpacket/msg_mhf_register_event.go @@ -12,8 +12,7 @@ type MsgMhfRegisterEvent struct { Unk0 uint16 WorldID uint16 LandID uint16 - Unk3 uint8 - Unk4 uint8 + Unk1 bool } // Opcode returns the ID associated with this packet type. @@ -27,8 +26,8 @@ func (m *MsgMhfRegisterEvent) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clie m.Unk0 = bf.ReadUint16() m.WorldID = bf.ReadUint16() m.LandID = bf.ReadUint16() - m.Unk3 = bf.ReadUint8() - m.Unk4 = bf.ReadUint8() + 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 52178279b..20ebcdba0 100644 --- a/network/mhfpacket/msg_mhf_release_event.go +++ b/network/mhfpacket/msg_mhf_release_event.go @@ -12,7 +12,6 @@ import ( type MsgMhfReleaseEvent struct { AckHandle uint32 RaviID uint32 - Unk1 uint32 } // Opcode returns the ID associated with this packet type. @@ -24,7 +23,7 @@ func (m *MsgMhfReleaseEvent) Opcode() network.PacketID { func (m *MsgMhfReleaseEvent) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.RaviID = bf.ReadUint32() - m.Unk1 = 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 433854ae3..729c84547 100644 --- a/network/mhfpacket/msg_mhf_update_guacot.go +++ b/network/mhfpacket/msg_mhf_update_guacot.go @@ -9,7 +9,7 @@ import ( type Goocoo struct { Index uint32 - Data1 []uint16 + Data1 []int16 Data2 []uint32 Name []byte } @@ -30,12 +30,12 @@ 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() - _ = bf.ReadUint16() // Zero + bf.ReadUint16() // Zeroed var temp Goocoo for i := 0; i < int(m.EntryCount); i++ { temp.Index = bf.ReadUint32() for j := 0; j < 22; j++ { - temp.Data1 = append(temp.Data1, bf.ReadUint16()) + temp.Data1 = append(temp.Data1, bf.ReadInt16()) } for j := 0; j < 2; j++ { temp.Data2 = append(temp.Data2, bf.ReadUint32()) 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 7e1ac5950..edf4eafb4 100644 --- a/network/mhfpacket/msg_sys_load_register.go +++ b/network/mhfpacket/msg_sys_load_register.go @@ -24,8 +24,8 @@ func (m *MsgSysLoadRegister) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien m.AckHandle = bf.ReadUint32() m.RegisterID = bf.ReadUint32() m.Values = bf.ReadUint8() - _ = bf.ReadUint8() - _ = bf.ReadUint16() + 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/patch-schema/03-event_quests.sql b/patch-schema/03-event_quests.sql index 94aac0c65..1374a3d08 100644 --- a/patch-schema/03-event_quests.sql +++ b/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/07-scenarios-counter.sql b/patch-schema/07-scenarios-counter.sql new file mode 100644 index 000000000..3ea2c65b2 --- /dev/null +++ b/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/patch-schema/08-kill-counts.sql b/patch-schema/08-kill-counts.sql new file mode 100644 index 000000000..1c170cedd --- /dev/null +++ b/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/patch-schema/09-fix-guild-treasure.sql b/patch-schema/09-fix-guild-treasure.sql new file mode 100644 index 000000000..1c022292f --- /dev/null +++ b/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/patch-schema/10-rework-distributions.sql b/patch-schema/10-rework-distributions.sql new file mode 100644 index 000000000..7945de343 --- /dev/null +++ b/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/patch-schema/11-event-quest-flags.sql b/patch-schema/11-event-quest-flags.sql new file mode 100644 index 000000000..5f88d732d --- /dev/null +++ b/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/patch-schema/12-event_quest_cycling.sql b/patch-schema/12-event_quest_cycling.sql new file mode 100644 index 000000000..8760bdab4 --- /dev/null +++ b/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/patch-schema/13-festa-trial-votes.sql b/patch-schema/13-festa-trial-votes.sql new file mode 100644 index 000000000..d9e3d0290 --- /dev/null +++ b/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/patch-schema/14-fix-fpoint-trades.sql b/patch-schema/14-fix-fpoint-trades.sql new file mode 100644 index 000000000..c4e698655 --- /dev/null +++ b/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/patch-schema/15-reset-goocoos.sql b/patch-schema/15-reset-goocoos.sql new file mode 100644 index 000000000..ca4d3fa11 --- /dev/null +++ b/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/server/channelserver/handlers.go b/server/channelserver/handlers.go index e502bfa0f..334cc0c0d 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "erupe-ce/common/mhfcourse" "erupe-ce/common/mhfitem" + "erupe-ce/common/mhfmon" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" _config "erupe-ce/config" @@ -17,8 +18,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 @@ -129,7 +131,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { if !s.server.erupeConfig.DevModeOptions.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)) @@ -232,7 +234,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 @@ -305,9 +307,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) {} @@ -354,143 +367,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() @@ -511,30 +498,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())) } } } @@ -547,47 +534,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()) @@ -599,7 +620,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)) } @@ -628,12 +649,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 handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { @@ -693,16 +869,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 } @@ -785,39 +961,31 @@ 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()) } @@ -830,7 +998,7 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() bf.WriteUint32(goocoo.Index) for i := range goocoo.Data1 { - bf.WriteUint16(goocoo.Data1[i]) + bf.WriteInt16(goocoo.Data1[i]) } for i := range goocoo.Data2 { bf.WriteUint32(goocoo.Data2[i]) @@ -844,727 +1012,54 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) { 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) { @@ -1599,10 +1094,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) @@ -1642,21 +1137,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.WriteUint32(uint32(TimeWeekStart().Unix())) // Start + bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride) bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride) - bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride) - bf.WriteInt32(0) - bf.WriteInt32(0) - bf.WriteInt32(0) + for i, m := range s.server.erupeConfig.DevModeOptions.EarthMonsterOverride { + if _config.ErupeConfig.RealClientMode <= _config.G9 { + if i == 3 { + break + } + } + if i == 4 { + break + } + bf.WriteInt32(m) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -1856,7 +1361,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..9c4b0b732 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -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 30a377b82..71a8a6f07 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -6,6 +6,7 @@ import ( "erupe-ce/common/mhfcourse" "erupe-ce/common/token" "erupe-ce/config" + "erupe-ce/network" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "fmt" @@ -74,7 +75,7 @@ func sendServerChatMessage(s *Session, message string) { msgBinChat.Build(bf) castedBin := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, + CharID: 0, MessageType: BinaryMessageTypeChat, RawDataPayload: bf.Data(), } @@ -83,7 +84,7 @@ 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["PSN"].Prefix: if commands["PSN"].Enabled { @@ -125,7 +126,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 +161,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 s.server.erupeConfig.RealClientMode < _config.G10 { + sendServerChatMessage(s, s.server.dict["commandKqfVersion"]) + } else { + 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)) + } } } } @@ -297,8 +302,8 @@ func parseChatCommand(s *Session, command string) { case commands["Teleport"].Prefix: if commands["Teleport"].Enabled { 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) @@ -317,6 +322,16 @@ func parseChatCommand(s *Session, command string) { } else { sendDisabledCommandMessage(s, commands["Teleport"]) } + case commands["Help"].Prefix: + if commands["Help"].Enabled { + for _, command := range commands { + if command.Enabled { + sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", s.server.erupeConfig.CommandPrefix, command.Prefix, command.Description)) + } + } + } else { + sendDisabledCommandMessage(s, commands["Help"]) + } } } @@ -390,7 +405,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { 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 } diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index bdbd2f744..9d90cc898 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,22 @@ 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 + } + if _config.ErupeConfig.RealClientMode == _config.G5 { + pointers[lBookshelfData] = 5548 + } else if _config.ErupeConfig.RealClientMode <= _config.GG { + pointers[lBookshelfData] = 4520 } return pointers } @@ -176,8 +195,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 +212,25 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.Gender = false } if !save.IsNewCharacter { - if _config.ErupeConfig.RealClientMode >= _config.G10 { + if _config.ErupeConfig.RealClientMode >= _config.F4 { 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..805fa59f5 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -2,6 +2,7 @@ package channelserver import ( "erupe-ce/common/stringsupport" + _config "erupe-ce/config" "fmt" "io" "os" @@ -44,6 +45,9 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } + if s.server.erupeConfig.DevModeOptions.SaveDumps.RawEnabled { + dumpSaveData(s, saveData, "raw-savedata") + } s.logger.Info("Updating save with blob") characterSaveData.decompSave = saveData } @@ -54,7 +58,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,162 +76,39 @@ 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) { @@ -301,7 +182,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 { 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..42b70f064 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -72,10 +72,10 @@ 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.RealClientMode >= _config.Z2 { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36)) + } else { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32)) } return } @@ -84,7 +84,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { 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 d01f92a3d..d39b629d9 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -199,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 95cc14e0d..0ea67fee2 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -96,7 +96,7 @@ 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_prizes_accepted") - s.server.db.Exec("UPDATE guild_characters SET souls=0") + s.server.db.Exec("UPDATE guild_characters SET souls=0, trial_vote=NULL") } func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { @@ -141,13 +141,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 FestivalColour `db:"monopoly"` Unk uint16 } @@ -205,7 +205,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_colour) + 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_colour) + END, CAST('none' AS public.festival_colour)) 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 { @@ -221,12 +233,12 @@ 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.WriteInt16(int16(FestivalColourCodes[trial.Monopoly])) 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}, @@ -254,6 +266,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) @@ -261,11 +274,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 @@ -294,17 +309,17 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { ps.Uint8(bf, "", true) // Guild Name } - // 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) + // Final bonus rates + bf.WriteUint32(1) // 5000-Infinity? + bf.WriteUint32(5000) // 5000+ souls + bf.WriteUint32(2000) // 2000-4999 souls + bf.WriteUint32(1000) // 1000-1999 souls + 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 ps.Uint16(bf, "", false) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) @@ -391,6 +406,7 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { 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)) } diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 5fdd2dac1..ad5221281 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" @@ -26,14 +25,14 @@ type FestivalColour string const ( FestivalColourNone FestivalColour = "none" - FestivalColourRed FestivalColour = "red" FestivalColourBlue FestivalColour = "blue" + FestivalColourRed FestivalColour = "red" ) -var FestivalColourCodes = map[FestivalColour]uint8{ - FestivalColourBlue: 0x00, - FestivalColourRed: 0x01, - FestivalColourNone: 0xFF, +var FestivalColourCodes = map[FestivalColour]int8{ + FestivalColourNone: -1, + FestivalColourBlue: 0, + FestivalColourRed: 1, } type GuildApplicationType string @@ -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(FestivalColourCodes[guild.FestivalColour]) 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 @@ -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 { @@ -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_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 231e6a72d..dc764a382 100644 --- a/server/channelserver/handlers_house.go +++ b/server/channelserver/handlers_house.go @@ -121,7 +121,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) @@ -215,10 +217,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)) @@ -240,8 +244,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) { @@ -252,71 +256,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 { @@ -355,12 +353,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_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..98226c777 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -62,25 +62,30 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { } } -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,6 +108,11 @@ 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 @@ -139,14 +149,17 @@ 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 { @@ -155,8 +168,8 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) { 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 +190,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 +236,59 @@ 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 { + 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)) { + break + } + } + + data, err := makeEventQuest(s, rows) + if err != nil { 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 { + continue } else { + totalCount++ if totalCount > pkt.Offset && len(bf.Data()) < 60000 { returnedCount++ bf.WriteBytes(data) @@ -220,6 +297,9 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { } } } + + rows.Close() + tx.Commit() } type tuneValue struct { @@ -565,7 +645,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { 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}) diff --git a/server/channelserver/handlers_register.go b/server/channelserver/handlers_register.go index 6a74358aa..3d47c3633 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -10,7 +10,7 @@ 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.Unk3 > 0 && s.server.getRaviSemaphore() == nil { + if pkt.Unk1 && s.server.getRaviSemaphore() == nil { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) return } 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..a9de55b28 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,14 @@ 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.Push(s.stage.id) s.stageMoveStack.Lock() } - s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) if s.reservationStage != nil { s.reservationStage = nil } @@ -177,8 +173,8 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { // 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 { @@ -195,7 +191,7 @@ 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 + // Set a new move stack from the given stage ID if !s.stageMoveStack.Locked { s.stageMoveStack.Set(pkt.StageID) } @@ -207,9 +203,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 +218,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 +243,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 +372,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..4ce0bcc9f 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -126,8 +126,9 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { 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) 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)) } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 5a4b29fe9..70f52e461 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -5,10 +5,11 @@ import ( "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" @@ -73,6 +74,9 @@ type Server struct { name string raviente *Raviente + + questCacheData map[int][]byte + questCacheTime map[int]time.Time } type Raviente struct { @@ -115,6 +119,7 @@ func (s *Server) GetRaviMultiplier() float64 { 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 { @@ -123,28 +128,20 @@ func (s *Server) UpdateRavi(semaID uint32, index uint8, value uint32, update boo default: value = uint32(float64(value) * s.GetRaviMultiplier()) } - prev = s.raviente.state[index] - if prev != 0 && !update { - return prev, prev - } - s.raviente.state[index] += value - return prev, s.raviente.state[index] + dest = &s.raviente.state case 0x50000: - prev = s.raviente.support[index] - if prev != 0 && !update { - return prev, prev - } - s.raviente.support[index] += value - return prev, s.raviente.support[index] + dest = &s.raviente.support case 0x60000: - prev = s.raviente.register[index] - if prev != 0 && !update { - return prev, prev - } - s.raviente.register[index] += value - return prev, s.raviente.register[index] + dest = &s.raviente.register + default: + return 0, 0 } - return 0, 0 + if update { + (*dest)[index] += value + } else { + (*dest)[index] = value + } + return prev, (*dest)[index] } // NewServer creates a new Server type. @@ -170,6 +167,8 @@ func NewServer(config *Config) *Server { state: make([]uint32, 30), support: make([]uint32, 30), }, + questCacheData: make(map[int][]byte), + questCacheTime: make(map[int]time.Time), } // Mezeporta @@ -323,7 +322,6 @@ func (s *Server) BroadcastChatMessage(message string) { msgBinChat.Build(bf) s.BroadcastMHF(&mhfpacket.MsgSysCastedBinary{ - CharID: 0xFFFFFFFF, MessageType: BinaryMessageTypeChat, RawDataPayload: bf.Data(), }, nil) @@ -355,7 +353,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(), diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index dbd48cfb8..df74e1212 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -12,6 +12,7 @@ func getLangStrings(s *Server) map[string]string { strings["commandKqfGet"] = "現在のキークエストフラグ:%x" strings["commandKqfSetError"] = "キークエコマンドエラー 例:%s set xxxxxxxxxxxxxxxx" strings["commandKqfSetSuccess"] = "キークエストのフラグが更新されました。ワールド/ランドを移動してください" + strings["commandKqfVersion"] = "This command is disabled prior to MHFG10" strings["commandRightsError"] = "コース更新コマンドエラー 例:%s x" strings["commandRightsSuccess"] = "コース情報を更新しました:%d" strings["commandCourseError"] = "コース確認コマンドエラー 例:%s " @@ -64,6 +65,7 @@ func getLangStrings(s *Server) map[string]string { strings["commandKqfGet"] = "KQF: %x" strings["commandKqfSetError"] = "Error in command. Format: %s set xxxxxxxxxxxxxxxx" strings["commandKqfSetSuccess"] = "KQF set, please switch Land/World" + strings["commandKqfVersion"] = "This command is disabled prior to MHFG10" strings["commandRightsError"] = "Error in command. Format: %s x" strings["commandRightsSuccess"] = "Set rights integer: %d" strings["commandCourseError"] = "Error in command. Format: %s " diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 406ea4384..5034f38c2 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 @@ -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. @@ -254,7 +259,7 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien if sender == "Server" && !s.server.erupeConfig.DevModeOptions.LogOutboundMessages { return - } else if !s.server.erupeConfig.DevModeOptions.LogInboundMessages { + } else if sender != "Server" && !s.server.erupeConfig.DevModeOptions.LogInboundMessages { return } @@ -262,12 +267,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.DevModeOptions.LogMessageData { + if len(data) <= s.server.erupeConfig.DevModeOptions.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") } } 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/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index eaa023c5b..4b478fa24 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -69,7 +69,11 @@ 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) + if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 { + bf.WriteUint16(_config.ErupeConfig.ProxyPort) + } else { + bf.WriteUint16(ci.Port) + } bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(ci.MaxPlayers) var currentPlayers uint16 diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 77ac6468d..34cc371e2 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 { @@ -154,35 +155,30 @@ func (s *Session) makeSignResponse(uid uint32) []byte { 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/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..b6cc03515 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.DevModeOptions.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.DevModeOptions.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)