diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 2f56c0f75..c33872e81 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -27,7 +27,7 @@ jobs:
./www/
./savedata/
./bin/
- ./RoadShopItems.csv
+ ./bundled-schema/
- name: Build Windows-amd64
run: env GOOS=windows GOARCH=amd64 go build -v
@@ -42,4 +42,4 @@ jobs:
./www/
./savedata/
./bin/
- ./RoadShopItems.csv
+ ./bundled-schema/
diff --git a/README.md b/README.md
index 916c6db7a..a157aabe3 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,18 @@
# Erupe Community Edition
+## Setup
+- If you are only looking to install Erupe, please use [a pre-compiled binary](https://github.com/ZeruLight/Erupe/releases/latest).
+- If you want to modify or compile Erupe yourself please read on.
+### Requirements
+- [Go](https://go.dev/dl/)
+- [PostgreSQL](https://www.postgresql.org/download/)
+### Installation
+1. Bring up a fresh database by using the [backup file attached with the latest release](https://github.com/ZeruLight/Erupe/releases/latest/download/Erupe.sql).
+2. Run each script under [patch-schema](./patch-schema) as they introduce newer schema.
+3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup.
+4. Run `go build` or `go run .` to compile Erupe.
+### Note
+- You will need to acquire and install the client files and quest binaries separately.
+# Resources
+[Community FAQ Pastebin](https://pastebin.com/QqAwZSTC)
-This is a community upload of a community project. The amount of people who worked on it is innumerous, and hard to keep track of. Credits to Andoryuuta, Fist's Team, the French Team, Mai's Team and many others. No matter the relations, these files will remain public and open source, free for all to use and modify.
-
-[A pastebin with various links, tips, and FAQ](https://pastebin.com/QqAwZSTC)
-
-[An upload for the quest and scenario files exists here](https://github.com/xl3lackout/MHFZ-Quest-Files)
-(Over 300k+ files)
+[Quests and Scenario Binary Files](https://github.com/xl3lackout/MHFZ-Quest-Files)
diff --git a/RoadShopItems.csv b/RoadShopItems.csv
deleted file mode 100644
index 23e1b2102..000000000
--- a/RoadShopItems.csv
+++ /dev/null
@@ -1,911 +0,0 @@
-10,6,1,2146,25,1,0,0,1,1,0,1,0,0
-10,6,2,2147,25,1,0,0,1,1,0,1,0,0
-10,6,3,2148,25,1,0,0,1,1,0,1,0,0
-10,6,4,2149,25,1,0,0,1,1,0,1,0,0
-10,6,5,2150,25,1,0,0,1,1,0,1,0,0
-10,6,6,2151,25,1,0,0,1,1,0,1,0,0
-10,6,7,2152,25,1,0,0,1,1,0,1,0,0
-10,6,8,2153,25,1,0,0,1,1,0,1,0,0
-10,6,9,2154,25,1,0,0,1,1,0,1,0,0
-10,6,10,2155,25,1,0,0,1,1,0,1,0,0
-10,6,11,4398,25,1,0,0,1,1,0,1,0,0
-10,6,12,12460,25,1,0,0,1,1,0,1,0,0
-10,6,13,12461,25,1,0,0,1,1,0,1,0,0
-10,6,14,12462,25,1,0,0,1,1,0,1,0,0
-10,6,15,12463,25,1,0,0,1,1,0,1,0,0
-10,6,16,12464,25,1,0,0,1,1,0,1,0,0
-10,6,17,12465,25,1,0,0,1,1,0,1,0,0
-10,6,18,12466,25,1,0,0,1,1,0,1,0,0
-10,6,19,12467,25,1,0,0,1,1,0,1,0,0
-10,6,20,12468,25,1,0,0,1,1,0,1,0,0
-10,6,21,12469,25,1,0,0,1,1,0,1,0,0
-10,6,22,15109,1000,1,0,0,1,1,0,1,0,0
-10,6,23,15110,1000,1,0,0,1,1,0,1,0,0
-10,6,24,2158,200,100,0,0,1,1,0,1,0,0
-10,6,25,12306,2,1,0,0,1,1,0,1,80,0
-10,6,26,12306,20000,10000,0,0,1,1,0,1,80,0
-10,4,27,11664,20000,1,0,0,1,1,0,1,0,0
-10,4,28,11665,20000,1,0,0,1,1,0,1,0,0
-10,4,29,11666,20000,1,0,0,1,1,0,1,0,0
-10,4,30,11667,20000,1,0,0,1,1,0,1,0,0
-10,4,31,11668,20000,1,0,0,1,1,0,1,0,0
-10,4,32,11669,20000,1,0,0,1,1,0,1,0,0
-10,4,33,11670,20000,1,0,0,1,1,0,1,0,0
-10,4,34,11671,20000,1,0,0,1,1,0,1,0,0
-10,4,35,11672,20000,1,0,0,1,1,0,1,0,0
-10,4,36,11673,20000,1,0,0,1,1,0,1,0,0
-10,4,37,11674,20000,1,0,0,1,1,0,1,0,0
-10,4,38,11675,20000,1,0,0,1,1,0,1,0,0
-10,4,39,11676,20000,1,0,0,1,1,0,1,0,0
-10,4,40,11677,20000,1,0,0,1,1,0,1,0,0
-10,4,41,11678,20000,1,0,0,1,1,0,1,0,0
-10,4,42,11679,20000,1,0,0,1,1,0,1,0,0
-10,4,43,11680,20000,1,0,0,1,1,0,1,0,0
-10,4,44,11681,20000,1,0,0,1,1,0,1,0,0
-10,4,45,11682,20000,1,0,0,1,1,0,1,0,0
-10,4,46,11683,20000,1,0,0,1,1,0,1,0,0
-10,4,47,11684,20000,1,0,0,1,1,0,1,0,0
-10,4,48,11685,20000,1,0,0,1,1,0,1,0,0
-10,4,49,11686,20000,1,0,0,1,1,0,1,0,0
-10,4,50,11687,20000,1,0,0,1,1,0,1,0,0
-10,4,51,11688,20000,1,0,0,1,1,0,1,0,0
-10,4,52,11689,20000,1,0,0,1,1,0,1,0,0
-10,4,53,11690,20000,1,0,0,1,1,0,1,0,0
-10,4,54,11691,20000,1,0,0,1,1,0,1,0,0
-10,4,55,11692,20000,1,0,0,1,1,0,1,0,0
-10,4,56,11693,20000,1,0,0,1,1,0,1,0,0
-10,4,57,11694,20000,1,0,0,1,1,0,1,0,0
-10,4,58,11695,20000,1,0,0,1,1,0,1,0,0
-10,4,59,11696,20000,1,0,0,1,1,0,1,0,0
-10,4,60,11697,20000,1,0,0,1,1,0,1,0,0
-10,4,61,12893,20000,1,0,0,1,1,0,1,0,0
-10,4,62,12894,20000,1,0,0,1,1,0,1,0,0
-10,4,63,12895,20000,1,0,0,1,1,0,1,0,0
-10,4,64,12896,20000,1,0,0,1,1,0,1,0,0
-10,4,65,12897,20000,1,0,0,1,1,0,1,0,0
-10,4,66,12898,20000,1,0,0,1,1,0,1,0,0
-10,4,67,12899,20000,1,0,0,1,1,0,1,0,0
-10,4,68,14337,20000,1,0,0,1,1,0,1,0,0
-10,4,69,14338,20000,1,0,0,1,1,0,1,0,0
-10,4,70,14339,20000,1,0,0,1,1,0,1,0,0
-10,4,71,14340,20000,1,0,0,1,1,0,1,0,0
-10,4,72,14341,20000,1,0,0,1,1,0,1,0,0
-10,4,73,14342,20000,1,0,0,1,1,0,1,0,0
-10,4,74,14343,20000,1,0,0,1,1,0,1,0,0
-10,4,75,14344,20000,1,0,0,1,1,0,1,0,0
-10,4,76,14345,20000,1,0,0,1,1,0,1,0,0
-10,4,77,9254,10000,1,0,0,1,1,0,1,0,0
-10,4,78,9255,10000,1,0,0,1,1,0,1,0,0
-10,4,79,9256,10000,1,0,0,1,1,0,1,0,0
-10,4,80,9257,10000,1,0,0,1,1,0,1,0,0
-10,4,81,9258,10000,1,0,0,1,1,0,1,0,0
-10,4,82,9259,10000,1,0,0,1,1,0,1,0,0
-10,4,83,9260,10000,1,0,0,1,1,0,1,0,0
-10,4,84,9261,10000,1,0,0,1,1,0,1,0,0
-10,4,85,9262,10000,1,0,0,1,1,0,1,0,0
-10,4,86,9263,10000,1,0,0,1,1,0,1,0,0
-10,4,87,9264,10000,1,0,0,1,1,0,1,0,0
-10,4,88,9265,10000,1,0,0,1,1,0,1,0,0
-10,4,89,9266,10000,1,0,0,1,1,0,1,0,0
-10,4,90,9267,10000,1,0,0,1,1,0,1,0,0
-10,4,91,9268,10000,1,0,0,1,1,0,1,0,0
-10,4,92,9269,10000,1,0,0,1,1,0,1,0,0
-10,4,93,9270,10000,1,0,0,1,1,0,1,0,0
-10,4,94,9271,10000,1,0,0,1,1,0,1,0,0
-10,4,95,9272,10000,1,0,0,1,1,0,1,0,0
-10,4,96,9273,10000,1,0,0,1,1,0,1,0,0
-10,4,97,9274,10000,1,0,0,1,1,0,1,0,0
-10,4,98,9275,10000,1,0,0,1,1,0,1,0,0
-10,4,99,9276,10000,1,0,0,1,1,0,1,0,0
-10,4,100,9277,10000,1,0,0,1,1,0,1,0,0
-10,4,101,9278,10000,1,0,0,1,1,0,1,0,0
-10,4,102,9279,10000,1,0,0,1,1,0,1,0,0
-10,4,103,9280,10000,1,0,0,1,1,0,1,0,0
-10,4,104,9281,10000,1,0,0,1,1,0,1,0,0
-10,4,105,9282,10000,1,0,0,1,1,0,1,0,0
-10,4,106,9283,10000,1,0,0,1,1,0,1,0,0
-10,4,107,9284,10000,1,0,0,1,1,0,1,0,0
-10,4,108,9285,10000,1,0,0,1,1,0,1,0,0
-10,4,109,9286,10000,1,0,0,1,1,0,1,0,0
-10,4,110,9287,10000,1,0,0,1,1,0,1,0,0
-10,4,111,9288,10000,1,0,0,1,1,0,1,0,0
-10,4,112,9289,10000,1,0,0,1,1,0,1,0,0
-10,4,113,9290,10000,1,0,0,1,1,0,1,0,0
-10,4,114,9291,10000,1,0,0,1,1,0,1,0,0
-10,4,115,9292,10000,1,0,0,1,1,0,1,0,0
-10,4,116,9293,10000,1,0,0,1,1,0,1,0,0
-10,4,117,9294,10000,1,0,0,1,1,0,1,0,0
-10,4,118,9295,10000,1,0,0,1,1,0,1,0,0
-10,4,119,9296,10000,1,0,0,1,1,0,1,0,0
-10,4,120,9297,10000,1,0,0,1,1,0,1,0,0
-10,4,121,9298,10000,1,0,0,1,1,0,1,0,0
-10,4,122,9299,10000,1,0,0,1,1,0,1,0,0
-10,4,123,9300,10000,1,0,0,1,1,0,1,0,0
-10,4,124,9301,10000,1,0,0,1,1,0,1,0,0
-10,4,125,13196,10000,1,0,0,1,1,0,1,0,0
-10,4,126,13197,10000,1,0,0,1,1,0,1,0,0
-10,4,127,13198,10000,1,0,0,1,1,0,1,0,0
-10,4,128,13199,10000,1,0,0,1,1,0,1,0,0
-10,4,129,15542,10000,1,0,0,1,1,0,1,0,0
-10,4,130,15543,10000,1,0,0,1,1,0,1,0,0
-10,4,131,15544,10000,1,0,0,1,1,0,1,0,0
-10,4,132,15545,10000,1,0,0,1,1,0,1,0,0
-10,4,133,13640,20000,1,0,0,1,1,0,1,0,0
-10,4,134,13641,20000,1,0,0,1,1,0,1,0,0
-10,4,135,13642,20000,1,0,0,1,1,0,1,0,0
-10,4,136,13643,20000,1,0,0,1,1,0,1,0,0
-10,4,137,13644,20000,1,0,0,1,1,0,1,0,0
-10,4,138,13645,20000,1,0,0,1,1,0,1,0,0
-10,4,139,13646,20000,1,0,0,1,1,0,1,0,0
-10,4,140,13647,20000,1,0,0,1,1,0,1,0,0
-10,4,141,13648,20000,1,0,0,1,1,0,1,0,0
-10,4,142,13649,20000,1,0,0,1,1,0,1,0,0
-10,4,143,13650,20000,1,0,0,1,1,0,1,0,0
-10,4,144,13651,20000,1,0,0,1,1,0,1,0,0
-10,4,145,13652,20000,1,0,0,1,1,0,1,0,0
-10,4,146,13653,20000,1,0,0,1,1,0,1,0,0
-10,4,147,13654,20000,1,0,0,1,1,0,1,0,0
-10,4,148,13655,20000,1,0,0,1,1,0,1,0,0
-10,4,149,13656,20000,1,0,0,1,1,0,1,0,0
-10,4,150,13657,20000,1,0,0,1,1,0,1,0,0
-10,4,151,13658,20000,1,0,0,1,1,0,1,0,0
-10,4,152,13659,20000,1,0,0,1,1,0,1,0,0
-10,4,153,13660,20000,1,0,0,1,1,0,1,0,0
-10,4,154,13661,20000,1,0,0,1,1,0,1,0,0
-10,4,155,13662,20000,1,0,0,1,1,0,1,0,0
-10,4,156,13663,20000,1,0,0,1,1,0,1,0,0
-10,4,157,13664,20000,1,0,0,1,1,0,1,0,0
-10,4,158,13665,20000,1,0,0,1,1,0,1,0,0
-10,4,159,13666,20000,1,0,0,1,1,0,1,0,0
-10,4,160,13667,20000,1,0,0,1,1,0,1,0,0
-10,4,161,13668,20000,1,0,0,1,1,0,1,0,0
-10,4,162,13669,20000,1,0,0,1,1,0,1,0,0
-10,4,163,13670,20000,1,0,0,1,1,0,1,0,0
-10,4,164,13671,20000,1,0,0,1,1,0,1,0,0
-10,4,165,13672,20000,1,0,0,1,1,0,1,0,0
-10,4,166,13673,20000,1,0,0,1,1,0,1,0,0
-10,4,167,13674,20000,1,0,0,1,1,0,1,0,0
-10,4,168,13675,20000,1,0,0,1,1,0,1,0,0
-10,4,169,13676,20000,1,0,0,1,1,0,1,0,0
-10,4,170,13677,20000,1,0,0,1,1,0,1,0,0
-10,4,171,13678,20000,1,0,0,1,1,0,1,0,0
-10,4,172,13679,20000,1,0,0,1,1,0,1,0,0
-10,4,173,13680,20000,1,0,0,1,1,0,1,0,0
-10,4,174,13681,20000,1,0,0,1,1,0,1,0,0
-10,4,175,13682,20000,1,0,0,1,1,0,1,0,0
-10,4,176,13683,20000,1,0,0,1,1,0,1,0,0
-10,4,177,13684,20000,1,0,0,1,1,0,1,0,0
-10,4,178,13685,20000,1,0,0,1,1,0,1,0,0
-10,4,179,13686,20000,1,0,0,1,1,0,1,0,0
-10,4,180,13687,20000,1,0,0,1,1,0,1,0,0
-10,4,181,13688,20000,1,0,0,1,1,0,1,0,0
-10,4,182,13689,20000,1,0,0,1,1,0,1,0,0
-10,4,183,13690,20000,1,0,0,1,1,0,1,0,0
-10,4,184,13691,20000,1,0,0,1,1,0,1,0,0
-10,4,185,15546,20000,1,0,0,1,1,0,1,0,0
-10,4,186,15547,20000,1,0,0,1,1,0,1,0,0
-10,4,187,15548,20000,1,0,0,1,1,0,1,0,0
-10,4,188,15549,20000,1,0,0,1,1,0,1,0,0
-10,4,189,16162,35000,1,0,0,1,1,0,1,0,0
-10,4,190,16163,35000,1,0,0,1,1,0,1,0,0
-10,4,191,16164,35000,1,0,0,1,1,0,1,0,0
-10,4,192,16165,35000,1,0,0,1,1,0,1,0,0
-10,4,193,16166,35000,1,0,0,1,1,0,1,0,0
-10,4,194,16167,35000,1,0,0,1,1,0,1,0,0
-10,4,195,16168,35000,1,0,0,1,1,0,1,0,0
-10,4,196,16169,35000,1,0,0,1,1,0,1,0,0
-10,4,197,16172,35000,1,0,0,1,1,0,1,0,0
-10,4,198,16173,35000,1,0,0,1,1,0,1,0,0
-10,4,199,16174,35000,1,0,0,1,1,0,1,0,0
-10,4,200,16175,35000,1,0,0,1,1,0,1,0,0
-10,4,201,16176,35000,1,0,0,1,1,0,1,0,0
-10,4,202,16177,35000,1,0,0,1,1,0,1,0,0
-10,4,203,16178,35000,1,0,0,1,1,0,1,0,0
-10,4,204,16179,35000,1,0,0,1,1,0,1,0,0
-10,4,205,16182,35000,1,0,0,1,1,0,1,0,0
-10,4,206,16183,35000,1,0,0,1,1,0,1,0,0
-10,4,207,16184,35000,1,0,0,1,1,0,1,0,0
-10,4,208,16185,35000,1,0,0,1,1,0,1,0,0
-10,4,209,16186,35000,1,0,0,1,1,0,1,0,0
-10,4,210,16187,35000,1,0,0,1,1,0,1,0,0
-10,4,211,16188,35000,1,0,0,1,1,0,1,0,0
-10,4,212,16189,35000,1,0,0,1,1,0,1,0,0
-10,4,213,16192,35000,1,0,0,1,1,0,1,0,0
-10,4,214,16193,35000,1,0,0,1,1,0,1,0,0
-10,4,215,16194,35000,1,0,0,1,1,0,1,0,0
-10,4,216,16195,35000,1,0,0,1,1,0,1,0,0
-10,4,217,16196,35000,1,0,0,1,1,0,1,0,0
-10,4,218,16197,35000,1,0,0,1,1,0,1,0,0
-10,4,219,16198,35000,1,0,0,1,1,0,1,0,0
-10,4,220,16199,35000,1,0,0,1,1,0,1,0,0
-10,4,221,16202,35000,1,0,0,1,1,0,1,0,0
-10,4,222,16203,35000,1,0,0,1,1,0,1,0,0
-10,4,223,16204,35000,1,0,0,1,1,0,1,0,0
-10,4,224,16205,35000,1,0,0,1,1,0,1,0,0
-10,4,225,16206,35000,1,0,0,1,1,0,1,0,0
-10,4,226,16207,35000,1,0,0,1,1,0,1,0,0
-10,4,227,16208,35000,1,0,0,1,1,0,1,0,0
-10,4,228,16209,35000,1,0,0,1,1,0,1,0,0
-10,4,229,16212,35000,1,0,0,1,1,0,1,0,0
-10,4,230,16213,35000,1,0,0,1,1,0,1,0,0
-10,4,231,16214,35000,1,0,0,1,1,0,1,0,0
-10,4,232,16215,35000,1,0,0,1,1,0,1,0,0
-10,4,233,16216,35000,1,0,0,1,1,0,1,0,0
-10,4,234,16217,35000,1,0,0,1,1,0,1,0,0
-10,4,235,16218,35000,1,0,0,1,1,0,1,0,0
-10,4,236,16219,35000,1,0,0,1,1,0,1,0,0
-10,4,237,16222,35000,1,0,0,1,1,0,1,0,0
-10,4,238,16223,35000,1,0,0,1,1,0,1,0,0
-10,4,239,16224,35000,1,0,0,1,1,0,1,0,0
-10,4,240,16225,35000,1,0,0,1,1,0,1,0,0
-10,4,241,16226,35000,1,0,0,1,1,0,1,0,0
-10,4,242,16227,35000,1,0,0,1,1,0,1,0,0
-10,4,243,16228,35000,1,0,0,1,1,0,1,0,0
-10,4,244,16229,35000,1,0,0,1,1,0,1,0,0
-10,4,245,16232,35000,1,0,0,1,1,0,1,0,0
-10,4,246,16233,35000,1,0,0,1,1,0,1,0,0
-10,4,247,16234,35000,1,0,0,1,1,0,1,0,0
-10,4,248,16235,35000,1,0,0,1,1,0,1,0,0
-10,4,249,16236,35000,1,0,0,1,1,0,1,0,0
-10,4,250,16237,35000,1,0,0,1,1,0,1,0,0
-10,4,251,16238,35000,1,0,0,1,1,0,1,0,0
-10,4,252,16239,35000,1,0,0,1,1,0,1,0,0
-10,4,253,16242,35000,1,0,0,1,1,0,1,0,0
-10,4,254,16243,35000,1,0,0,1,1,0,1,0,0
-10,4,255,16244,35000,1,0,0,1,1,0,1,0,0
-10,4,256,16245,35000,1,0,0,1,1,0,1,0,0
-10,4,257,16246,35000,1,0,0,1,1,0,1,0,0
-10,4,258,16247,35000,1,0,0,1,1,0,1,0,0
-10,4,259,16248,35000,1,0,0,1,1,0,1,0,0
-10,4,260,16249,35000,1,0,0,1,1,0,1,0,0
-10,4,261,16252,35000,1,0,0,1,1,0,1,0,0
-10,4,262,16253,35000,1,0,0,1,1,0,1,0,0
-10,4,263,16254,35000,1,0,0,1,1,0,1,0,0
-10,4,264,16255,35000,1,0,0,1,1,0,1,0,0
-10,4,265,16256,35000,1,0,0,1,1,0,1,0,0
-10,4,266,16257,35000,1,0,0,1,1,0,1,0,0
-10,4,267,16258,35000,1,0,0,1,1,0,1,0,0
-10,4,268,16259,35000,1,0,0,1,1,0,1,0,0
-10,4,269,16262,35000,1,0,0,1,1,0,1,0,0
-10,4,270,16263,35000,1,0,0,1,1,0,1,0,0
-10,4,271,16264,35000,1,0,0,1,1,0,1,0,0
-10,4,272,16265,35000,1,0,0,1,1,0,1,0,0
-10,4,273,16266,35000,1,0,0,1,1,0,1,0,0
-10,4,274,16267,35000,1,0,0,1,1,0,1,0,0
-10,4,275,16268,35000,1,0,0,1,1,0,1,0,0
-10,4,276,16269,35000,1,0,0,1,1,0,1,0,0
-10,4,277,16272,35000,1,0,0,1,1,0,1,0,0
-10,4,278,16273,35000,1,0,0,1,1,0,1,0,0
-10,4,279,16274,35000,1,0,0,1,1,0,1,0,0
-10,4,280,16275,35000,1,0,0,1,1,0,1,0,0
-10,4,281,16276,35000,1,0,0,1,1,0,1,0,0
-10,4,282,16277,35000,1,0,0,1,1,0,1,0,0
-10,4,283,16278,35000,1,0,0,1,1,0,1,0,0
-10,4,284,16279,35000,1,0,0,1,1,0,1,0,0
-10,4,285,16282,35000,1,0,0,1,1,0,1,0,0
-10,4,286,16283,35000,1,0,0,1,1,0,1,0,0
-10,4,287,16284,35000,1,0,0,1,1,0,1,0,0
-10,4,288,16285,35000,1,0,0,1,1,0,1,0,0
-10,4,289,16286,35000,1,0,0,1,1,0,1,0,0
-10,4,290,16287,35000,1,0,0,1,1,0,1,0,0
-10,4,291,16288,35000,1,0,0,1,1,0,1,0,0
-10,4,292,16289,35000,1,0,0,1,1,0,1,0,0
-10,4,293,16292,35000,1,0,0,1,1,0,1,0,0
-10,4,294,16293,35000,1,0,0,1,1,0,1,0,0
-10,4,295,16294,35000,1,0,0,1,1,0,1,0,0
-10,4,296,16295,35000,1,0,0,1,1,0,1,0,0
-10,4,297,16296,35000,1,0,0,1,1,0,1,0,0
-10,4,298,16297,35000,1,0,0,1,1,0,1,0,0
-10,4,299,16298,35000,1,0,0,1,1,0,1,0,0
-10,4,300,16299,35000,1,0,0,1,1,0,1,0,0
-10,8,301,14136,15000,1,0,0,1,1,0,1,0,0
-10,8,302,14137,15000,1,0,0,1,1,0,1,0,0
-10,8,303,14138,15000,1,0,0,1,1,0,1,0,0
-10,8,304,14139,15000,1,0,0,1,1,0,1,0,0
-10,8,305,14140,15000,1,0,0,1,1,0,1,0,0
-10,8,306,14141,15000,1,0,0,1,1,0,1,0,0
-10,8,307,14142,15000,1,0,0,1,1,0,1,0,0
-10,8,308,14143,15000,1,0,0,1,1,0,1,0,0
-10,8,309,14144,15000,1,0,0,1,1,0,1,0,0
-10,8,310,14145,15000,1,0,0,1,1,0,1,0,0
-10,8,311,14454,30000,1,0,0,1,1,0,1,0,0
-10,8,312,14455,30000,1,0,0,1,1,0,1,0,0
-10,8,313,14456,30000,1,0,0,1,1,0,1,0,0
-10,8,314,14457,30000,1,0,0,1,1,0,1,0,0
-10,8,315,14458,30000,1,0,0,1,1,0,1,0,0
-10,8,316,14459,30000,1,0,0,1,1,0,1,0,0
-10,8,317,14460,30000,1,0,0,1,1,0,1,0,0
-10,8,318,14461,30000,1,0,0,1,1,0,1,0,0
-10,8,319,14462,30000,1,0,0,1,1,0,1,0,0
-10,8,320,14463,30000,1,0,0,1,1,0,1,0,0
-10,8,321,12724,50000,1,0,0,1,1,0,1,0,0
-10,8,322,12725,50000,1,0,0,1,1,0,1,0,0
-10,8,323,12726,50000,1,0,0,1,1,0,1,0,0
-10,8,324,12727,50000,1,0,0,1,1,0,1,0,0
-10,8,325,12728,50000,1,0,0,1,1,0,1,0,0
-10,8,326,12729,50000,1,0,0,1,1,0,1,0,0
-10,8,327,12730,50000,1,0,0,1,1,0,1,0,0
-10,8,328,12731,50000,1,0,0,1,1,0,1,0,0
-10,8,329,12732,50000,1,0,0,1,1,0,1,0,0
-10,8,330,12733,50000,1,0,0,1,1,0,1,0,0
-10,8,331,12734,50000,1,0,0,1,1,0,1,0,0
-10,8,332,12735,50000,1,0,0,1,1,0,1,0,0
-10,8,333,12736,50000,1,0,0,1,1,0,1,0,0
-10,8,334,12737,50000,1,0,0,1,1,0,1,0,0
-10,8,335,12738,50000,1,0,0,1,1,0,1,0,0
-10,8,336,12739,50000,1,0,0,1,1,0,1,0,0
-10,8,337,12740,50000,1,0,0,1,1,0,1,0,0
-10,8,338,12741,50000,1,0,0,1,1,0,1,0,0
-10,8,339,12742,50000,1,0,0,1,1,0,1,0,0
-10,8,340,12743,50000,1,0,0,1,1,0,1,0,0
-10,8,341,12744,50000,1,0,0,1,1,0,1,0,0
-10,8,342,12745,50000,1,0,0,1,1,0,1,0,0
-10,8,343,12746,50000,1,0,0,1,1,0,1,0,0
-10,8,344,12747,50000,1,0,0,1,1,0,1,0,0
-10,8,345,12748,50000,1,0,0,1,1,0,1,0,0
-10,8,346,12749,50000,1,0,0,1,1,0,1,0,0
-10,8,347,12750,50000,1,0,0,1,1,0,1,0,0
-10,8,348,12751,50000,1,0,0,1,1,0,1,0,0
-10,8,349,12752,50000,1,0,0,1,1,0,1,0,0
-10,8,350,12753,50000,1,0,0,1,1,0,1,0,0
-10,8,351,15070,50000,1,0,0,1,1,0,1,0,0
-10,8,352,15071,50000,1,0,0,1,1,0,1,0,0
-10,8,353,15072,50000,1,0,0,1,1,0,1,0,0
-10,8,354,15073,50000,1,0,0,1,1,0,1,0,0
-10,8,355,15074,50000,1,0,0,1,1,0,1,0,0
-10,8,356,15075,50000,1,0,0,1,1,0,1,0,0
-10,8,357,15076,50000,1,0,0,1,1,0,1,0,0
-10,8,358,15077,50000,1,0,0,1,1,0,1,0,0
-10,8,359,15078,50000,1,0,0,1,1,0,1,0,0
-10,8,360,15079,50000,1,0,0,1,1,0,1,0,0
-10,8,361,15567,20000,1,0,0,1,1,0,1,0,0
-10,8,362,15568,20000,1,0,0,1,1,0,1,0,0
-10,8,363,15569,20000,1,0,0,1,1,0,1,0,0
-10,8,364,15570,20000,1,0,0,1,1,0,1,0,0
-10,8,365,15571,20000,1,0,0,1,1,0,1,0,0
-10,8,366,15572,20000,1,0,0,1,1,0,1,0,0
-10,8,367,15573,20000,1,0,0,1,1,0,1,0,0
-10,8,368,15574,20000,1,0,0,1,1,0,1,0,0
-10,8,369,15575,20000,1,0,0,1,1,0,1,0,0
-10,8,370,15576,20000,1,0,0,1,1,0,1,0,0
-10,8,371,15577,20000,1,0,0,1,1,0,1,0,0
-10,8,372,15578,20000,1,0,0,1,1,0,1,0,0
-10,8,373,15579,20000,1,0,0,1,1,0,1,0,0
-10,8,374,15580,20000,1,0,0,1,1,0,1,0,0
-10,8,375,15581,20000,1,0,0,1,1,0,1,0,0
-10,8,376,15582,20000,1,0,0,1,1,0,1,0,0
-10,8,377,15583,20000,1,0,0,1,1,0,1,0,0
-10,8,378,15584,20000,1,0,0,1,1,0,1,0,0
-10,8,379,15585,20000,1,0,0,1,1,0,1,0,0
-10,8,380,15586,20000,1,0,0,1,1,0,1,0,0
-10,8,381,15587,20000,1,0,0,1,1,0,1,0,0
-10,8,382,15588,20000,1,0,0,1,1,0,1,0,0
-10,8,383,15589,20000,1,0,0,1,1,0,1,0,0
-10,8,384,15590,20000,1,0,0,1,1,0,1,0,0
-10,8,385,15591,20000,1,0,0,1,1,0,1,0,0
-10,8,386,15592,20000,1,0,0,1,1,0,1,0,0
-10,8,387,15593,20000,1,0,0,1,1,0,1,0,0
-10,8,388,15594,20000,1,0,0,1,1,0,1,0,0
-10,8,389,15595,20000,1,0,0,1,1,0,1,0,0
-10,8,390,15596,20000,1,0,0,1,1,0,1,0,0
-10,8,391,15597,20000,1,0,0,1,1,0,1,0,0
-10,8,392,15598,20000,1,0,0,1,1,0,1,0,0
-10,8,393,15599,20000,1,0,0,1,1,0,1,0,0
-10,8,394,15600,20000,1,0,0,1,1,0,1,0,0
-10,8,395,15601,20000,1,0,0,1,1,0,1,0,0
-10,8,396,15602,20000,1,0,0,1,1,0,1,0,0
-10,8,397,15603,20000,1,0,0,1,1,0,1,0,0
-10,8,398,15604,20000,1,0,0,1,1,0,1,0,0
-10,8,399,15605,20000,1,0,0,1,1,0,1,0,0
-10,8,400,15606,20000,1,0,0,1,1,0,1,0,0
-10,8,401,15607,20000,1,0,0,1,1,0,1,0,0
-10,8,402,15608,20000,1,0,0,1,1,0,1,0,0
-10,8,403,15609,20000,1,0,0,1,1,0,1,0,0
-10,8,404,15610,20000,1,0,0,1,1,0,1,0,0
-10,8,405,15611,20000,1,0,0,1,1,0,1,0,0
-10,8,406,15612,20000,1,0,0,1,1,0,1,0,0
-10,8,407,15613,20000,1,0,0,1,1,0,1,0,0
-10,8,408,15614,20000,1,0,0,1,1,0,1,0,0
-10,8,409,15615,20000,1,0,0,1,1,0,1,0,0
-10,8,410,15616,20000,1,0,0,1,1,0,1,0,0
-10,8,411,15617,20000,1,0,0,1,1,0,1,0,0
-10,8,412,15618,20000,1,0,0,1,1,0,1,0,0
-10,8,413,15619,20000,1,0,0,1,1,0,1,0,0
-10,8,414,15620,20000,1,0,0,1,1,0,1,0,0
-10,8,415,15621,20000,1,0,0,1,1,0,1,0,0
-10,8,416,15622,20000,1,0,0,1,1,0,1,0,0
-10,8,417,15623,20000,1,0,0,1,1,0,1,0,0
-10,8,418,15624,20000,1,0,0,1,1,0,1,0,0
-10,8,419,15625,20000,1,0,0,1,1,0,1,0,0
-10,8,420,15626,20000,1,0,0,1,1,0,1,0,0
-10,8,421,15627,20000,1,0,0,1,1,0,1,0,0
-10,8,422,15628,20000,1,0,0,1,1,0,1,0,0
-10,8,423,15629,20000,1,0,0,1,1,0,1,0,0
-10,8,424,15630,20000,1,0,0,1,1,0,1,0,0
-10,8,425,15631,20000,1,0,0,1,1,0,1,0,0
-10,8,426,15632,20000,1,0,0,1,1,0,1,0,0
-10,8,427,15633,20000,1,0,0,1,1,0,1,0,0
-10,8,428,15634,20000,1,0,0,1,1,0,1,0,0
-10,8,429,15635,20000,1,0,0,1,1,0,1,0,0
-10,8,430,15636,20000,1,0,0,1,1,0,1,0,0
-10,8,431,15637,20000,1,0,0,1,1,0,1,0,0
-10,8,432,15638,20000,1,0,0,1,1,0,1,0,0
-10,8,433,15639,20000,1,0,0,1,1,0,1,0,0
-10,8,434,15640,20000,1,0,0,1,1,0,1,0,0
-10,8,435,15641,20000,1,0,0,1,1,0,1,0,0
-10,8,436,15642,20000,1,0,0,1,1,0,1,0,0
-10,8,437,15643,20000,1,0,0,1,1,0,1,0,0
-10,8,438,15644,20000,1,0,0,1,1,0,1,0,0
-10,8,439,15645,20000,1,0,0,1,1,0,1,0,0
-10,8,440,15646,20000,1,0,0,1,1,0,1,0,0
-10,8,441,15647,20000,1,0,0,1,1,0,1,0,0
-10,8,442,15648,20000,1,0,0,1,1,0,1,0,0
-10,8,443,15649,20000,1,0,0,1,1,0,1,0,0
-10,8,444,15650,20000,1,0,0,1,1,0,1,0,0
-10,8,445,15651,20000,1,0,0,1,1,0,1,0,0
-10,8,446,15652,20000,1,0,0,1,1,0,1,0,0
-10,8,447,15653,20000,1,0,0,1,1,0,1,0,0
-10,8,448,15654,20000,1,0,0,1,1,0,1,0,0
-10,8,449,15655,20000,1,0,0,1,1,0,1,0,0
-10,8,450,15656,20000,1,0,0,1,1,0,1,0,0
-10,8,451,15657,20000,1,0,0,1,1,0,1,0,0
-10,8,452,15658,20000,1,0,0,1,1,0,1,0,0
-10,8,453,15659,20000,1,0,0,1,1,0,1,0,0
-10,8,454,15660,20000,1,0,0,1,1,0,1,0,0
-10,8,455,15661,20000,1,0,0,1,1,0,1,0,0
-10,8,456,15662,20000,1,0,0,1,1,0,1,0,0
-10,8,457,15663,20000,1,0,0,1,1,0,1,0,0
-10,8,458,15664,20000,1,0,0,1,1,0,1,0,0
-10,8,459,15665,20000,1,0,0,1,1,0,1,0,0
-10,8,460,15666,20000,1,0,0,1,1,0,1,0,0
-10,8,461,15667,20000,1,0,0,1,1,0,1,0,0
-10,8,462,15668,20000,1,0,0,1,1,0,1,0,0
-10,8,463,15669,20000,1,0,0,1,1,0,1,0,0
-10,8,464,15670,20000,1,0,0,1,1,0,1,0,0
-10,8,465,15671,20000,1,0,0,1,1,0,1,0,0
-10,8,466,15672,20000,1,0,0,1,1,0,1,0,0
-10,8,467,15673,20000,1,0,0,1,1,0,1,0,0
-10,8,468,15674,20000,1,0,0,1,1,0,1,0,0
-10,8,469,15675,20000,1,0,0,1,1,0,1,0,0
-10,8,470,15676,20000,1,0,0,1,1,0,1,0,0
-10,8,471,15677,20000,1,0,0,1,1,0,1,0,0
-10,8,472,15678,20000,1,0,0,1,1,0,1,0,0
-10,8,473,15679,20000,1,0,0,1,1,0,1,0,0
-10,8,474,15680,20000,1,0,0,1,1,0,1,0,0
-10,8,475,15681,20000,1,0,0,1,1,0,1,0,0
-10,8,476,15682,20000,1,0,0,1,1,0,1,0,0
-10,8,477,15683,20000,1,0,0,1,1,0,1,0,0
-10,8,478,15684,20000,1,0,0,1,1,0,1,0,0
-10,8,479,15685,20000,1,0,0,1,1,0,1,0,0
-10,8,480,15686,20000,1,0,0,1,1,0,1,0,0
-10,8,481,15687,20000,1,0,0,1,1,0,1,0,0
-10,8,482,15688,20000,1,0,0,1,1,0,1,0,0
-10,8,483,15689,20000,1,0,0,1,1,0,1,0,0
-10,8,484,15690,20000,1,0,0,1,1,0,1,0,0
-10,8,485,15691,20000,1,0,0,1,1,0,1,0,0
-10,8,486,15692,20000,1,0,0,1,1,0,1,0,0
-10,8,487,15693,20000,1,0,0,1,1,0,1,0,0
-10,8,488,15694,20000,1,0,0,1,1,0,1,0,0
-10,8,489,15695,20000,1,0,0,1,1,0,1,0,0
-10,8,490,15696,20000,1,0,0,1,1,0,1,0,0
-10,8,491,15697,20000,1,0,0,1,1,0,1,0,0
-10,8,492,15698,20000,1,0,0,1,1,0,1,0,0
-10,8,493,15699,20000,1,0,0,1,1,0,1,0,0
-10,8,494,15700,20000,1,0,0,1,1,0,1,0,0
-10,8,495,15701,20000,1,0,0,1,1,0,1,0,0
-10,8,496,15702,20000,1,0,0,1,1,0,1,0,0
-10,8,497,15703,20000,1,0,0,1,1,0,1,0,0
-10,8,498,15704,20000,1,0,0,1,1,0,1,0,0
-10,8,499,15705,20000,1,0,0,1,1,0,1,0,0
-10,8,500,15706,20000,1,0,0,1,1,0,1,0,0
-10,8,501,15707,20000,1,0,0,1,1,0,1,0,0
-10,8,502,15708,20000,1,0,0,1,1,0,1,0,0
-10,8,503,15709,20000,1,0,0,1,1,0,1,0,0
-10,8,504,15710,20000,1,0,0,1,1,0,1,0,0
-10,8,505,15711,20000,1,0,0,1,1,0,1,0,0
-10,8,506,15712,20000,1,0,0,1,1,0,1,0,0
-10,8,507,15713,20000,1,0,0,1,1,0,1,0,0
-10,8,508,15714,20000,1,0,0,1,1,0,1,0,0
-10,8,509,15715,20000,1,0,0,1,1,0,1,0,0
-10,8,510,15716,20000,1,0,0,1,1,0,1,0,0
-10,8,511,15717,20000,1,0,0,1,1,0,1,0,0
-10,8,512,15718,20000,1,0,0,1,1,0,1,0,0
-10,8,513,15719,20000,1,0,0,1,1,0,1,0,0
-10,8,514,15720,20000,1,0,0,1,1,0,1,0,0
-10,8,515,15721,20000,1,0,0,1,1,0,1,0,0
-10,8,516,15722,20000,1,0,0,1,1,0,1,0,0
-10,8,517,15723,20000,1,0,0,1,1,0,1,0,0
-10,8,518,15724,20000,1,0,0,1,1,0,1,0,0
-10,8,519,15725,20000,1,0,0,1,1,0,1,0,0
-10,8,520,15726,20000,1,0,0,1,1,0,1,0,0
-10,8,521,15727,20000,1,0,0,1,1,0,1,0,0
-10,8,522,15728,20000,1,0,0,1,1,0,1,0,0
-10,8,523,15729,20000,1,0,0,1,1,0,1,0,0
-10,8,524,15730,20000,1,0,0,1,1,0,1,0,0
-10,8,525,15731,20000,1,0,0,1,1,0,1,0,0
-10,8,526,15732,20000,1,0,0,1,1,0,1,0,0
-10,8,527,15733,20000,1,0,0,1,1,0,1,0,0
-10,8,528,15734,20000,1,0,0,1,1,0,1,0,0
-10,8,529,15735,20000,1,0,0,1,1,0,1,0,0
-10,8,530,15736,20000,1,0,0,1,1,0,1,0,0
-10,8,531,15737,20000,1,0,0,1,1,0,1,0,0
-10,8,532,15738,20000,1,0,0,1,1,0,1,0,0
-10,8,533,15739,20000,1,0,0,1,1,0,1,0,0
-10,8,534,15740,20000,1,0,0,1,1,0,1,0,0
-10,8,535,15741,20000,1,0,0,1,1,0,1,0,0
-10,8,536,15742,20000,1,0,0,1,1,0,1,0,0
-10,8,537,15743,20000,1,0,0,1,1,0,1,0,0
-10,8,538,15744,20000,1,0,0,1,1,0,1,0,0
-10,8,539,15745,20000,1,0,0,1,1,0,1,0,0
-10,8,540,15746,20000,1,0,0,1,1,0,1,0,0
-10,8,541,15747,20000,1,0,0,1,1,0,1,0,0
-10,8,542,15748,20000,1,0,0,1,1,0,1,0,0
-10,8,543,15749,20000,1,0,0,1,1,0,1,0,0
-10,8,544,15750,20000,1,0,0,1,1,0,1,0,0
-10,8,545,15751,20000,1,0,0,1,1,0,1,0,0
-10,8,546,15752,20000,1,0,0,1,1,0,1,0,0
-10,8,547,15753,20000,1,0,0,1,1,0,1,0,0
-10,8,548,15754,20000,1,0,0,1,1,0,1,0,0
-10,8,549,15755,20000,1,0,0,1,1,0,1,0,0
-10,8,550,15756,20000,1,0,0,1,1,0,1,0,0
-10,8,551,15757,20000,1,0,0,1,1,0,1,0,0
-10,8,552,15758,20000,1,0,0,1,1,0,1,0,0
-10,8,553,15759,20000,1,0,0,1,1,0,1,0,0
-10,8,554,15760,20000,1,0,0,1,1,0,1,0,0
-10,8,555,15761,20000,1,0,0,1,1,0,1,0,0
-10,8,556,15762,20000,1,0,0,1,1,0,1,0,0
-10,8,557,15763,20000,1,0,0,1,1,0,1,0,0
-10,8,558,15764,20000,1,0,0,1,1,0,1,0,0
-10,8,559,15765,20000,1,0,0,1,1,0,1,0,0
-10,8,560,15766,20000,1,0,0,1,1,0,1,0,0
-10,8,561,15919,20000,1,0,0,1,1,0,1,0,0
-10,8,562,15920,20000,1,0,0,1,1,0,1,0,0
-10,8,563,15921,20000,1,0,0,1,1,0,1,0,0
-10,8,564,15922,20000,1,0,0,1,1,0,1,0,0
-10,8,565,15923,20000,1,0,0,1,1,0,1,0,0
-10,8,566,15924,20000,1,0,0,1,1,0,1,0,0
-10,8,567,15925,20000,1,0,0,1,1,0,1,0,0
-10,8,568,15926,20000,1,0,0,1,1,0,1,0,0
-10,8,569,15927,20000,1,0,0,1,1,0,1,0,0
-10,8,570,15928,20000,1,0,0,1,1,0,1,0,0
-10,8,571,15929,20000,1,0,0,1,1,0,1,0,0
-10,8,572,15930,20000,1,0,0,1,1,0,1,0,0
-10,8,573,15931,20000,1,0,0,1,1,0,1,0,0
-10,8,574,15932,20000,1,0,0,1,1,0,1,0,0
-10,8,575,15933,20000,1,0,0,1,1,0,1,0,0
-10,8,576,15934,20000,1,0,0,1,1,0,1,0,0
-10,8,577,15935,20000,1,0,0,1,1,0,1,0,0
-10,8,578,15936,20000,1,0,0,1,1,0,1,0,0
-10,8,579,15937,20000,1,0,0,1,1,0,1,0,0
-10,8,580,15938,20000,1,0,0,1,1,0,1,0,0
-10,8,581,15939,20000,1,0,0,1,1,0,1,0,0
-10,8,582,15940,20000,1,0,0,1,1,0,1,0,0
-10,8,583,15941,20000,1,0,0,1,1,0,1,0,0
-10,8,584,15942,20000,1,0,0,1,1,0,1,0,0
-10,8,585,15943,20000,1,0,0,1,1,0,1,0,0
-10,8,586,15944,20000,1,0,0,1,1,0,1,0,0
-10,8,587,15945,20000,1,0,0,1,1,0,1,0,0
-10,8,588,15946,20000,1,0,0,1,1,0,1,0,0
-10,8,589,15947,20000,1,0,0,1,1,0,1,0,0
-10,8,590,15948,20000,1,0,0,1,1,0,1,0,0
-10,8,591,15949,20000,1,0,0,1,1,0,1,0,0
-10,8,592,15950,20000,1,0,0,1,1,0,1,0,0
-10,8,593,15951,20000,1,0,0,1,1,0,1,0,0
-10,8,594,15952,20000,1,0,0,1,1,0,1,0,0
-10,8,595,15953,20000,1,0,0,1,1,0,1,0,0
-10,8,596,15954,20000,1,0,0,1,1,0,1,0,0
-10,8,597,15955,20000,1,0,0,1,1,0,1,0,0
-10,8,598,15956,20000,1,0,0,1,1,0,1,0,0
-10,8,599,15957,20000,1,0,0,1,1,0,1,0,0
-10,8,600,15958,20000,1,0,0,1,1,0,1,0,0
-10,8,601,15959,20000,1,0,0,1,1,0,1,0,0
-10,8,602,15960,20000,1,0,0,1,1,0,1,0,0
-10,8,603,15961,20000,1,0,0,1,1,0,1,0,0
-10,8,604,15962,20000,1,0,0,1,1,0,1,0,0
-10,8,605,15963,20000,1,0,0,1,1,0,1,0,0
-10,8,606,15964,20000,1,0,0,1,1,0,1,0,0
-10,8,607,15965,20000,1,0,0,1,1,0,1,0,0
-10,8,608,15966,20000,1,0,0,1,1,0,1,0,0
-10,8,609,15967,20000,1,0,0,1,1,0,1,0,0
-10,8,610,15968,20000,1,0,0,1,1,0,1,0,0
-10,7,611,13506,250,1,0,0,1,1,0,1,50,0
-10,7,612,15011,250,1,0,0,1,1,0,1,50,0
-10,7,613,13636,250,1,0,0,1,1,0,1,50,0
-10,7,614,1227,250,1,0,0,1,1,0,1,50,0
-10,7,615,15022,250,1,0,0,1,1,0,1,50,0
-10,8,616,4407,1000,1,0,0,1,1,0,1,0,0
-10,8,617,4408,1000,1,0,0,1,1,0,1,0,0
-10,8,618,4409,1000,1,0,0,1,1,0,1,0,0
-10,8,619,4410,1000,1,0,0,1,1,0,1,0,0
-10,8,620,4411,1000,1,0,0,1,1,0,1,0,0
-10,8,621,4412,1000,1,0,0,1,1,0,1,0,0
-10,8,622,4413,1000,1,0,0,1,1,0,1,0,0
-10,8,623,4414,1000,1,0,0,1,1,0,1,0,0
-10,8,624,4823,1000,1,0,0,1,1,0,1,0,0
-10,8,625,4824,1000,1,0,0,1,1,0,1,0,0
-10,8,626,4825,1000,1,0,0,1,1,0,1,0,0
-10,8,627,4826,1000,1,0,0,1,1,0,1,0,0
-10,8,628,4827,1000,1,0,0,1,1,0,1,0,0
-10,8,629,4828,1000,1,0,0,1,1,0,1,0,0
-10,8,630,4829,1000,1,0,0,1,1,0,1,0,0
-10,8,631,4830,1000,1,0,0,1,1,0,1,0,0
-10,8,632,5194,1000,1,0,0,1,1,0,1,0,0
-10,8,633,5195,1000,1,0,0,1,1,0,1,0,0
-10,8,634,5196,1000,1,0,0,1,1,0,1,0,0
-10,8,635,5197,1000,1,0,0,1,1,0,1,0,0
-10,8,636,5198,1000,1,0,0,1,1,0,1,0,0
-10,8,637,5199,1000,1,0,0,1,1,0,1,0,0
-10,8,638,5200,1000,1,0,0,1,1,0,1,0,0
-10,8,639,5201,1000,1,0,0,1,1,0,1,0,0
-10,8,640,13630,1000,1,0,0,1,1,0,1,0,0
-10,8,641,13631,1000,1,0,0,1,1,0,1,0,0
-10,8,642,13632,1000,1,0,0,1,1,0,1,0,0
-10,8,643,13633,1000,1,0,0,1,1,0,1,0,0
-10,8,644,13634,1000,1,0,0,1,1,0,1,0,0
-10,8,645,13635,1000,1,0,0,1,1,0,1,0,0
-10,8,646,15103,1000,1,0,0,1,1,0,1,0,0
-10,8,647,15104,1000,1,0,0,1,1,0,1,0,0
-10,8,648,15105,1000,1,0,0,1,1,0,1,0,0
-10,8,649,15106,1000,1,0,0,1,1,0,1,0,0
-10,8,650,15107,1000,1,0,0,1,1,0,1,0,0
-10,8,651,15108,1000,1,0,0,1,1,0,1,0,0
-10,8,652,16459,1000,1,0,0,1,1,0,1,0,0
-10,8,653,16460,1000,1,0,0,1,1,0,1,0,0
-10,8,654,16461,1000,1,0,0,1,1,0,1,0,0
-10,8,655,16462,1000,1,0,0,1,1,0,1,0,0
-10,8,656,16463,1000,1,0,0,1,1,0,1,0,0
-10,8,657,16464,1000,1,0,0,1,1,0,1,0,0
-10,8,658,16465,1000,1,0,0,1,1,0,1,0,0
-10,8,659,16466,1000,1,0,0,1,1,0,1,0,0
-10,8,660,16467,1000,1,0,0,1,1,0,1,0,0
-10,8,661,16468,1000,1,0,0,1,1,0,1,0,0
-10,8,662,16469,1000,1,0,0,1,1,0,1,0,0
-10,8,663,16470,1000,1,0,0,1,1,0,1,0,0
-10,8,664,16471,1000,1,0,0,1,1,0,1,0,0
-10,8,665,16472,1000,1,0,0,1,1,0,1,0,0
-10,8,666,13416,1000,1,0,0,1,1,0,1,0,0
-10,8,667,13417,1000,1,0,0,1,1,0,1,0,0
-10,8,668,13418,1000,1,0,0,1,1,0,1,0,0
-10,8,669,13419,1000,1,0,0,1,1,0,1,0,0
-10,8,670,13420,1000,1,0,0,1,1,0,1,0,0
-10,8,671,14283,1000,1,0,0,1,1,0,1,0,0
-10,8,672,14284,1000,1,0,0,1,1,0,1,0,0
-10,8,673,14285,1000,1,0,0,1,1,0,1,0,0
-10,8,674,14286,1000,1,0,0,1,1,0,1,0,0
-10,8,675,13182,1000,1,0,0,1,1,0,1,0,0
-10,8,676,13507,1000,1,0,0,1,1,0,1,0,0
-10,8,677,13981,1000,1,0,0,1,1,0,1,0,0
-10,8,678,14744,1000,1,0,0,1,1,0,1,0,0
-10,8,679,14893,1000,1,0,0,1,1,0,1,0,0
-10,8,680,15785,1000,1,0,0,1,1,0,1,0,0
-10,8,681,16419,1000,1,0,0,1,1,0,1,0,0
-10,8,682,11470,1000,1,0,0,1,1,0,1,0,0
-10,8,683,12512,1000,1,0,0,1,1,0,1,0,0
-10,8,684,12884,1000,1,0,0,1,1,0,1,0,0
-10,8,685,12513,1000,1,0,0,1,1,0,1,0,0
-10,8,686,12514,1000,1,0,0,1,1,0,1,0,0
-10,8,687,12515,1000,1,0,0,1,1,0,1,0,0
-10,8,688,12516,1000,1,0,0,1,1,0,1,0,0
-10,8,689,12517,1000,1,0,0,1,1,0,1,0,0
-10,8,690,12518,1000,1,0,0,1,1,0,1,0,0
-10,8,691,12519,1000,1,0,0,1,1,0,1,0,0
-10,8,692,12520,1000,1,0,0,1,1,0,1,0,0
-10,8,693,12521,1000,1,0,0,1,1,0,1,0,0
-10,8,694,8179,1000,1,0,0,1,1,0,1,0,0
-10,8,695,9704,1000,1,0,0,1,1,0,1,0,0
-10,8,696,15448,1000,1,0,0,1,1,0,1,0,0
-10,8,697,11162,1000,1,0,0,1,1,0,1,0,0
-10,8,698,11163,1000,1,0,0,1,1,0,1,0,0
-10,8,699,11164,1000,1,0,0,1,1,0,1,0,0
-10,8,700,11165,1000,1,0,0,1,1,0,1,0,0
-10,8,701,11661,1000,1,0,0,1,1,0,1,0,0
-10,8,702,11662,1000,1,0,0,1,1,0,1,0,0
-10,8,703,14639,1000,1,0,0,1,1,0,1,0,0
-10,8,704,13607,10,1,0,0,1,1,0,1,0,0
-10,7,705,15774,3000,1,0,0,1,1,0,1,100,0
-10,7,706,15775,3000,1,0,0,1,1,0,1,100,0
-10,7,707,11420,3000,1,0,0,1,1,0,1,100,0
-10,7,708,14704,3000,1,0,0,1,1,0,1,100,0
-10,7,709,13177,3000,1,0,0,1,1,0,1,100,0
-10,7,710,14191,3000,1,0,0,1,1,0,1,100,0
-10,7,711,13449,3000,1,0,0,1,1,0,1,100,0
-10,7,712,14192,3000,1,0,0,1,1,0,1,100,0
-10,7,713,15772,3000,1,0,0,1,1,0,1,100,0
-10,7,714,13791,3000,1,0,0,1,1,0,1,100,0
-10,7,715,14006,3000,1,0,0,1,1,0,1,100,0
-10,7,716,15768,3000,1,0,0,1,1,0,1,100,0
-10,7,717,14069,3000,1,0,0,1,1,0,1,100,0
-10,7,718,14124,3000,1,0,0,1,1,0,1,100,0
-10,7,719,15507,3000,1,0,0,1,1,0,1,100,0
-10,7,720,15508,3000,1,0,0,1,1,0,1,100,0
-10,7,721,14855,3000,1,0,0,1,1,0,1,100,0
-10,7,722,14894,3000,1,0,0,1,1,0,1,100,0
-10,7,723,16444,3000,1,0,0,1,1,0,1,100,0
-10,7,724,16445,3000,1,0,0,1,1,0,1,100,0
-10,7,725,12509,3000,1,0,0,1,1,0,1,100,0
-10,7,726,14126,3000,1,0,0,1,1,0,1,100,0
-10,7,727,15062,3000,1,0,0,1,1,0,1,100,0
-10,7,728,15063,3000,1,0,0,1,1,0,1,100,0
-10,7,729,14891,3000,1,0,0,1,1,0,1,100,0
-10,7,730,14895,3000,1,0,0,1,1,0,1,100,0
-10,7,731,14091,3000,1,0,0,1,1,0,1,100,0
-10,7,732,14092,3000,1,0,0,1,1,0,1,100,0
-10,7,733,14501,3000,1,0,0,1,1,0,1,100,0
-10,7,734,14506,3000,1,0,0,1,1,0,1,100,0
-10,7,735,15285,3000,1,0,0,1,1,0,1,100,0
-10,7,736,15286,3000,1,0,0,1,1,0,1,100,0
-10,7,737,16442,3000,1,0,0,1,1,0,1,100,0
-10,7,738,16443,3000,1,0,0,1,1,0,1,100,0
-10,7,739,15027,3000,1,0,0,1,1,0,1,100,0
-10,7,740,15028,3000,1,0,0,1,1,0,1,100,0
-10,7,741,13453,3000,1,0,0,1,1,0,1,100,0
-10,7,742,14193,3000,1,0,0,1,1,0,1,100,0
-10,7,743,13178,3000,1,0,0,1,1,0,1,100,0
-10,7,744,14194,3000,1,0,0,1,1,0,1,100,0
-10,7,745,16454,3000,1,0,0,1,1,0,1,100,0
-10,7,746,16455,3000,1,0,0,1,1,0,1,100,0
-10,7,747,15030,3000,1,0,0,1,1,0,1,100,0
-10,7,748,15031,3000,1,0,0,1,1,0,1,100,0
-10,7,749,13790,3000,1,0,0,1,1,0,1,100,0
-10,7,750,14005,3000,1,0,0,1,1,0,1,100,0
-10,7,751,14406,3000,1,0,0,1,1,0,1,100,0
-10,7,752,14413,3000,1,0,0,1,1,0,1,100,0
-10,7,753,16448,3000,1,0,0,1,1,0,1,100,0
-10,7,754,16449,3000,1,0,0,1,1,0,1,100,0
-10,7,755,12872,3000,1,0,0,1,1,0,1,100,0
-10,7,756,14187,3000,1,0,0,1,1,0,1,100,0
-10,7,757,14125,3000,1,0,0,1,1,0,1,100,0
-10,7,758,14500,3000,1,0,0,1,1,0,1,100,0
-10,7,759,14505,3000,1,0,0,1,1,0,1,100,0
-10,7,760,15118,3000,1,0,0,1,1,0,1,100,0
-10,7,761,15119,3000,1,0,0,1,1,0,1,100,0
-10,7,762,14662,3000,1,0,0,1,1,0,1,100,0
-10,7,763,14663,3000,1,0,0,1,1,0,1,100,0
-10,7,764,15771,3000,1,0,0,1,1,0,1,100,0
-10,7,765,9700,3000,1,0,0,1,1,0,1,100,0
-10,7,766,14498,3000,1,0,0,1,1,0,1,100,0
-10,7,767,14913,3000,1,0,0,1,1,0,1,100,0
-10,7,768,14914,3000,1,0,0,1,1,0,1,100,0
-10,7,769,13508,3000,1,0,0,1,1,0,1,100,0
-10,7,770,15115,3000,1,0,0,1,1,0,1,100,0
-10,7,771,15116,3000,1,0,0,1,1,0,1,100,0
-10,7,772,15113,3000,1,0,0,1,1,0,1,100,0
-10,7,773,15114,3000,1,0,0,1,1,0,1,100,0
-10,7,774,15222,3000,1,0,0,1,1,0,1,100,0
-10,7,775,15223,3000,1,0,0,1,1,0,1,100,0
-10,7,776,10750,3000,1,0,0,1,1,0,1,100,0
-10,7,777,14705,3000,1,0,0,1,1,0,1,100,0
-10,7,778,15027,3000,1,0,0,1,1,0,1,100,0
-10,7,779,15028,3000,1,0,0,1,1,0,1,100,0
-10,7,780,10380,3000,1,0,0,1,1,0,1,100,0
-10,7,781,15060,3000,1,0,0,1,1,0,1,100,0
-10,7,782,13963,3000,1,0,0,1,1,0,1,100,0
-10,7,783,14026,3000,1,0,0,1,1,0,1,100,0
-10,7,784,13964,3000,1,0,0,1,1,0,1,100,0
-10,7,785,14027,3000,1,0,0,1,1,0,1,100,0
-10,7,786,15064,3000,1,0,0,1,1,0,1,100,0
-10,7,787,15065,3000,1,0,0,1,1,0,1,100,0
-10,7,788,15524,3000,1,0,0,1,1,0,1,100,0
-10,7,789,15525,3000,1,0,0,1,1,0,1,100,0
-10,7,790,16450,3000,1,0,0,1,1,0,1,100,0
-10,7,791,16451,3000,1,0,0,1,1,0,1,100,0
-10,7,792,16344,3000,1,0,0,1,1,0,1,100,0
-10,7,793,16345,3000,1,0,0,1,1,0,1,100,0
-10,7,794,16342,3000,1,0,0,1,1,0,1,100,0
-10,7,795,16343,3000,1,0,0,1,1,0,1,100,0
-10,7,796,15220,3000,1,0,0,1,1,0,1,100,0
-10,7,797,15221,3000,1,0,0,1,1,0,1,100,0
-10,7,798,15066,3000,1,0,0,1,1,0,1,100,0
-10,7,799,15067,3000,1,0,0,1,1,0,1,100,0
-10,7,800,14089,3000,1,0,0,1,1,0,1,100,0
-10,7,801,14090,3000,1,0,0,1,1,0,1,100,0
-10,7,802,14195,3000,1,0,0,1,1,0,1,100,0
-10,7,803,14196,3000,1,0,0,1,1,0,1,100,0
-10,7,804,13965,3000,1,0,0,1,1,0,1,100,0
-10,7,805,14028,3000,1,0,0,1,1,0,1,100,0
-10,7,806,13508,3000,1,0,0,1,1,0,1,100,0
-10,7,807,13962,3000,1,0,0,1,1,0,1,100,0
-10,7,808,14314,3000,1,0,0,1,1,0,1,100,0
-10,7,809,13404,3000,1,0,0,1,1,0,1,100,0
-10,7,810,14188,3000,1,0,0,1,1,0,1,100,0
-10,7,811,14032,3000,1,0,0,1,1,0,1,100,0
-10,7,812,13960,3000,1,0,0,1,1,0,1,100,0
-10,7,813,15819,3000,1,0,0,1,1,0,1,100,0
-10,7,814,15820,3000,1,0,0,1,1,0,1,100,0
-10,7,815,10750,3000,1,0,0,1,1,0,1,100,0
-10,7,816,14705,3000,1,0,0,1,1,0,1,100,0
-10,7,817,14407,3000,1,0,0,1,1,0,1,100,0
-10,7,818,14414,3000,1,0,0,1,1,0,1,100,0
-10,7,819,16352,3000,1,0,0,1,1,0,1,100,0
-10,7,820,16353,3000,1,0,0,1,1,0,1,100,0
-10,7,821,14502,3000,1,0,0,1,1,0,1,100,0
-10,7,822,14507,3000,1,0,0,1,1,0,1,100,0
-10,7,823,10811,3000,1,0,0,1,1,0,1,100,0
-10,7,824,15061,3000,1,0,0,1,1,0,1,100,0
-10,7,825,15823,3000,1,0,0,1,1,0,1,100,0
-10,7,826,15824,3000,1,0,0,1,1,0,1,100,0
-10,7,827,15224,3000,1,0,0,1,1,0,1,100,0
-10,7,828,15225,3000,1,0,0,1,1,0,1,100,0
-10,7,829,14503,3000,1,0,0,1,1,0,1,100,0
-10,7,830,14510,3000,1,0,0,1,1,0,1,100,0
-10,7,831,15776,3000,1,0,0,1,1,0,1,100,0
-10,7,832,15777,3000,1,0,0,1,1,0,1,100,0
-10,7,833,15821,3000,1,0,0,1,1,0,1,100,0
-10,7,834,15822,3000,1,0,0,1,1,0,1,100,0
-10,7,835,14198,3000,1,0,0,1,1,0,1,100,0
-10,7,836,14197,3000,1,0,0,1,1,0,1,100,0
-10,7,837,16446,3000,1,0,0,1,1,0,1,100,0
-10,7,838,16447,3000,1,0,0,1,1,0,1,100,0
-10,7,839,14905,3000,1,0,0,1,1,0,1,100,0
-10,7,840,14907,3000,1,0,0,1,1,0,1,100,0
-10,7,841,14904,3000,1,0,0,1,1,0,1,100,0
-10,7,842,14906,3000,1,0,0,1,1,0,1,100,0
-10,7,843,14659,3000,1,0,0,1,1,0,1,100,0
-10,7,844,14660,3000,1,0,0,1,1,0,1,100,0
-10,7,845,13326,3000,1,0,0,1,1,0,1,100,0
-10,7,846,14416,3000,1,0,0,1,1,0,1,100,0
-10,7,847,13450,3000,1,0,0,1,1,0,1,100,0
-10,7,848,14031,3000,1,0,0,1,1,0,1,100,0
-10,7,849,16492,3000,1,0,0,1,1,0,1,100,0
-10,7,850,16493,3000,1,0,0,1,1,0,1,100,0
-10,8,851,1520,1,1,0,0,1,1,0,1,0,0
-10,8,852,7011,1,1,0,0,1,1,0,1,0,0
-10,7,853,14299,500,1,0,0,1,1,0,1,20,0
-10,7,854,14389,500,1,0,0,1,1,0,1,20,0
-10,7,855,15177,500,1,0,0,1,1,0,1,20,0
-10,7,856,14537,500,1,0,0,1,1,0,1,20,0
-10,7,857,14758,500,1,0,0,1,1,0,1,20,0
-10,7,858,14854,500,1,0,0,1,1,0,1,20,0
-10,7,859,13974,500,1,0,0,1,1,0,1,20,0
-10,7,860,15021,500,1,0,0,1,1,0,1,20,0
-10,7,861,15111,500,1,0,0,1,1,0,1,20,0
-10,7,862,15226,500,1,0,0,1,1,0,1,20,0
-10,7,863,15773,500,1,0,0,1,1,0,1,20,0
-10,7,864,15825,500,1,0,0,1,1,0,1,20,0
-10,7,865,15827,500,1,0,0,1,1,0,1,20,0
-10,7,866,16340,500,1,0,0,1,1,0,1,20,0
-10,7,867,16341,500,1,0,0,1,1,0,1,20,0
-10,7,868,16457,500,1,0,0,1,1,0,1,20,0
-10,7,869,16458,500,1,0,0,1,1,0,1,20,0
-10,7,870,11698,250,1,0,0,1,1,0,1,50,0
-10,7,871,11700,250,1,0,0,1,1,0,1,50,0
-10,8,872,4358,10,1,0,0,1,1,0,1,0,0
-10,8,873,7981,1,1,0,0,1,1,0,1,0,0
-10,8,874,7267,20,1,0,0,1,1,0,1,0,0
-10,8,875,9958,20,1,0,0,1,1,0,1,0,999
-10,8,876,1548,20,1,0,0,1,1,0,1,0,0
-10,8,877,1613,20,1,0,0,1,1,0,1,0,0
-10,8,878,1026,1,1,0,0,1,1,0,1,0,0
-10,8,879,5380,1,1,0,0,1,1,0,1,0,0
-10,8,880,11284,15,1,0,0,1,1,0,1,0,0
-10,8,881,11285,15,1,0,0,1,1,0,1,0,0
-10,8,882,11286,15,1,0,0,1,1,0,1,0,0
-10,8,883,10356,500,1,0,0,1,1,0,1,0,0
-10,8,884,12511,500,1,0,0,1,1,0,1,0,0
-10,8,885,13238,500,1,0,0,1,1,0,1,0,0
-10,8,886,1691,1,1,0,0,1,1,0,1,0,0
-10,8,887,9708,1,1,0,0,1,1,0,1,0,0
-10,8,888,11383,10,1,0,0,1,1,0,1,0,0
-10,8,889,11382,10,1,0,0,1,1,0,1,0,0
-10,8,890,11381,10,1,0,0,1,1,0,1,0,0
-10,7,891,16348,3000,1,0,0,1,1,0,1,100,0
-10,8,892,11386,10,1,0,0,1,1,0,1,0,0
-10,8,893,5767,1,10000,0,0,1,1,0,1,0,0
-10,8,894,5765,1,10000,0,0,1,1,0,1,0,0
-10,8,895,5768,1,10000,0,0,1,1,0,1,0,0
-10,8,896,14444,10,1,0,0,1,1,0,1,0,0
-10,8,897,14443,10,1,0,0,1,1,0,1,0,0
-10,8,898,14445,10,1,0,0,1,1,0,1,0,0
-10,8,899,15068,500,1,0,0,1,1,0,1,20,0
-10,7,900,16532,1000,1,0,0,1,1,0,1,0,0
-10,8,901,100,1,10000,0,0,1,1,0,1,0,0
-10,8,902,11243,1,1,0,0,1,1,0,1,0,0
-10,8,903,101,1,10000,0,0,1,1,0,1,0,0
-10,7,904,14368,3000,1,0,0,1,1,0,1,50,0
-10,8,905,8943,1,20,0,0,1,1,0,1,0,0
-10,7,906,1622,3000,1,0,0,1,1,0,1,0,0
-10,8,907,8953,1,20,0,0,1,1,0,1,0,0
-10,8,908,13693,1,20,0,0,1,1,0,1,0,0
-10,8,909,8949,1,20,0,0,1,1,0,1,0,0
-10,8,910,8955,1,20,0,0,1,1,0,1,0,0
-10,7,911,16456,500,1,0,0,1,1,0,1,0,0
\ No newline at end of file
diff --git a/bin/questlists/.gitkeep b/bin/questlists/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/bundled-schema/FestaDefaults.sql b/bundled-schema/FestaDefaults.sql
new file mode 100644
index 000000000..b8a3d46fc
--- /dev/null
+++ b/bundled-schema/FestaDefaults.sql
@@ -0,0 +1,260 @@
+BEGIN;
+
+-- Ripped prizes
+INSERT INTO public.festa_prizes
+ (type, tier, souls_req, item_id, num_item)
+VALUES
+ ('personal', 1, 1, 9647, 7),
+ ('personal', 2, 1, 9647, 7),
+ ('personal', 3, 1, 9647, 7),
+ ('personal', 1, 200, 11284, 4),
+ ('personal', 2, 200, 11284, 4),
+ ('personal', 3, 200, 11284, 4),
+ ('personal', 1, 400, 11381, 3),
+ ('personal', 2, 400, 11381, 3),
+ ('personal', 3, 400, 11381, 3),
+ ('personal', 1, 600, 11284, 8),
+ ('personal', 2, 600, 11284, 8),
+ ('personal', 3, 600, 11284, 8),
+ ('personal', 1, 800, 11384, 3),
+ ('personal', 2, 800, 11384, 3),
+ ('personal', 3, 800, 11384, 3),
+ ('personal', 1, 1000, 11284, 12),
+ ('personal', 2, 1000, 11284, 12),
+ ('personal', 3, 1000, 11284, 12),
+ ('personal', 1, 1200, 11381, 5),
+ ('personal', 2, 1200, 11381, 5),
+ ('personal', 3, 1200, 11381, 5),
+ ('personal', 1, 1400, 11284, 16),
+ ('personal', 2, 1400, 11284, 16),
+ ('personal', 3, 1400, 11284, 16),
+ ('personal', 1, 1700, 11384, 5),
+ ('personal', 2, 1700, 11384, 5),
+ ('personal', 3, 1700, 11384, 5),
+ ('personal', 1, 2000, 11284, 16),
+ ('personal', 2, 2000, 11284, 16),
+ ('personal', 3, 2000, 11284, 16),
+ ('personal', 1, 2500, 11382, 4),
+ ('personal', 2, 2500, 11382, 4),
+ ('personal', 3, 2500, 11382, 4),
+ ('personal', 1, 3000, 11284, 24),
+ ('personal', 2, 3000, 11284, 24),
+ ('personal', 3, 3000, 11284, 24),
+ ('personal', 1, 4000, 11385, 4),
+ ('personal', 2, 4000, 11385, 4),
+ ('personal', 3, 4000, 11385, 4),
+ ('personal', 1, 5000, 11381, 11),
+ ('personal', 2, 5000, 11381, 11),
+ ('personal', 3, 5000, 11381, 11),
+ ('personal', 1, 6000, 5177, 5),
+ ('personal', 2, 6000, 5177, 5),
+ ('personal', 3, 6000, 5177, 5),
+ ('personal', 1, 7000, 11384, 11),
+ ('personal', 2, 7000, 11384, 11),
+ ('personal', 3, 7000, 11384, 11),
+ ('personal', 1, 10000, 11382, 8),
+ ('personal', 2, 10000, 11382, 8),
+ ('personal', 3, 10000, 11382, 8),
+ ('personal', 1, 15000, 11385, 4),
+ ('personal', 2, 15000, 11385, 4),
+ ('personal', 3, 15000, 11385, 4),
+ ('personal', 1, 20000, 11381, 13),
+ ('personal', 2, 20000, 11381, 13),
+ ('personal', 3, 20000, 11381, 13),
+ ('personal', 1, 25000, 11385, 4),
+ ('personal', 2, 25000, 11385, 4),
+ ('personal', 3, 25000, 11385, 4),
+ ('personal', 1, 30000, 11383, 1),
+ ('personal', 2, 30000, 11383, 1),
+ ('personal', 3, 30000, 11383, 1);
+
+INSERT INTO public.festa_prizes
+(type, tier, souls_req, item_id, num_item)
+VALUES
+ ('guild', 1, 100, 7468, 5),
+ ('guild', 2, 100, 7468, 5),
+ ('guild', 3, 100, 7465, 5),
+ ('guild', 1, 300, 7469, 5),
+ ('guild', 2, 300, 7469, 5),
+ ('guild', 3, 300, 7466, 5),
+ ('guild', 1, 700, 7470, 5),
+ ('guild', 2, 700, 7470, 5),
+ ('guild', 3, 700, 7467, 5),
+ ('guild', 1, 1500, 13405, 14),
+ ('guild', 1, 1500, 1520, 3),
+ ('guild', 2, 1500, 13405, 14),
+ ('guild', 2, 1500, 1520, 3),
+ ('guild', 3, 1500, 7011, 3),
+ ('guild', 3, 1500, 13405, 14),
+ ('guild', 1, 3000, 10201, 10),
+ ('guild', 2, 3000, 10201, 10),
+ ('guild', 3, 3000, 10201, 10),
+ ('guild', 1, 6000, 13895, 14),
+ ('guild', 1, 6000, 1520, 6),
+ ('guild', 2, 6000, 13895, 14),
+ ('guild', 2, 6000, 1520, 6),
+ ('guild', 3, 6000, 13895, 14),
+ ('guild', 3, 6000, 7011, 4),
+ ('guild', 1, 12000, 13406, 14),
+ ('guild', 1, 12000, 1520, 9),
+ ('guild', 2, 12000, 13406, 14),
+ ('guild', 2, 12000, 1520, 9),
+ ('guild', 3, 12000, 13406, 14),
+ ('guild', 3, 12000, 7011, 5),
+ ('guild', 1, 25000, 10207, 10),
+ ('guild', 2, 25000, 10207, 10),
+ ('guild', 3, 25000, 10207, 10),
+ ('guild', 1, 50000, 1520, 12),
+ ('guild', 1, 50000, 13896, 14),
+ ('guild', 2, 50000, 1520, 12),
+ ('guild', 2, 50000, 13896, 14),
+ ('guild', 3, 50000, 7011, 6),
+ ('guild', 3, 50000, 13896, 14),
+ ('guild', 1, 100000, 10201, 10),
+ ('guild', 2, 100000, 10201, 10),
+ ('guild', 3, 100000, 10201, 10),
+ ('guild', 1, 200000, 13406, 16),
+ ('guild', 2, 200000, 13406, 16),
+ ('guild', 3, 200000, 13406, 16),
+ ('guild', 1, 300000, 13896, 16),
+ ('guild', 2, 300000, 13896, 16),
+ ('guild', 3, 300000, 13896, 16),
+ ('guild', 1, 400000, 10207, 10),
+ ('guild', 2, 400000, 10207, 10),
+ ('guild', 3, 400000, 10207, 10),
+ ('guild', 1, 500000, 13407, 6),
+ ('guild', 1, 500000, 13897, 6),
+ ('guild', 2, 500000, 13407, 6),
+ ('guild', 2, 500000, 13897, 6),
+ ('guild', 3, 500000, 13407, 6),
+ ('guild', 3, 500000, 13897, 6);
+
+-- Ripped trials
+INSERT INTO public.festa_trials
+ (objective, goal_id, times_req, locale_req, reward)
+VALUES
+ (1,27,1,0,1),
+ (5,53034,0,0,400),
+ (5,22042,0,0,89),
+ (5,23397,0,0,89),
+ (1,28,1,0,1),
+ (1,68,1,0,1),
+ (1,6,1,0,2),
+ (1,38,1,0,2),
+ (1,20,1,0,3),
+ (1,39,1,0,4),
+ (1,48,1,0,4),
+ (1,67,1,0,4),
+ (1,93,1,0,4),
+ (1,22,1,0,5),
+ (1,52,1,0,5),
+ (1,101,1,0,5),
+ (1,1,1,0,5),
+ (1,37,1,0,5),
+ (1,15,1,0,5),
+ (1,45,1,0,5),
+ (1,74,1,0,5),
+ (1,78,1,0,5),
+ (1,103,1,0,5),
+ (1,51,1,0,6),
+ (1,17,1,0,6),
+ (1,21,1,0,6),
+ (1,92,1,0,6),
+ (1,47,1,0,7),
+ (1,46,1,0,7),
+ (1,26,1,0,7),
+ (1,14,1,0,7),
+ (1,11,1,0,7),
+ (1,44,1,0,8),
+ (1,43,1,0,8),
+ (1,49,1,0,8),
+ (1,40,1,0,8),
+ (1,76,1,0,8),
+ (1,89,1,0,8),
+ (1,94,1,0,8),
+ (1,96,1,0,8),
+ (1,75,1,0,8),
+ (1,91,1,0,8),
+ (1,53,1,0,9),
+ (1,80,1,0,9),
+ (1,42,1,0,9),
+ (1,79,1,0,9),
+ (1,81,1,0,10),
+ (1,41,1,0,10),
+ (1,82,1,0,10),
+ (1,90,1,0,10),
+ (1,149,1,0,10),
+ (1,85,1,0,11),
+ (1,95,1,0,11),
+ (1,121,1,0,11),
+ (1,142,1,0,11),
+ (1,141,1,0,11),
+ (1,146,1,0,12),
+ (1,147,1,0,12),
+ (1,148,1,0,12),
+ (1,151,1,0,12),
+ (1,152,1,0,12),
+ (1,159,1,0,12),
+ (1,153,1,0,12),
+ (1,162,1,0,12),
+ (1,111,1,0,13),
+ (1,110,1,0,13),
+ (1,112,1,0,13),
+ (1,109,1,0,14),
+ (1,169,1,0,15),
+ (2,33,1,0,6),
+ (2,104,1,0,8),
+ (2,119,1,0,8),
+ (2,120,1,0,8),
+ (2,54,1,0,8),
+ (2,59,1,0,8),
+ (2,64,1,0,8),
+ (2,65,1,0,8),
+ (2,99,1,0,9),
+ (2,83,1,0,9),
+ (2,84,1,0,10),
+ (2,77,1,0,10),
+ (2,106,1,0,10),
+ (2,55,1,0,10),
+ (2,58,1,0,10),
+ (2,7,1,0,10),
+ (2,50,1,0,11),
+ (2,131,1,0,11),
+ (2,129,1,0,11),
+ (2,140,1,0,11),
+ (2,122,1,0,11),
+ (2,126,1,0,11),
+ (2,127,1,0,11),
+ (2,128,1,0,11),
+ (2,130,1,0,11),
+ (2,139,1,0,11),
+ (2,144,1,0,11),
+ (2,150,1,0,11),
+ (2,158,1,0,11),
+ (2,164,1,0,15),
+ (2,165,1,0,15),
+ (2,2,1,7,15),
+ (2,36,1,0,15),
+ (2,71,1,0,15),
+ (2,108,1,0,15),
+ (2,116,1,0,15),
+ (2,107,1,0,15),
+ (2,154,1,0,17),
+ (2,166,1,0,17),
+ (2,170,1,0,18),
+ (3,31,1,0,1),
+ (3,8,1,0,3),
+ (3,123,1,0,8),
+ (3,105,1,0,9),
+ (3,125,1,0,11),
+ (3,115,1,0,12),
+ (3,114,1,0,12),
+ (3,161,1,0,12),
+ (4,670,1,0,1),
+ (4,671,1,0,1),
+ (4,672,1,0,1),
+ (4,675,1,0,1),
+ (4,673,1,0,1),
+ (4,674,1,0,1);
+
+END;
\ No newline at end of file
diff --git a/bundled-schema/NetcafeDefaults.sql b/bundled-schema/NetcafeDefaults.sql
new file mode 100644
index 000000000..458cfa7b9
--- /dev/null
+++ b/bundled-schema/NetcafeDefaults.sql
@@ -0,0 +1,13 @@
+BEGIN;
+
+INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity)
+VALUES
+ (1800, 17, 0, 250),
+ (3600, 17, 0, 500),
+ (7200, 17, 0, 1000),
+ (10800, 17, 0, 1500),
+ (18000, 17, 0, 1750),
+ (28800, 17, 0, 3000),
+ (43200, 17, 0, 4000);
+
+END;
\ No newline at end of file
diff --git a/bundled-schema/RoadShopItems.sql b/bundled-schema/RoadShopItems.sql
new file mode 100644
index 000000000..593a3a3cf
--- /dev/null
+++ b/bundled-schema/RoadShopItems.sql
@@ -0,0 +1,918 @@
+BEGIN;
+
+INSERT INTO public.normal_shop_items
+(shoptype, shopid, itemhash, itemid, points, tradequantity, rankreqlow, rankreqhigh, rankreqg, storelevelreq, maximumquantity, boughtquantity, roadfloorsrequired, weeklyfataliskills)
+VALUES
+ (10,6,1,2146,25,1,0,0,1,1,0,1,0,0),
+ (10,6,2,2147,25,1,0,0,1,1,0,1,0,0),
+ (10,6,3,2148,25,1,0,0,1,1,0,1,0,0),
+ (10,6,4,2149,25,1,0,0,1,1,0,1,0,0),
+ (10,6,5,2150,25,1,0,0,1,1,0,1,0,0),
+ (10,6,6,2151,25,1,0,0,1,1,0,1,0,0),
+ (10,6,7,2152,25,1,0,0,1,1,0,1,0,0),
+ (10,6,8,2153,25,1,0,0,1,1,0,1,0,0),
+ (10,6,9,2154,25,1,0,0,1,1,0,1,0,0),
+ (10,6,10,2155,25,1,0,0,1,1,0,1,0,0),
+ (10,6,11,4398,25,1,0,0,1,1,0,1,0,0),
+ (10,6,12,12460,25,1,0,0,1,1,0,1,0,0),
+ (10,6,13,12461,25,1,0,0,1,1,0,1,0,0),
+ (10,6,14,12462,25,1,0,0,1,1,0,1,0,0),
+ (10,6,15,12463,25,1,0,0,1,1,0,1,0,0),
+ (10,6,16,12464,25,1,0,0,1,1,0,1,0,0),
+ (10,6,17,12465,25,1,0,0,1,1,0,1,0,0),
+ (10,6,18,12466,25,1,0,0,1,1,0,1,0,0),
+ (10,6,19,12467,25,1,0,0,1,1,0,1,0,0),
+ (10,6,20,12468,25,1,0,0,1,1,0,1,0,0),
+ (10,6,21,12469,25,1,0,0,1,1,0,1,0,0),
+ (10,6,22,15109,1000,1,0,0,1,1,0,1,0,0),
+ (10,6,23,15110,1000,1,0,0,1,1,0,1,0,0),
+ (10,6,24,2158,200,100,0,0,1,1,0,1,0,0),
+ (10,6,25,12306,2,1,0,0,1,1,0,1,80,0),
+ (10,6,26,12306,20000,10000,0,0,1,1,0,1,80,0),
+ (10,4,27,11664,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,28,11665,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,29,11666,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,30,11667,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,31,11668,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,32,11669,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,33,11670,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,34,11671,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,35,11672,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,36,11673,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,37,11674,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,38,11675,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,39,11676,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,40,11677,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,41,11678,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,42,11679,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,43,11680,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,44,11681,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,45,11682,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,46,11683,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,47,11684,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,48,11685,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,49,11686,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,50,11687,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,51,11688,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,52,11689,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,53,11690,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,54,11691,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,55,11692,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,56,11693,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,57,11694,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,58,11695,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,59,11696,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,60,11697,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,61,12893,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,62,12894,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,63,12895,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,64,12896,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,65,12897,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,66,12898,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,67,12899,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,68,14337,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,69,14338,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,70,14339,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,71,14340,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,72,14341,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,73,14342,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,74,14343,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,75,14344,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,76,14345,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,77,9254,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,78,9255,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,79,9256,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,80,9257,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,81,9258,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,82,9259,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,83,9260,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,84,9261,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,85,9262,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,86,9263,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,87,9264,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,88,9265,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,89,9266,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,90,9267,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,91,9268,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,92,9269,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,93,9270,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,94,9271,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,95,9272,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,96,9273,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,97,9274,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,98,9275,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,99,9276,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,100,9277,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,101,9278,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,102,9279,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,103,9280,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,104,9281,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,105,9282,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,106,9283,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,107,9284,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,108,9285,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,109,9286,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,110,9287,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,111,9288,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,112,9289,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,113,9290,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,114,9291,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,115,9292,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,116,9293,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,117,9294,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,118,9295,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,119,9296,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,120,9297,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,121,9298,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,122,9299,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,123,9300,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,124,9301,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,125,13196,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,126,13197,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,127,13198,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,128,13199,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,129,15542,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,130,15543,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,131,15544,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,132,15545,10000,1,0,0,1,1,0,1,0,0),
+ (10,4,133,13640,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,134,13641,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,135,13642,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,136,13643,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,137,13644,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,138,13645,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,139,13646,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,140,13647,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,141,13648,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,142,13649,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,143,13650,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,144,13651,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,145,13652,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,146,13653,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,147,13654,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,148,13655,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,149,13656,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,150,13657,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,151,13658,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,152,13659,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,153,13660,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,154,13661,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,155,13662,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,156,13663,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,157,13664,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,158,13665,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,159,13666,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,160,13667,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,161,13668,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,162,13669,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,163,13670,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,164,13671,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,165,13672,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,166,13673,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,167,13674,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,168,13675,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,169,13676,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,170,13677,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,171,13678,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,172,13679,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,173,13680,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,174,13681,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,175,13682,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,176,13683,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,177,13684,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,178,13685,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,179,13686,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,180,13687,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,181,13688,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,182,13689,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,183,13690,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,184,13691,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,185,15546,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,186,15547,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,187,15548,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,188,15549,20000,1,0,0,1,1,0,1,0,0),
+ (10,4,189,16162,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,190,16163,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,191,16164,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,192,16165,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,193,16166,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,194,16167,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,195,16168,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,196,16169,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,197,16172,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,198,16173,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,199,16174,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,200,16175,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,201,16176,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,202,16177,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,203,16178,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,204,16179,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,205,16182,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,206,16183,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,207,16184,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,208,16185,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,209,16186,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,210,16187,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,211,16188,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,212,16189,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,213,16192,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,214,16193,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,215,16194,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,216,16195,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,217,16196,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,218,16197,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,219,16198,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,220,16199,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,221,16202,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,222,16203,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,223,16204,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,224,16205,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,225,16206,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,226,16207,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,227,16208,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,228,16209,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,229,16212,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,230,16213,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,231,16214,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,232,16215,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,233,16216,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,234,16217,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,235,16218,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,236,16219,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,237,16222,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,238,16223,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,239,16224,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,240,16225,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,241,16226,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,242,16227,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,243,16228,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,244,16229,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,245,16232,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,246,16233,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,247,16234,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,248,16235,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,249,16236,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,250,16237,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,251,16238,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,252,16239,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,253,16242,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,254,16243,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,255,16244,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,256,16245,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,257,16246,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,258,16247,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,259,16248,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,260,16249,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,261,16252,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,262,16253,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,263,16254,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,264,16255,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,265,16256,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,266,16257,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,267,16258,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,268,16259,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,269,16262,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,270,16263,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,271,16264,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,272,16265,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,273,16266,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,274,16267,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,275,16268,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,276,16269,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,277,16272,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,278,16273,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,279,16274,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,280,16275,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,281,16276,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,282,16277,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,283,16278,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,284,16279,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,285,16282,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,286,16283,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,287,16284,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,288,16285,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,289,16286,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,290,16287,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,291,16288,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,292,16289,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,293,16292,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,294,16293,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,295,16294,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,296,16295,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,297,16296,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,298,16297,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,299,16298,35000,1,0,0,1,1,0,1,0,0),
+ (10,4,300,16299,35000,1,0,0,1,1,0,1,0,0),
+ (10,8,301,14136,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,302,14137,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,303,14138,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,304,14139,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,305,14140,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,306,14141,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,307,14142,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,308,14143,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,309,14144,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,310,14145,15000,1,0,0,1,1,0,1,0,0),
+ (10,8,311,14454,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,312,14455,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,313,14456,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,314,14457,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,315,14458,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,316,14459,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,317,14460,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,318,14461,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,319,14462,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,320,14463,30000,1,0,0,1,1,0,1,0,0),
+ (10,8,321,12724,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,322,12725,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,323,12726,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,324,12727,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,325,12728,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,326,12729,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,327,12730,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,328,12731,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,329,12732,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,330,12733,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,331,12734,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,332,12735,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,333,12736,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,334,12737,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,335,12738,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,336,12739,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,337,12740,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,338,12741,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,339,12742,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,340,12743,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,341,12744,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,342,12745,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,343,12746,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,344,12747,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,345,12748,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,346,12749,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,347,12750,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,348,12751,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,349,12752,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,350,12753,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,351,15070,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,352,15071,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,353,15072,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,354,15073,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,355,15074,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,356,15075,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,357,15076,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,358,15077,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,359,15078,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,360,15079,50000,1,0,0,1,1,0,1,0,0),
+ (10,8,361,15567,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,362,15568,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,363,15569,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,364,15570,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,365,15571,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,366,15572,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,367,15573,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,368,15574,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,369,15575,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,370,15576,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,371,15577,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,372,15578,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,373,15579,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,374,15580,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,375,15581,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,376,15582,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,377,15583,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,378,15584,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,379,15585,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,380,15586,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,381,15587,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,382,15588,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,383,15589,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,384,15590,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,385,15591,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,386,15592,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,387,15593,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,388,15594,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,389,15595,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,390,15596,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,391,15597,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,392,15598,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,393,15599,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,394,15600,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,395,15601,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,396,15602,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,397,15603,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,398,15604,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,399,15605,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,400,15606,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,401,15607,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,402,15608,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,403,15609,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,404,15610,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,405,15611,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,406,15612,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,407,15613,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,408,15614,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,409,15615,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,410,15616,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,411,15617,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,412,15618,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,413,15619,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,414,15620,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,415,15621,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,416,15622,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,417,15623,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,418,15624,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,419,15625,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,420,15626,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,421,15627,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,422,15628,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,423,15629,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,424,15630,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,425,15631,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,426,15632,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,427,15633,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,428,15634,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,429,15635,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,430,15636,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,431,15637,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,432,15638,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,433,15639,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,434,15640,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,435,15641,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,436,15642,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,437,15643,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,438,15644,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,439,15645,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,440,15646,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,441,15647,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,442,15648,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,443,15649,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,444,15650,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,445,15651,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,446,15652,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,447,15653,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,448,15654,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,449,15655,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,450,15656,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,451,15657,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,452,15658,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,453,15659,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,454,15660,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,455,15661,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,456,15662,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,457,15663,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,458,15664,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,459,15665,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,460,15666,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,461,15667,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,462,15668,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,463,15669,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,464,15670,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,465,15671,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,466,15672,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,467,15673,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,468,15674,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,469,15675,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,470,15676,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,471,15677,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,472,15678,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,473,15679,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,474,15680,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,475,15681,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,476,15682,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,477,15683,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,478,15684,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,479,15685,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,480,15686,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,481,15687,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,482,15688,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,483,15689,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,484,15690,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,485,15691,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,486,15692,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,487,15693,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,488,15694,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,489,15695,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,490,15696,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,491,15697,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,492,15698,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,493,15699,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,494,15700,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,495,15701,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,496,15702,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,497,15703,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,498,15704,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,499,15705,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,500,15706,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,501,15707,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,502,15708,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,503,15709,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,504,15710,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,505,15711,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,506,15712,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,507,15713,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,508,15714,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,509,15715,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,510,15716,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,511,15717,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,512,15718,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,513,15719,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,514,15720,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,515,15721,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,516,15722,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,517,15723,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,518,15724,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,519,15725,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,520,15726,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,521,15727,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,522,15728,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,523,15729,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,524,15730,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,525,15731,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,526,15732,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,527,15733,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,528,15734,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,529,15735,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,530,15736,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,531,15737,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,532,15738,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,533,15739,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,534,15740,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,535,15741,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,536,15742,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,537,15743,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,538,15744,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,539,15745,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,540,15746,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,541,15747,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,542,15748,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,543,15749,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,544,15750,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,545,15751,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,546,15752,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,547,15753,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,548,15754,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,549,15755,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,550,15756,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,551,15757,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,552,15758,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,553,15759,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,554,15760,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,555,15761,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,556,15762,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,557,15763,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,558,15764,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,559,15765,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,560,15766,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,561,15919,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,562,15920,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,563,15921,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,564,15922,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,565,15923,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,566,15924,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,567,15925,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,568,15926,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,569,15927,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,570,15928,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,571,15929,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,572,15930,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,573,15931,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,574,15932,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,575,15933,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,576,15934,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,577,15935,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,578,15936,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,579,15937,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,580,15938,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,581,15939,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,582,15940,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,583,15941,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,584,15942,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,585,15943,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,586,15944,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,587,15945,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,588,15946,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,589,15947,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,590,15948,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,591,15949,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,592,15950,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,593,15951,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,594,15952,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,595,15953,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,596,15954,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,597,15955,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,598,15956,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,599,15957,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,600,15958,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,601,15959,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,602,15960,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,603,15961,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,604,15962,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,605,15963,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,606,15964,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,607,15965,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,608,15966,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,609,15967,20000,1,0,0,1,1,0,1,0,0),
+ (10,8,610,15968,20000,1,0,0,1,1,0,1,0,0),
+ (10,7,611,13506,250,1,0,0,1,1,0,1,50,0),
+ (10,7,612,15011,250,1,0,0,1,1,0,1,50,0),
+ (10,7,613,13636,250,1,0,0,1,1,0,1,50,0),
+ (10,7,614,1227,250,1,0,0,1,1,0,1,50,0),
+ (10,7,615,15022,250,1,0,0,1,1,0,1,50,0),
+ (10,8,616,4407,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,617,4408,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,618,4409,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,619,4410,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,620,4411,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,621,4412,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,622,4413,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,623,4414,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,624,4823,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,625,4824,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,626,4825,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,627,4826,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,628,4827,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,629,4828,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,630,4829,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,631,4830,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,632,5194,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,633,5195,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,634,5196,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,635,5197,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,636,5198,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,637,5199,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,638,5200,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,639,5201,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,640,13630,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,641,13631,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,642,13632,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,643,13633,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,644,13634,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,645,13635,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,646,15103,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,647,15104,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,648,15105,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,649,15106,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,650,15107,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,651,15108,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,652,16459,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,653,16460,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,654,16461,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,655,16462,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,656,16463,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,657,16464,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,658,16465,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,659,16466,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,660,16467,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,661,16468,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,662,16469,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,663,16470,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,664,16471,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,665,16472,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,666,13416,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,667,13417,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,668,13418,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,669,13419,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,670,13420,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,671,14283,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,672,14284,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,673,14285,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,674,14286,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,675,13182,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,676,13507,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,677,13981,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,678,14744,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,679,14893,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,680,15785,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,681,16419,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,682,11470,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,683,12512,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,684,12884,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,685,12513,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,686,12514,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,687,12515,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,688,12516,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,689,12517,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,690,12518,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,691,12519,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,692,12520,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,693,12521,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,694,8179,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,695,9704,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,696,15448,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,697,11162,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,698,11163,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,699,11164,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,700,11165,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,701,11661,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,702,11662,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,703,14639,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,704,13607,10,1,0,0,1,1,0,1,0,0),
+ (10,7,705,15774,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,706,15775,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,707,11420,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,708,14704,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,709,13177,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,710,14191,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,711,13449,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,712,14192,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,713,15772,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,714,13791,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,715,14006,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,716,15768,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,717,14069,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,718,14124,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,719,15507,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,720,15508,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,721,14855,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,722,14894,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,723,16444,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,724,16445,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,725,12509,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,726,14126,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,727,15062,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,728,15063,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,729,14891,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,730,14895,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,731,14091,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,732,14092,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,733,14501,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,734,14506,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,735,15285,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,736,15286,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,737,16442,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,738,16443,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,739,15027,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,740,15028,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,741,13453,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,742,14193,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,743,13178,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,744,14194,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,745,16454,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,746,16455,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,747,15030,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,748,15031,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,749,13790,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,750,14005,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,751,14406,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,752,14413,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,753,16448,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,754,16449,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,755,12872,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,756,14187,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,757,14125,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,758,14500,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,759,14505,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,760,15118,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,761,15119,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,762,14662,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,763,14663,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,764,15771,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,765,9700,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,766,14498,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,767,14913,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,768,14914,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,769,13508,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,770,15115,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,771,15116,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,772,15113,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,773,15114,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,774,15222,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,775,15223,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,776,10750,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,777,14705,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,778,15027,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,779,15028,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,780,10380,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,781,15060,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,782,13963,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,783,14026,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,784,13964,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,785,14027,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,786,15064,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,787,15065,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,788,15524,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,789,15525,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,790,16450,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,791,16451,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,792,16344,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,793,16345,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,794,16342,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,795,16343,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,796,15220,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,797,15221,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,798,15066,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,799,15067,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,800,14089,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,801,14090,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,802,14195,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,803,14196,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,804,13965,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,805,14028,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,806,13508,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,807,13962,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,808,14314,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,809,13404,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,810,14188,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,811,14032,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,812,13960,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,813,15819,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,814,15820,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,815,10750,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,816,14705,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,817,14407,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,818,14414,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,819,16352,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,820,16353,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,821,14502,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,822,14507,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,823,10811,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,824,15061,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,825,15823,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,826,15824,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,827,15224,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,828,15225,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,829,14503,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,830,14510,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,831,15776,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,832,15777,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,833,15821,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,834,15822,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,835,14198,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,836,14197,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,837,16446,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,838,16447,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,839,14905,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,840,14907,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,841,14904,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,842,14906,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,843,14659,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,844,14660,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,845,13326,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,846,14416,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,847,13450,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,848,14031,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,849,16492,3000,1,0,0,1,1,0,1,100,0),
+ (10,7,850,16493,3000,1,0,0,1,1,0,1,100,0),
+ (10,8,851,1520,1,1,0,0,1,1,0,1,0,0),
+ (10,8,852,7011,1,1,0,0,1,1,0,1,0,0),
+ (10,7,853,14299,500,1,0,0,1,1,0,1,20,0),
+ (10,7,854,14389,500,1,0,0,1,1,0,1,20,0),
+ (10,7,855,15177,500,1,0,0,1,1,0,1,20,0),
+ (10,7,856,14537,500,1,0,0,1,1,0,1,20,0),
+ (10,7,857,14758,500,1,0,0,1,1,0,1,20,0),
+ (10,7,858,14854,500,1,0,0,1,1,0,1,20,0),
+ (10,7,859,13974,500,1,0,0,1,1,0,1,20,0),
+ (10,7,860,15021,500,1,0,0,1,1,0,1,20,0),
+ (10,7,861,15111,500,1,0,0,1,1,0,1,20,0),
+ (10,7,862,15226,500,1,0,0,1,1,0,1,20,0),
+ (10,7,863,15773,500,1,0,0,1,1,0,1,20,0),
+ (10,7,864,15825,500,1,0,0,1,1,0,1,20,0),
+ (10,7,865,15827,500,1,0,0,1,1,0,1,20,0),
+ (10,7,866,16340,500,1,0,0,1,1,0,1,20,0),
+ (10,7,867,16341,500,1,0,0,1,1,0,1,20,0),
+ (10,7,868,16457,500,1,0,0,1,1,0,1,20,0),
+ (10,7,869,16458,500,1,0,0,1,1,0,1,20,0),
+ (10,7,870,11698,250,1,0,0,1,1,0,1,50,0),
+ (10,7,871,11700,250,1,0,0,1,1,0,1,50,0),
+ (10,8,872,4358,10,1,0,0,1,1,0,1,0,0),
+ (10,8,873,7981,1,1,0,0,1,1,0,1,0,0),
+ (10,8,874,7267,20,1,0,0,1,1,0,1,0,0),
+ (10,8,875,9958,20,1,0,0,1,1,0,1,0,999),
+ (10,8,876,1548,20,1,0,0,1,1,0,1,0,0),
+ (10,8,877,1613,20,1,0,0,1,1,0,1,0,0),
+ (10,8,878,1026,1,1,0,0,1,1,0,1,0,0),
+ (10,8,879,5380,1,1,0,0,1,1,0,1,0,0),
+ (10,8,880,11284,15,1,0,0,1,1,0,1,0,0),
+ (10,8,881,11285,15,1,0,0,1,1,0,1,0,0),
+ (10,8,882,11286,15,1,0,0,1,1,0,1,0,0),
+ (10,8,883,10356,500,1,0,0,1,1,0,1,0,0),
+ (10,8,884,12511,500,1,0,0,1,1,0,1,0,0),
+ (10,8,885,13238,500,1,0,0,1,1,0,1,0,0),
+ (10,8,886,1691,1,1,0,0,1,1,0,1,0,0),
+ (10,8,887,9708,1,1,0,0,1,1,0,1,0,0),
+ (10,8,888,11383,10,1,0,0,1,1,0,1,0,0),
+ (10,8,889,11382,10,1,0,0,1,1,0,1,0,0),
+ (10,8,890,11381,10,1,0,0,1,1,0,1,0,0),
+ (10,7,891,16348,3000,1,0,0,1,1,0,1,100,0),
+ (10,8,892,11386,10,1,0,0,1,1,0,1,0,0),
+ (10,8,893,5767,1,10000,0,0,1,1,0,1,0,0),
+ (10,8,894,5765,1,10000,0,0,1,1,0,1,0,0),
+ (10,8,895,5768,1,10000,0,0,1,1,0,1,0,0),
+ (10,8,896,14444,10,1,0,0,1,1,0,1,0,0),
+ (10,8,897,14443,10,1,0,0,1,1,0,1,0,0),
+ (10,8,898,14445,10,1,0,0,1,1,0,1,0,0),
+ (10,8,899,15068,500,1,0,0,1,1,0,1,20,0),
+ (10,7,900,16532,1000,1,0,0,1,1,0,1,0,0),
+ (10,8,901,100,1,10000,0,0,1,1,0,1,0,0),
+ (10,8,902,11243,1,1,0,0,1,1,0,1,0,0),
+ (10,8,903,101,1,10000,0,0,1,1,0,1,0,0),
+ (10,7,904,14368,3000,1,0,0,1,1,0,1,50,0),
+ (10,8,905,8943,1,20,0,0,1,1,0,1,0,0),
+ (10,7,906,1622,3000,1,0,0,1,1,0,1,0,0),
+ (10,8,907,8953,1,20,0,0,1,1,0,1,0,0),
+ (10,8,908,13693,1,20,0,0,1,1,0,1,0,0),
+ (10,8,909,8949,1,20,0,0,1,1,0,1,0,0),
+ (10,8,910,8955,1,20,0,0,1,1,0,1,0,0),
+ (10,7,911,16456,500,1,0,0,1,1,0,1,0,0);
+
+END;
\ No newline at end of file
diff --git a/config.json b/config.json
index 00e541adf..31ecd8416 100644
--- a/config.json
+++ b/config.json
@@ -2,14 +2,17 @@
"Host": "127.0.0.1",
"BinPath": "bin",
"DisableSoftCrash": false,
- "devmode": true,
- "devmodeoptions": {
- "serverName" : "",
+ "FeaturedWeapons": 1,
+ "DevMode": true,
+ "DevModeOptions": {
+ "PatchServerManifest": "",
+ "PatchServerFile": "",
+ "AutoCreateAccount": true,
"EnableLauncherServer": false,
- "hideLoginNotice": false,
- "loginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!
â– Report bugs on Discord!
â– Test everything!
â– Don't talk to softlocking NPCs!
â– Fork the code on GitHub!
Thank you to all of the contributors,
this wouldn't exist without you.",
- "cleandb": false,
- "maxlauncherhr": false,
+ "HideLoginNotice": false,
+ "LoginNotice": "Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!
â– Report bugs on Discord!
â– Test everything!
â– Don't talk to softlocking NPCs!
â– Fork the code on GitHub!
Thank you to all of the contributors,
this wouldn't exist without you.",
+ "CleanDB": false,
+ "MaxLauncherHR": false,
"LogInboundMessages": false,
"LogOutboundMessages": false,
"MaxHexdumpLength": 256,
@@ -20,64 +23,108 @@
"MezFesAlt": false,
"DisableMailItems": true,
"DisableTokenCheck": false,
+ "QuestDebugTools": false,
"SaveDumps": {
"Enabled": true,
"OutputDir": "savedata"
}
},
- "discord": {
- "enabled": false,
- "bottoken": "",
- "realtimeChannelID": ""
+ "Discord": {
+ "Enabled": false,
+ "BotToken": "",
+ "RealtimeChannelID": ""
},
- "database": {
- "host": "localhost",
- "port": 5432,
- "user": "postgres",
- "password": "",
- "database": "erupe"
+ "Commands": [
+ {
+ "Name": "Rights",
+ "Enabled": false,
+ "Prefix": "!rights"
+ }, {
+ "Name": "Raviente",
+ "Enabled": true,
+ "Prefix": "!ravi"
+ }, {
+ "Name": "Teleport",
+ "Enabled": false,
+ "Prefix": "!tele"
+ }, {
+ "Name": "Reload",
+ "Enabled": true,
+ "Prefix": "!reload"
+ }, {
+ "Name": "KeyQuest",
+ "Enabled": false,
+ "Prefix": "!kqf"
+ }, {
+ "Name": "Course",
+ "Enabled": true,
+ "Prefix": "!course"
+ }
+ ],
+ "Courses": [
+ {"Name": "HunterLife", "Enabled": true},
+ {"Name": "ExtraA", "Enabled": true},
+ {"Name": "Premium", "Enabled": true},
+ {"Name": "Assist", "Enabled": false},
+ {"Name": "Netcafe", "Enabled": false},
+ {"Name": "Hiden", "Enabled": false},
+ {"Name": "HunterSupport", "Enabled": false},
+ {"Name": "NetcafeBoost", "Enabled": false}
+ ],
+ "Database": {
+ "Host": "localhost",
+ "Port": 5432,
+ "User": "postgres",
+ "Password": "",
+ "Database": "erupe"
},
- "launcher": {
- "port": 80,
+ "Launcher": {
+ "Enabled": false,
+ "Port": 80,
"UseOriginalLauncherFiles": false
},
- "sign": {
- "port": 53312
+ "Sign": {
+ "Enabled": true,
+ "Port": 53312
},
- "entrance": {
- "port": 53310,
- "entries": [
+ "Channel": {
+ "Enabled": true
+ },
+ "Entrance": {
+ "Enabled": true,
+ "Port": 53310,
+ "Entries": [
{
- "name": "Newbie", "description": "", "ip": "", "type": 3, "recommended": 2, "allowedclientflags": 0,
- "channels": [
- { "port": 54001, "MaxPlayers": 100 },
- { "port": 54002, "MaxPlayers": 100 }
+ "Name": "Newbie", "Description": "", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54001, "MaxPlayers": 100 },
+ { "Port": 54002, "MaxPlayers": 100 }
]
}, {
- "name": "Normal", "description": "", "ip": "", "type": 1, "recommended": 0, "allowedclientflags": 0,
- "channels": [
- { "port": 54003, "MaxPlayers": 100 },
- { "port": 54004, "MaxPlayers": 100 }
+ "Name": "Normal", "Description": "", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54003, "MaxPlayers": 100 },
+ { "Port": 54004, "MaxPlayers": 100 }
]
}, {
- "name": "Cities", "description": "", "ip": "", "type": 2, "recommended": 0, "allowedclientflags": 0,
- "channels": [
- { "port": 54005, "MaxPlayers": 100 }
+ "Name": "Cities", "Description": "", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54005, "MaxPlayers": 100 }
]
}, {
- "name": "Tavern", "description": "", "ip": "", "type": 4, "recommended": 0, "allowedclientflags": 0,
- "channels": [
- { "port": 54006, "MaxPlayers": 100 }
+ "Name": "Tavern", "Description": "", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54006, "MaxPlayers": 100 }
]
}, {
- "name": "Return", "description": "", "ip": "", "type": 5, "recommended": 0, "allowedclientflags": 0,
- "channels": [
- { "port": 54007, "MaxPlayers": 100 }
+ "Name": "Return", "Description": "", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54007, "MaxPlayers": 100 }
]
}, {
- "name": "MezFes", "description": "", "ip": "", "type": 6, "recommended": 6, "allowedclientflags": 0,
- "channels": [
- { "port": 54008, "MaxPlayers": 100 }
+ "Name": "MezFes", "Description": "", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0,
+ "Channels": [
+ { "Port": 54008, "MaxPlayers": 100 }
]
}
]
diff --git a/config/config.go b/config/config.go
index bfbf8086a..d2804b9fc 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,8 +1,11 @@
package config
import (
+ "fmt"
"log"
"net"
+ "os"
+ "time"
"github.com/spf13/viper"
)
@@ -12,36 +15,41 @@ type Config struct {
Host string `mapstructure:"Host"`
BinPath string `mapstructure:"BinPath"`
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
+ FeaturedWeapons int // Number of Active Feature weapons to generate daily
DevMode bool
DevModeOptions DevModeOptions
Discord Discord
+ Commands []Command
+ Courses []Course
Database Database
Launcher Launcher
Sign Sign
+ Channel Channel
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
- ServerName string // To get specific instance server about (Current Players/Event Week)
- EnableLauncherServer bool // Enables the launcher server to be served on port 80
- HideLoginNotice bool // Hide the Erupe notice on login
- LoginNotice string // MHFML string of the login notice displayed
- CleanDB bool // Automatically wipes the DB on server reset.
- MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
- FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
- LogInboundMessages bool // Log all messages sent to the server
- LogOutboundMessages bool // Log all messages sent to the clients
- MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
- DivaEvent int // Diva Defense event status
- FestaEvent int // Hunter's Festa event status
- TournamentEvent int // VS Tournament event status
- MezFesEvent bool // MezFes status
- MezFesAlt bool // Swaps out Volpakkun for Tokotoko
- DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
- DisableMailItems bool // Hack to prevent english versions of MHF from crashing
- SaveDumps SaveDumpOptions
+ PatchServerManifest string // Manifest patch server override
+ PatchServerFile string // File patch server override
+ AutoCreateAccount bool // Automatically create accounts if they don't exist
+ HideLoginNotice bool // Hide the Erupe notice on login
+ LoginNotice string // MHFML string of the login notice displayed
+ CleanDB bool // Automatically wipes the DB on server reset.
+ MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
+ LogInboundMessages bool // Log all messages sent to the server
+ LogOutboundMessages bool // Log all messages sent to the clients
+ MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
+ DivaEvent int // Diva Defense event status
+ FestaEvent int // Hunter's Festa event status
+ TournamentEvent int // VS Tournament event status
+ MezFesEvent bool // MezFes status
+ MezFesAlt bool // Swaps out Volpakkun for Tokotoko
+ DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
+ DisableMailItems bool // Hack to prevent english versions of MHF from crashing
+ QuestDebugTools bool // Enable various quest debug logs
+ SaveDumps SaveDumpOptions
}
type SaveDumpOptions struct {
@@ -53,10 +61,20 @@ type SaveDumpOptions struct {
type Discord struct {
Enabled bool
BotToken string
- ServerID string
RealtimeChannelID string
- DevRoles []string
- DevMode bool
+}
+
+// Command is a channelserver chat command
+type Command struct {
+ Name string
+ Enabled bool
+ Prefix string
+}
+
+// Course represents a course within MHF
+type Course struct {
+ Name string
+ Enabled bool
}
// Database holds the postgres database config.
@@ -70,17 +88,24 @@ type Database struct {
// Launcher holds the launcher server config.
type Launcher struct {
+ Enabled bool
Port int
UseOriginalLauncherFiles bool
}
// Sign holds the sign server config.
type Sign struct {
- Port int
+ Enabled bool
+ Port int
+}
+
+type Channel struct {
+ Enabled bool
}
// Entrance holds the entrance server config.
type Entrance struct {
+ Enabled bool
Port uint16
Entries []EntranceServerInfo
}
@@ -107,6 +132,17 @@ type EntranceChannelInfo struct {
CurrentPlayers uint16
}
+var ErupeConfig *Config
+
+func init() {
+ var err error
+ ErupeConfig, err = LoadConfig()
+ if err != nil {
+ preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
+ }
+
+}
+
// getOutboundIP4 gets the preferred outbound ip4 of this machine
// From https://stackoverflow.com/a/37382208
func getOutboundIP4() net.IP {
@@ -148,3 +184,20 @@ func LoadConfig() (*Config, error) {
return c, nil
}
+
+func preventClose(text string) {
+ if ErupeConfig.DisableSoftCrash {
+ os.Exit(0)
+ }
+ fmt.Println("\nFailed to start Erupe:\n" + text)
+ go wait()
+ fmt.Println("\nPress Enter/Return to exit...")
+ fmt.Scanln()
+ os.Exit(0)
+}
+
+func wait() {
+ for {
+ time.Sleep(time.Millisecond * 100)
+ }
+}
diff --git a/go.mod b/go.mod
index 8107625a2..1d7f83e7b 100644
--- a/go.mod
+++ b/go.mod
@@ -1,23 +1,38 @@
module erupe-ce
-go 1.16
+go 1.19
require (
github.com/bwmarrin/discordgo v0.23.2
- github.com/golang/mock v1.6.0 // indirect
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
- github.com/gorilla/websocket v1.4.2 // indirect
github.com/jmoiron/sqlx v1.3.4
github.com/lib/pq v1.10.4
- github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96
github.com/spf13/viper v1.8.1
+ go.uber.org/zap v1.18.1
+ golang.org/x/crypto v0.1.0
+ golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f
+ golang.org/x/text v0.4.0
+)
+
+require (
+ github.com/felixge/httpsnoop v1.0.1 // indirect
+ github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/golang/mock v1.6.0 // indirect
+ github.com/gorilla/websocket v1.4.2 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/magiconair/properties v1.8.5 // indirect
+ github.com/mitchellh/mapstructure v1.4.3 // indirect
+ github.com/pelletier/go-toml v1.9.3 // indirect
+ github.com/spf13/afero v1.6.0 // indirect
+ github.com/spf13/cast v1.3.1 // indirect
+ github.com/spf13/jwalterweatherman v1.1.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/subosito/gotenv v1.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
- go.uber.org/zap v1.18.1
- golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
- golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect
- golang.org/x/text v0.3.7
- golang.org/x/tools v0.1.8 // indirect
+ golang.org/x/sys v0.1.0 // indirect
+ gopkg.in/ini.v1 v1.62.0 // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/go.sum b/go.sum
index 9750b4732..ca2bd44a9 100644
--- a/go.sum
+++ b/go.sum
@@ -78,9 +78,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
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=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -189,9 +188,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@@ -202,9 +200,8 @@ github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaW
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
-github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -222,9 +219,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -265,7 +261,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
@@ -295,8 +290,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8=
-golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
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=
@@ -307,6 +302,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-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc=
+golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
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=
@@ -333,7 +330,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -370,8 +366,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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=
@@ -435,12 +429,9 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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=
@@ -449,9 +440,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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=
@@ -508,8 +498,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
-golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
+golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -617,9 +606,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
diff --git a/main.go b/main.go
index b9dc62538..dcb3a14b4 100644
--- a/main.go
+++ b/main.go
@@ -14,13 +14,12 @@ import (
"erupe-ce/server/entranceserver"
"erupe-ce/server/launcherserver"
"erupe-ce/server/signserver"
+
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"go.uber.org/zap"
)
-var erupeConfig *config.Config
-
// Temporary DB auto clean on startup for quick development & testing.
func cleanDB(db *sqlx.DB) {
_ = db.MustExec("DELETE FROM guild_characters")
@@ -36,27 +35,21 @@ func main() {
defer zapLogger.Sync()
logger := zapLogger.Named("main")
- logger.Info("Starting Erupe")
+ logger.Info("Starting Erupe (9.1b)")
- // Load the configuration.
- erupeConfig, err = config.LoadConfig()
- if err != nil {
- preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
- }
-
- if erupeConfig.Database.Password == "" {
+ if config.ErupeConfig.Database.Password == "" {
preventClose("Database password is blank")
}
- if net.ParseIP(erupeConfig.Host) == nil {
- ips, _ := net.LookupIP(erupeConfig.Host)
+ if net.ParseIP(config.ErupeConfig.Host) == nil {
+ ips, _ := net.LookupIP(config.ErupeConfig.Host)
for _, ip := range ips {
if ip != nil {
- erupeConfig.Host = ip.String()
+ config.ErupeConfig.Host = ip.String()
break
}
}
- if net.ParseIP(erupeConfig.Host) == nil {
+ if net.ParseIP(config.ErupeConfig.Host) == nil {
preventClose("Invalid host address")
}
}
@@ -64,10 +57,10 @@ func main() {
// Discord bot
var discordBot *discordbot.DiscordBot = nil
- if erupeConfig.Discord.Enabled {
+ if config.ErupeConfig.Discord.Enabled {
bot, err := discordbot.NewDiscordBot(discordbot.Options{
Logger: logger,
- Config: erupeConfig,
+ Config: config.ErupeConfig,
})
if err != nil {
@@ -90,11 +83,11 @@ func main() {
// Create the postgres DB pool.
connectString := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname= %s sslmode=disable",
- erupeConfig.Database.Host,
- erupeConfig.Database.Port,
- erupeConfig.Database.User,
- erupeConfig.Database.Password,
- erupeConfig.Database.Database,
+ config.ErupeConfig.Database.Host,
+ config.ErupeConfig.Database.Port,
+ config.ErupeConfig.Database.User,
+ config.ErupeConfig.Database.Password,
+ config.ErupeConfig.Database.Database,
)
db, err := sqlx.Open("postgres", connectString)
@@ -112,11 +105,9 @@ func main() {
// Clear stale data
_ = db.MustExec("DELETE FROM sign_sessions")
_ = db.MustExec("DELETE FROM servers")
- _ = db.MustExec("DELETE FROM cafe_accepted")
- _ = db.MustExec("UPDATE characters SET cafe_time=0")
// Clean the DB if the option is on.
- if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB {
+ if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.CleanDB {
logger.Info("Cleaning DB")
cleanDB(db)
logger.Info("Done cleaning DB")
@@ -126,13 +117,13 @@ func main() {
// Launcher HTTP server.
var launcherServer *launcherserver.Server
- if erupeConfig.DevMode && erupeConfig.DevModeOptions.EnableLauncherServer {
+ if config.ErupeConfig.Launcher.Enabled {
launcherServer = launcherserver.NewServer(
&launcherserver.Config{
Logger: logger.Named("launcher"),
- ErupeConfig: erupeConfig,
+ ErupeConfig: config.ErupeConfig,
DB: db,
- UseOriginalLauncherFiles: erupeConfig.Launcher.UseOriginalLauncherFiles,
+ UseOriginalLauncherFiles: config.ErupeConfig.Launcher.UseOriginalLauncherFiles,
})
err = launcherServer.Start()
if err != nil {
@@ -142,72 +133,83 @@ func main() {
}
// Entrance server.
- entranceServer := entranceserver.NewServer(
- &entranceserver.Config{
- Logger: logger.Named("entrance"),
- ErupeConfig: erupeConfig,
- DB: db,
- })
- err = entranceServer.Start()
- if err != nil {
- preventClose(fmt.Sprintf("Failed to start entrance server: %s", err.Error()))
+
+ var entranceServer *entranceserver.Server
+ if config.ErupeConfig.Entrance.Enabled {
+ entranceServer = entranceserver.NewServer(
+ &entranceserver.Config{
+ Logger: logger.Named("entrance"),
+ ErupeConfig: config.ErupeConfig,
+ DB: db,
+ })
+ err = entranceServer.Start()
+ if err != nil {
+ preventClose(fmt.Sprintf("Failed to start entrance server: %s", err.Error()))
+ }
+ logger.Info("Started entrance server")
}
- logger.Info("Started entrance server")
// Sign server.
- signServer := signserver.NewServer(
- &signserver.Config{
- Logger: logger.Named("sign"),
- ErupeConfig: erupeConfig,
- DB: db,
- })
- err = signServer.Start()
- if err != nil {
- preventClose(fmt.Sprintf("Failed to start sign server: %s", err.Error()))
+
+ var signServer *signserver.Server
+ if config.ErupeConfig.Sign.Enabled {
+ signServer = signserver.NewServer(
+ &signserver.Config{
+ Logger: logger.Named("sign"),
+ ErupeConfig: config.ErupeConfig,
+ DB: db,
+ })
+ err = signServer.Start()
+ if err != nil {
+ preventClose(fmt.Sprintf("Failed to start sign server: %s", err.Error()))
+ }
+ logger.Info("Started sign server")
}
- logger.Info("Started sign server")
var channels []*channelserver.Server
- channelQuery := ""
- si := 0
- ci := 0
- count := 1
- for _, ee := range erupeConfig.Entrance.Entries {
- for _, ce := range ee.Channels {
- sid := (4096 + si*256) + (16 + ci)
- c := *channelserver.NewServer(&channelserver.Config{
- ID: uint16(sid),
- Logger: logger.Named("channel-" + fmt.Sprint(count)),
- ErupeConfig: erupeConfig,
- DB: db,
- DiscordBot: discordBot,
- })
- if ee.IP == "" {
- c.IP = erupeConfig.Host
- } else {
- c.IP = ee.IP
- }
- c.Port = ce.Port
- err = c.Start()
- if err != nil {
- preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
- } else {
- channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, si%3)
- channels = append(channels, &c)
- logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
- ci++
- count++
+
+ if config.ErupeConfig.Channel.Enabled {
+ channelQuery := ""
+ si := 0
+ ci := 0
+ count := 1
+ for _, ee := range config.ErupeConfig.Entrance.Entries {
+ for i, ce := range ee.Channels {
+ sid := (4096 + si*256) + (16 + ci)
+ c := *channelserver.NewServer(&channelserver.Config{
+ ID: uint16(sid),
+ Logger: logger.Named("channel-" + fmt.Sprint(count)),
+ ErupeConfig: config.ErupeConfig,
+ DB: db,
+ DiscordBot: discordBot,
+ })
+ if ee.IP == "" {
+ c.IP = config.ErupeConfig.Host
+ } else {
+ c.IP = ee.IP
+ }
+ c.Port = ce.Port
+ err = c.Start()
+ if err != nil {
+ preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
+ } else {
+ channelQuery += fmt.Sprintf(`INSERT INTO servers (server_id, season, current_players, world_name, world_description, land) VALUES (%d, %d, 0, '%s', '%s', %d);`, sid, si%3, ee.Name, ee.Description, i+1)
+ channels = append(channels, &c)
+ logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
+ ci++
+ count++
+ }
}
+ ci = 0
+ si++
}
- ci = 0
- si++
- }
- // Register all servers in DB
- _ = db.MustExec(channelQuery)
+ // Register all servers in DB
+ _ = db.MustExec(channelQuery)
- for _, c := range channels {
- c.Channels = channels
+ for _, c := range channels {
+ c.Channels = channels
+ }
}
// Wait for exit or interrupt with ctrl+C.
@@ -217,12 +219,21 @@ func main() {
logger.Info("Trying to shutdown gracefully")
- for _, c := range channels {
- c.Shutdown()
+ if config.ErupeConfig.Channel.Enabled {
+ for _, c := range channels {
+ c.Shutdown()
+ }
}
- signServer.Shutdown()
- entranceServer.Shutdown()
- if erupeConfig.DevModeOptions.EnableLauncherServer {
+
+ if config.ErupeConfig.Sign.Enabled {
+ signServer.Shutdown()
+ }
+
+ if config.ErupeConfig.Entrance.Enabled {
+ entranceServer.Shutdown()
+ }
+
+ if config.ErupeConfig.Launcher.Enabled {
launcherServer.Shutdown()
}
@@ -236,7 +247,7 @@ func wait() {
}
func preventClose(text string) {
- if erupeConfig.DisableSoftCrash {
+ if config.ErupeConfig.DisableSoftCrash {
os.Exit(0)
}
fmt.Println("\nFailed to start Erupe:\n" + text)
diff --git a/migrations/000001_initial_db.down.sql b/migrations/000001_initial_db.down.sql
deleted file mode 100644
index 04f7e46f8..000000000
--- a/migrations/000001_initial_db.down.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-BEGIN;
-
-DROP TABLE IF EXISTS sign_sessions;
-DROP TABLE IF EXISTS characters;
-DROP TABLE IF EXISTS users;
-
-DROP DOMAIN IF EXISTS uint8;
-DROP DOMAIN IF EXISTS uint16;
-
-END;
\ No newline at end of file
diff --git a/migrations/000001_initial_db.up.sql b/migrations/000001_initial_db.up.sql
deleted file mode 100644
index eeac64a33..000000000
--- a/migrations/000001_initial_db.up.sql
+++ /dev/null
@@ -1,37 +0,0 @@
-BEGIN;
-
-CREATE DOMAIN uint8 AS smallint
- CHECK(VALUE >= 0 AND VALUE <= 255);
-
-CREATE DOMAIN uint16 AS integer
- CHECK(VALUE >= 0 AND VALUE <= 65536);
-
-CREATE TABLE users (
- id serial NOT NULL PRIMARY KEY,
- username text UNIQUE NOT NULL,
- password text NOT NULL,
- item_box bytea
-);
-
-CREATE TABLE characters (
- id serial NOT NULL PRIMARY KEY,
- user_id bigint REFERENCES users(id),
- is_female boolean,
- is_new_character boolean,
- small_gr_level uint8,
- gr_override_mode boolean,
- name varchar(15),
- unk_desc_string varchar(31),
- gr_override_level uint16,
- gr_override_unk0 uint8,
- gr_override_unk1 uint8
-);
-
-CREATE TABLE sign_sessions (
- id serial NOT NULL PRIMARY KEY,
- user_id bigint REFERENCES users(id),
- auth_token_num bigint,
- auth_token_str text
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000002_alter_characters.down.sql b/migrations/000002_alter_characters.down.sql
deleted file mode 100644
index c5eeb425b..000000000
--- a/migrations/000002_alter_characters.down.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN exp,
- DROP COLUMN weapon,
- DROP COLUMN last_login;
-
-END;
\ No newline at end of file
diff --git a/migrations/000002_alter_characters.up.sql b/migrations/000002_alter_characters.up.sql
deleted file mode 100644
index 8e92154dc..000000000
--- a/migrations/000002_alter_characters.up.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- ADD COLUMN exp uint16,
- ADD COLUMN weapon uint16,
- ADD COLUMN last_login integer;
-
-END;
\ No newline at end of file
diff --git a/migrations/000003_character_savedata.down.sql b/migrations/000003_character_savedata.down.sql
deleted file mode 100644
index d8d1ca8c6..000000000
--- a/migrations/000003_character_savedata.down.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN savedata;
-
-END;
\ No newline at end of file
diff --git a/migrations/000003_character_savedata.up.sql b/migrations/000003_character_savedata.up.sql
deleted file mode 100644
index d0f39a223..000000000
--- a/migrations/000003_character_savedata.up.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- ADD COLUMN savedata bytea;
-
-END;
\ No newline at end of file
diff --git a/migrations/000004_character_additional.down.sql b/migrations/000004_character_additional.down.sql
deleted file mode 100644
index 5229381cb..000000000
--- a/migrations/000004_character_additional.down.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN decomyset,
- DROP COLUMN hunternavi,
- DROP COLUMN otomoairou,
- DROP COLUMN partner,
- DROP COLUMN platebox,
- DROP COLUMN platedata,
- DROP COLUMN platemyset,
- DROP COLUMN rengokudata;
-
-END;
\ No newline at end of file
diff --git a/migrations/000004_character_additional.up.sql b/migrations/000004_character_additional.up.sql
deleted file mode 100644
index 699351796..000000000
--- a/migrations/000004_character_additional.up.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- ADD COLUMN decomyset bytea,
- ADD COLUMN hunternavi bytea,
- ADD COLUMN otomoairou bytea,
- ADD COLUMN partner bytea,
- ADD COLUMN platebox bytea,
- ADD COLUMN platedata bytea,
- ADD COLUMN platemyset bytea,
- ADD COLUMN trophy bytea,
- ADD COLUMN rengokudata bytea;
-
-END;
diff --git a/migrations/000005_quests.down.sql b/migrations/000005_quests.down.sql
deleted file mode 100644
index 60eff8f22..000000000
--- a/migrations/000005_quests.down.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN;
-
-DROP TABLE IF EXISTS questlists;
-
-END;
\ No newline at end of file
diff --git a/migrations/000005_quests.up.sql b/migrations/000005_quests.up.sql
deleted file mode 100644
index 26a02f8d1..000000000
--- a/migrations/000005_quests.up.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-BEGIN;
-
-CREATE TABLE questlists (
- ind int NOT NULL PRIMARY KEY,
- questlist bytea
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000006_mercenary.down.sql b/migrations/000006_mercenary.down.sql
deleted file mode 100644
index 54cbd957b..000000000
--- a/migrations/000006_mercenary.down.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN savemercenary;
-
-END;
\ No newline at end of file
diff --git a/migrations/000006_mercenary.up.sql b/migrations/000006_mercenary.up.sql
deleted file mode 100644
index af025dff0..000000000
--- a/migrations/000006_mercenary.up.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- ADD COLUMN savemercenary bytea;
-
-END;
\ No newline at end of file
diff --git a/migrations/000007_guilds.down.sql b/migrations/000007_guilds.down.sql
deleted file mode 100644
index 344ab854e..000000000
--- a/migrations/000007_guilds.down.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-DROP TABLE guild_characters;
-DROP TABLE guilds;
-
-END;
\ No newline at end of file
diff --git a/migrations/000007_guilds.up.sql b/migrations/000007_guilds.up.sql
deleted file mode 100644
index e327c7db3..000000000
--- a/migrations/000007_guilds.up.sql
+++ /dev/null
@@ -1,22 +0,0 @@
-BEGIN;
-
-CREATE TABLE guilds
-(
- id serial NOT NULL PRIMARY KEY,
- name varchar(24),
- created_at timestamp DEFAULT NOW(),
- leader_id int NOT NULL,
- main_motto varchar(255) DEFAULT ''
-);
-
-CREATE TABLE guild_characters
-(
- id serial NOT NULL PRIMARY KEY,
- guild_id bigint REFERENCES guilds (id),
- character_id bigint REFERENCES characters (id),
- joined_at timestamp DEFAULT NOW()
-);
-
-CREATE UNIQUE INDEX guild_character_unique_index ON guild_characters (character_id);
-
-END;
\ No newline at end of file
diff --git a/migrations/000008_guild_additional.down.sql b/migrations/000008_guild_additional.down.sql
deleted file mode 100644
index 9252a98f2..000000000
--- a/migrations/000008_guild_additional.down.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN;
-
-ALTER TABLE guilds
- DROP COLUMN rp;
-
-ALTER TABLE guild_characters
- DROP COLUMN is_applicant,
- DROP COLUMN is_sub_leader,
- DROP COLUMN order_index;
-
-END;
\ No newline at end of file
diff --git a/migrations/000008_guild_additional.up.sql b/migrations/000008_guild_additional.up.sql
deleted file mode 100644
index 7e16984d3..000000000
--- a/migrations/000008_guild_additional.up.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN;
-
-ALTER TABLE guild_characters
- ADD COLUMN is_applicant bool NOT NULL DEFAULT false,
- ADD COLUMN is_sub_leader bool NOT NULL DEFAULT false,
- ADD COLUMN order_index int NOT NULL DEFAULT 1;
-
-ALTER TABLE guilds
- ADD COLUMN rp uint16 NOT NULL DEFAULT 0;
-
-END;
diff --git a/migrations/000009_character_social.down.sql b/migrations/000009_character_social.down.sql
deleted file mode 100644
index 243ea6712..000000000
--- a/migrations/000009_character_social.down.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN restrict_guild_scout;
-
-END;
\ No newline at end of file
diff --git a/migrations/000009_character_social.up.sql b/migrations/000009_character_social.up.sql
deleted file mode 100644
index f94ce2043..000000000
--- a/migrations/000009_character_social.up.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- ADD COLUMN restrict_guild_scout bool NOT NULL DEFAULT false;
-
-END;
\ No newline at end of file
diff --git a/migrations/000010_guild_comments_festival_hall.down.sql b/migrations/000010_guild_comments_festival_hall.down.sql
deleted file mode 100644
index c86d1ee5e..000000000
--- a/migrations/000010_guild_comments_festival_hall.down.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-BEGIN;
-
-ALTER TABLE guilds
- DROP COLUMN comment,
- DROP COLUMN festival_colour,
- DROP COLUMN guild_hall;
-
-DROP TYPE festival_colour;
-
-END;
\ No newline at end of file
diff --git a/migrations/000010_guild_comments_festival_hall.up.sql b/migrations/000010_guild_comments_festival_hall.up.sql
deleted file mode 100644
index 3caec4149..000000000
--- a/migrations/000010_guild_comments_festival_hall.up.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN;
-
-CREATE TYPE festival_colour AS ENUM ('none', 'red', 'blue');
-
-ALTER TABLE guilds
- ADD COLUMN comment varchar(255) NOT NULL DEFAULT '',
- ADD COLUMN festival_colour festival_colour DEFAULT 'none',
- ADD COLUMN guild_hall int DEFAULT 0;
-
-
-END;
\ No newline at end of file
diff --git a/migrations/000011_character_points_minidata.down.sql b/migrations/000011_character_points_minidata.down.sql
deleted file mode 100644
index 557608be9..000000000
--- a/migrations/000011_character_points_minidata.down.sql
+++ /dev/null
@@ -1,24 +0,0 @@
-BEGIN;
-
-ALTER TABLE characters
- DROP COLUMN minidata,
- DROP COLUMN gacha_trial,
- DROP COLUMN gacha_prem,
- DROP COLUMN gacha_items,
- DROP COLUMN daily_time,
- DROP COLUMN frontier_points,
- DROP COLUMN netcafe_points,
- DROP COLUMN house_info,
- DROP COLUMN login_boost,
- DROP COLUMN skin_hist,
- DROP COLUMN gcp;
-
-DROP TABLE fpoint_items;
-DROP TABLE gacha_shop;
-DROP TABLE gacha_shop_items;
-DROP TABLE lucky_box_state;
-DROP TABLE stepup_state;
-DROP TABLE normal_shop_items;
-DROP TABLE shop_item_state;
-
-END;
\ No newline at end of file
diff --git a/migrations/000011_character_points_minidata.up.sql b/migrations/000011_character_points_minidata.up.sql
deleted file mode 100644
index 29baa0790..000000000
--- a/migrations/000011_character_points_minidata.up.sql
+++ /dev/null
@@ -1,100 +0,0 @@
-BEGIN;
-ALTER TABLE characters
- ADD COLUMN minidata bytea,
- ADD COLUMN gacha_trial int,
- ADD COLUMN gacha_prem int,
- ADD COLUMN gacha_items bytea,
- ADD COLUMN daily_time timestamp,
- ADD COLUMN frontier_points int,
- ADD COLUMN netcafe_points int,
- ADD COLUMN house_info bytea,
- ADD COLUMN login_boost bytea,
- ADD COLUMN skin_hist bytea,
- ADD COLUMN kouryou_point int,
- ADD COLUMN gcp int;
-
-CREATE TABLE fpoint_items
-(
- hash int,
- itemType uint8,
- itemID uint16,
- quant uint16,
- itemValue uint16,
- tradeType uint8
-);
-
-
-CREATE TABLE gacha_shop
-(
- hash bigint,
- reqGR int,
- reqHR int,
- gachaName varchar(255),
- gachaLink0 varchar(255),
- gachaLink1 varchar(255),
- gachaLink2 varchar(255),
- extraIcon int,
- gachaType int,
- hideFlag bool
-);
-
-CREATE TABLE gacha_shop_items
-(
- shophash int,
- entryType uint8,
- itemhash int UNIQUE NOT NULL,
- currType uint8,
- currNumber uint16,
- currQuant uint16,
- percentage uint16,
- rarityIcon uint8,
- rollsCount uint8,
- itemCount uint8,
- dailyLimit uint8,
- itemType int[],
- itemId int[],
- quantity int[]
-);
-
-CREATE TABLE lucky_box_state
-(
- char_id bigint REFERENCES characters (id),
- shophash int UNIQUE NOT NULL,
- used_itemhash int[]
-);
-
-
-CREATE TABLE stepup_state
-(
- char_id bigint REFERENCES characters (id),
- shophash int UNIQUE NOT NULL,
- step_progression int,
- step_time timestamp
-);
-
-CREATE TABLE normal_shop_items
-(
- shoptype int,
- shopid int,
- itemhash int UNIQUE NOT NULL,
- itemID uint16,
- Points uint16,
- TradeQuantity uint16,
- rankReqLow uint16,
- rankReqHigh uint16,
- rankReqG uint16,
- storeLevelReq uint16,
- maximumQuantity uint16,
- boughtQuantity uint16,
- roadFloorsRequired uint16,
- weeklyFatalisKills uint16
-);
-
-CREATE TABLE shop_item_state
-(
- char_id bigint REFERENCES characters (id),
- itemhash int UNIQUE NOT NULL,
- usedquantity int
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000012_loginboost_etc.down.sql b/migrations/000012_loginboost_etc.down.sql
deleted file mode 100644
index 13102b0b0..000000000
--- a/migrations/000012_loginboost_etc.down.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-BEGIN;
-
-DROP TABLE login_boost_state;
-
-END;
\ No newline at end of file
diff --git a/migrations/000012_loginboost_etc.up.sql b/migrations/000012_loginboost_etc.up.sql
deleted file mode 100644
index 4feff1790..000000000
--- a/migrations/000012_loginboost_etc.up.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-BEGIN;
-
-CREATE TABLE login_boost_state
-(
- char_id bigint REFERENCES characters (id),
- week_req uint8,
- week_count uint8,
- available bool,
- end_time int,
- CONSTRAINT id_week UNIQUE(char_id, week_req)
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000013_shop_constraints.down.sql b/migrations/000013_shop_constraints.down.sql
deleted file mode 100644
index 7b5ac1242..000000000
--- a/migrations/000013_shop_constraints.down.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-BEGIN;
-
-ALTER TABLE shop_item_state DROP CONSTRAINT shop_item_state_id_itemhash;
-ALTER TABLE shop_item_state ADD CONSTRAINT shop_item_state_itemhash_key UNIQUE (itemhash);
-
-ALTER TABLE stepup_state DROP CONSTRAINT stepup_state_id_shophash;
-ALTER TABLE stepup_state ADD CONSTRAINT stepup_state_shophash_key UNIQUE (shophash);
-
-ALTER TABLE lucky_box_state DROP CONSTRAINT lucky_box_state_id_shophash;
-ALTER TABLE lucky_box_state ADD CONSTRAINT lucky_box_state_shophash_key UNIQUE (shophash);
-
-END;
\ No newline at end of file
diff --git a/migrations/000013_shop_constraints.up.sql b/migrations/000013_shop_constraints.up.sql
deleted file mode 100644
index a48e81ac2..000000000
--- a/migrations/000013_shop_constraints.up.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-BEGIN;
-
-ALTER TABLE shop_item_state DROP CONSTRAINT shop_item_state_itemhash_key;
-ALTER TABLE shop_item_state ADD CONSTRAINT shop_item_state_id_itemhash UNIQUE(char_id, itemhash);
-
-ALTER TABLE stepup_state DROP CONSTRAINT stepup_state_shophash_key;
-ALTER TABLE stepup_state ADD CONSTRAINT stepup_state_id_shophash UNIQUE(char_id, shophash);
-
-ALTER TABLE lucky_box_state DROP CONSTRAINT lucky_box_state_shophash_key;
-ALTER TABLE lucky_box_state ADD CONSTRAINT lucky_box_state_id_shophash UNIQUE(char_id, shophash);
-
-END;
\ No newline at end of file
diff --git a/migrations/000014_guild_flags_applications.down.sql b/migrations/000014_guild_flags_applications.down.sql
deleted file mode 100644
index 6cfe86288..000000000
--- a/migrations/000014_guild_flags_applications.down.sql
+++ /dev/null
@@ -1,18 +0,0 @@
-BEGIN;
-ALTER TABLE guild_characters
- RENAME COLUMN avoid_leadership TO is_sub_leader;
-
-ALTER TABLE guild_characters
- ADD COLUMN is_applicant bool NOT NULL DEFAULT false;
-
-ALTER TABLE guilds
- DROP COLUMN icon,
- ALTER COLUMN main_motto TYPE varchar USING '',
- DROP COLUMN sub_motto;
-
-ALTER TABLE guilds
- ALTER COLUMN main_motto SET DEFAULT '';
-
-DROP TABLE guild_applications;
-DROP TYPE guild_application_type;
-END;
\ No newline at end of file
diff --git a/migrations/000014_guild_flags_applications.up.sql b/migrations/000014_guild_flags_applications.up.sql
deleted file mode 100644
index 4bc09668a..000000000
--- a/migrations/000014_guild_flags_applications.up.sql
+++ /dev/null
@@ -1,30 +0,0 @@
-BEGIN;
-CREATE TYPE guild_application_type AS ENUM ('applied', 'invited');
-
-CREATE TABLE guild_applications
-(
- id serial NOT NULL PRIMARY KEY,
- guild_id int NOT NULL REFERENCES guilds (id),
- character_id int NOT NULL REFERENCES characters (id),
- actor_id int NOT NULL REFERENCES characters (id),
- application_type guild_application_type NOT NULL,
- created_at timestamp NOT NULL DEFAULT now(),
- CONSTRAINT guild_application_character_id UNIQUE (guild_id, character_id)
-);
-
-CREATE INDEX guild_application_type_index ON guild_applications (application_type);
-
-ALTER TABLE guild_characters
- DROP COLUMN is_applicant;
-
-ALTER TABLE guild_characters
- RENAME COLUMN is_sub_leader TO avoid_leadership;
-
-ALTER TABLE guilds
- ALTER COLUMN main_motto SET DEFAULT 0;
-
-ALTER TABLE guilds
- ADD COLUMN icon bytea,
- ADD COLUMN sub_motto int DEFAULT 0,
- ALTER COLUMN main_motto TYPE int USING 0;
-END;
\ No newline at end of file
diff --git a/migrations/000015_mail.down.sql b/migrations/000015_mail.down.sql
deleted file mode 100644
index 8ed5b834f..000000000
--- a/migrations/000015_mail.down.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-BEGIN;
-DROP TABLE mail;
-END;
\ No newline at end of file
diff --git a/migrations/000015_mail.up.sql b/migrations/000015_mail.up.sql
deleted file mode 100644
index ba0dc4edd..000000000
--- a/migrations/000015_mail.up.sql
+++ /dev/null
@@ -1,19 +0,0 @@
-BEGIN;
-CREATE TABLE mail
-(
- id SERIAL NOT NULL PRIMARY KEY,
- sender_id INT NOT NULL REFERENCES characters (id),
- recipient_id INT NOT NULL REFERENCES characters (id),
- subject VARCHAR NOT NULL DEFAULT '',
- body VARCHAR NOT NULL DEFAULT '',
- read BOOL NOT NULL DEFAULT FALSE,
- attached_item_received BOOL NOT NULL DEFAULT FALSE,
- attached_item INT DEFAULT NULL,
- attached_item_amount INT NOT NULL DEFAULT 1,
- is_guild_invite BOOL NOT NULL DEFAULT FALSE,
- created_at TIMESTAMP NOT NULL DEFAULT NOW(),
- deleted BOOL NOT NULL DEFAULT FALSE
-);
-
-CREATE INDEX mail_recipient_deleted_created_id_index ON mail (recipient_id, deleted, created_at DESC, id DESC);
-END;
\ No newline at end of file
diff --git a/migrations/000016_server.down.sql b/migrations/000016_server.down.sql
deleted file mode 100644
index a5fb2d6b0..000000000
--- a/migrations/000016_server.down.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-BEGIN;
-DROP TABLE public.servers;
-END;
\ No newline at end of file
diff --git a/migrations/000016_server.up.sql b/migrations/000016_server.up.sql
deleted file mode 100644
index f6a7798e7..000000000
--- a/migrations/000016_server.up.sql
+++ /dev/null
@@ -1,21 +0,0 @@
-BEGIN;
--- Table: public.servers
-
--- DROP TABLE IF EXISTS public.servers;
-
-CREATE TABLE IF NOT EXISTS public.servers
-(
- server_id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
- server_name text COLLATE pg_catalog."default",
- season integer,
- current_players integer,
- event_id integer,
- event_expiration integer,
- CONSTRAINT servers_pkey PRIMARY KEY (server_id)
-)
-
-TABLESPACE pg_default;
-
-ALTER TABLE IF EXISTS public.servers
- OWNER to postgres;
-END;
\ No newline at end of file
diff --git a/migrations/000017_account.down.sql b/migrations/000017_account.down.sql
deleted file mode 100644
index c52957af0..000000000
--- a/migrations/000017_account.down.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-BEGIN;
-DROP TABLE public.account_ban;
-DROP TABLE public.account_history;
-DROP TABLE public.account_moderation;
-DROP TABLE public.account_sub;
-END;
\ No newline at end of file
diff --git a/migrations/000017_account.up.sql b/migrations/000017_account.up.sql
deleted file mode 100644
index f636d619f..000000000
--- a/migrations/000017_account.up.sql
+++ /dev/null
@@ -1,45 +0,0 @@
-BEGIN;
-
-CREATE TABLE IF NOT EXISTS public.account_ban
-(
- user_id integer NOT NULL,
- title text COLLATE pg_catalog."default",
- reason text COLLATE pg_catalog."default",
- date text COLLATE pg_catalog."default",
- pass_origin text COLLATE pg_catalog."default",
- pass_block text COLLATE pg_catalog."default",
- CONSTRAINT ban_pkey PRIMARY KEY (user_id)
-);
-
-CREATE TABLE IF NOT EXISTS public.account_history
-(
- report_id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
- user_id integer,
- title text COLLATE pg_catalog."default",
- reason text COLLATE pg_catalog."default",
- date date,
- CONSTRAINT account_history_pkey PRIMARY KEY (report_id)
-);
-
-CREATE TABLE IF NOT EXISTS public.account_moderation
-(
- id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
- username text COLLATE pg_catalog."default",
- password text COLLATE pg_catalog."default",
- type text COLLATE pg_catalog."default",
- CONSTRAINT account_moderation_pkey PRIMARY KEY (id)
-);
-
-CREATE TABLE IF NOT EXISTS public.account_sub
-(
- id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
- discord_id text COLLATE pg_catalog."default",
- erupe_account text COLLATE pg_catalog."default",
- erupe_password text COLLATE pg_catalog."default",
- date_inscription date,
- country text COLLATE pg_catalog."default",
- presentation text COLLATE pg_catalog."default",
- CONSTRAINT account_auth_pkey PRIMARY KEY (id)
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000018_event_week.down.sql b/migrations/000018_event_week.down.sql
deleted file mode 100644
index 756df1f22..000000000
--- a/migrations/000018_event_week.down.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-BEGIN;
-DROP TABLE public.event_week;
-END;
\ No newline at end of file
diff --git a/migrations/000018_event_week.up.sql b/migrations/000018_event_week.up.sql
deleted file mode 100644
index f536871f2..000000000
--- a/migrations/000018_event_week.up.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-BEGIN;
-
-CREATE TABLE IF NOT EXISTS public.event_week
-(
- id integer NOT NULL,
- event_id integer NOT NULL,
- date_expiration integer NOT NULL,
- CONSTRAINT event_week_pkey PRIMARY KEY (id)
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000019_gook.down.sql b/migrations/000019_gook.down.sql
deleted file mode 100644
index a4b544e9a..000000000
--- a/migrations/000019_gook.down.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-BEGIN;
-DROP TABLE public.gook;
-END;
\ No newline at end of file
diff --git a/migrations/000019_gook.up.sql b/migrations/000019_gook.up.sql
deleted file mode 100644
index fe5c051c0..000000000
--- a/migrations/000019_gook.up.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-BEGIN;
-
-CREATE TABLE IF NOT EXISTS public.gook
-(
- id serial NOT NULL PRIMARY KEY,
- gook0 bytea,
- gook1 bytea,
- gook2 bytea,
- gook3 bytea,
- gook4 bytea,
- gook5 bytea,
- gook0status boolean,
- gook1status boolean,
- gook2status boolean,
- gook3status boolean,
- gook4status boolean,
- gook5status boolean
-);
-
-END;
\ No newline at end of file
diff --git a/migrations/000020_history.down.sql b/migrations/000020_history.down.sql
deleted file mode 100644
index 623bd53c8..000000000
--- a/migrations/000020_history.down.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-BEGIN;
-DROP TABLE public.history;
-END;
\ No newline at end of file
diff --git a/migrations/000020_history.up.sql b/migrations/000020_history.up.sql
deleted file mode 100644
index 96d6b36c8..000000000
--- a/migrations/000020_history.up.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-BEGIN;
-
-CREATE TABLE IF NOT EXISTS public.history
-(
- user_id integer,
- admin_id integer,
- report_id integer NOT NULL,
- title text COLLATE pg_catalog."default",
- reason text COLLATE pg_catalog."default",
- CONSTRAINT history_pkey PRIMARY KEY (report_id)
-);
-
-END;
\ No newline at end of file
diff --git a/network/clientctx/clientcontext.go b/network/clientctx/clientcontext.go
index 0f890f6d7..021ae3299 100644
--- a/network/clientctx/clientcontext.go
+++ b/network/clientctx/clientcontext.go
@@ -1,8 +1,4 @@
package clientctx
-import "erupe-ce/common/stringsupport"
-
// ClientContext holds contextual data required for packet encoding/decoding.
-type ClientContext struct {
- StrConv *stringsupport.StringConverter
-}
+type ClientContext struct{} // Unused
diff --git a/network/mhfpacket/msg_mhf_enumerate_guild.go b/network/mhfpacket/msg_mhf_enumerate_guild.go
index 65edbc555..691f8241f 100644
--- a/network/mhfpacket/msg_mhf_enumerate_guild.go
+++ b/network/mhfpacket/msg_mhf_enumerate_guild.go
@@ -32,6 +32,7 @@ type MsgMhfEnumerateGuild struct {
AckHandle uint32
Type EnumerateGuildType
Page uint8
+ Sorting bool
RawDataPayload []byte
}
@@ -45,6 +46,8 @@ func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.AckHandle = bf.ReadUint32()
m.Type = EnumerateGuildType(bf.ReadUint8())
m.Page = bf.ReadUint8()
+ m.Sorting = bf.ReadBool()
+ _ = bf.ReadUint8()
m.RawDataPayload = bf.DataFromCurrent()
bf.Seek(-2, io.SeekEnd)
return nil
diff --git a/network/mhfpacket/msg_mhf_enumerate_house.go b/network/mhfpacket/msg_mhf_enumerate_house.go
index 9bd1f30ef..da6a25de7 100644
--- a/network/mhfpacket/msg_mhf_enumerate_house.go
+++ b/network/mhfpacket/msg_mhf_enumerate_house.go
@@ -29,8 +29,10 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.CharID = bf.ReadUint32()
m.Method = bf.ReadUint8()
m.Unk = bf.ReadUint16()
- _ = bf.ReadUint8() // len
- m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ lenName := bf.ReadUint8()
+ if lenName > 0 {
+ m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ }
return nil
}
diff --git a/network/mhfpacket/msg_mhf_get_gem_info.go b/network/mhfpacket/msg_mhf_get_gem_info.go
index 516aea6be..36eb7948e 100644
--- a/network/mhfpacket/msg_mhf_get_gem_info.go
+++ b/network/mhfpacket/msg_mhf_get_gem_info.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"
)
// MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO
-type MsgMhfGetGemInfo struct{}
+type MsgMhfGetGemInfo struct {
+ AckHandle uint32
+ Unk uint32
+ Unk1 []byte
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetGemInfo) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfGetGemInfo) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
- return errors.New("NOT IMPLEMENTED")
+ m.AckHandle = bf.ReadUint32()
+ m.Unk = bf.ReadUint32()
+ m.Unk1 = bf.ReadBytes(24)
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/network/mhfpacket/msg_mhf_get_tower_info.go b/network/mhfpacket/msg_mhf_get_tower_info.go
index 37ac8417b..a0b686485 100644
--- a/network/mhfpacket/msg_mhf_get_tower_info.go
+++ b/network/mhfpacket/msg_mhf_get_tower_info.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"
)
// The server sends different responses based on these values.
@@ -13,7 +13,7 @@ const (
TowerInfoTypeUnk0 = iota
TowerInfoTypeTowerRankPoint
TowerInfoTypeGetOwnTowerSkill
- TowerInfoTypeUnk3
+ TowerInfoTypeGetOwnTowerLevelV3
TowerInfoTypeTowerTouhaHistory
TowerInfoTypeUnk5
)
diff --git a/network/mhfpacket/msg_mhf_guild_huntdata.go b/network/mhfpacket/msg_mhf_guild_huntdata.go
index b744cce23..9e114c968 100644
--- a/network/mhfpacket/msg_mhf_guild_huntdata.go
+++ b/network/mhfpacket/msg_mhf_guild_huntdata.go
@@ -1,17 +1,18 @@
package mhfpacket
-import (
- "errors"
+import (
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
// MsgMhfGuildHuntdata represents the MSG_MHF_GUILD_HUNTDATA
-type MsgMhfGuildHuntdata struct{
- AckHandle uint32
- Unk0 uint8
+type MsgMhfGuildHuntdata struct {
+ AckHandle uint32
+ Operation uint8
+ GuildID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -22,7 +23,10 @@ func (m *MsgMhfGuildHuntdata) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfGuildHuntdata) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
- m.Unk0 = bf.ReadUint8()
+ m.Operation = bf.ReadUint8()
+ if m.Operation == 1 {
+ m.GuildID = bf.ReadUint32()
+ }
return nil
}
diff --git a/network/mhfpacket/msg_mhf_info_joint.go b/network/mhfpacket/msg_mhf_info_joint.go
index 6426b9211..17e468c7c 100644
--- a/network/mhfpacket/msg_mhf_info_joint.go
+++ b/network/mhfpacket/msg_mhf_info_joint.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"
)
// MsgMhfInfoJoint represents the MSG_MHF_INFO_JOINT
-type MsgMhfInfoJoint struct{}
+type MsgMhfInfoJoint struct {
+ AckHandle uint32
+ AllianceID uint32
+ Unk uint32
+}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfInfoJoint) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfInfoJoint) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfInfoJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
- return errors.New("NOT IMPLEMENTED")
+ m.AckHandle = bf.ReadUint32()
+ m.AllianceID = bf.ReadUint32()
+ m.Unk = bf.ReadUint32()
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/network/mhfpacket/msg_mhf_operate_guild.go b/network/mhfpacket/msg_mhf_operate_guild.go
index ea986cda2..a79719290 100644
--- a/network/mhfpacket/msg_mhf_operate_guild.go
+++ b/network/mhfpacket/msg_mhf_operate_guild.go
@@ -1,39 +1,43 @@
package mhfpacket
import (
- "errors"
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
type OperateGuildAction uint8
const (
- OPERATE_GUILD_DISBAND = 0x01
- OPERATE_GUILD_APPLY = 0x02
- OPERATE_GUILD_LEAVE = 0x03
- OPERATE_GUILD_RESIGN = 0x04
- OPERATE_GUILD_SET_APPLICATION_DENY = 0x05
- OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06
- OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07
- OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08
- OPERATE_GUILD_UPDATE_COMMENT = 0x09
- OPERATE_GUILD_DONATE_RANK = 0x0a
- OPERATE_GUILD_UPDATE_MOTTO = 0x0b
- OPERATE_GUILD_RENAME_PUGI_1 = 0x0c
- OPERATE_GUILD_RENAME_PUGI_2 = 0x0d
- OPERATE_GUILD_RENAME_PUGI_3 = 0x0e
- OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f
- OPERATE_GUILD_CHANGE_PUGI_2 = 0x10
- OPERATE_GUILD_CHANGE_PUGI_3 = 0x11
- // pugi something
- OPERATE_GUILD_DONATE_EVENT = 0x15
- // pugi something
- OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19
- OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a
- OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b
+ OPERATE_GUILD_DISBAND = 0x01
+ OPERATE_GUILD_APPLY = 0x02
+ OPERATE_GUILD_LEAVE = 0x03
+ OPERATE_GUILD_RESIGN = 0x04
+ OPERATE_GUILD_SET_APPLICATION_DENY = 0x05
+ OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06
+ OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07
+ OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08
+ OPERATE_GUILD_UPDATE_COMMENT = 0x09
+ OPERATE_GUILD_DONATE_RANK = 0x0a
+ OPERATE_GUILD_UPDATE_MOTTO = 0x0b
+ OPERATE_GUILD_RENAME_PUGI_1 = 0x0c
+ OPERATE_GUILD_RENAME_PUGI_2 = 0x0d
+ OPERATE_GUILD_RENAME_PUGI_3 = 0x0e
+ OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f
+ OPERATE_GUILD_CHANGE_PUGI_2 = 0x10
+ OPERATE_GUILD_CHANGE_PUGI_3 = 0x11
+ OPERATE_GUILD_UNLOCK_OUTFIT = 0x12
+ // 0x13 Unk
+ // 0x14 Unk
+ OPERATE_GUILD_DONATE_EVENT = 0x15
+ OPERATE_GUILD_EVENT_EXCHANGE = 0x16
+ // 0x17 Unk
+ // 0x18 Unk
+ OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19
+ OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a
+ OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b
)
// MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD
@@ -41,7 +45,8 @@ type MsgMhfOperateGuild struct {
AckHandle uint32
GuildID uint32
Action OperateGuildAction
- UnkData []byte
+ Data1 *byteframe.ByteFrame
+ Data2 *byteframe.ByteFrame
}
// Opcode returns the ID associated with this packet type.
@@ -54,8 +59,9 @@ func (m *MsgMhfOperateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
m.AckHandle = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
m.Action = OperateGuildAction(bf.ReadUint8())
- m.UnkData = 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_operate_joint.go b/network/mhfpacket/msg_mhf_operate_joint.go
index 9733a2278..1fa360d01 100644
--- a/network/mhfpacket/msg_mhf_operate_joint.go
+++ b/network/mhfpacket/msg_mhf_operate_joint.go
@@ -1,28 +1,28 @@
package mhfpacket
import (
- "errors"
+ "errors"
- "erupe-ce/network/clientctx"
- "erupe-ce/network"
"erupe-ce/common/byteframe"
+ "erupe-ce/network"
+ "erupe-ce/network/clientctx"
)
type OperateJointAction uint8
const (
- OPERATE_JOINT_DISBAND = 0x01
- OPERATE_JOINT_LEAVE = 0x03
- OPERATE_JOINT_KICK = 0x09
+ OPERATE_JOINT_DISBAND = 0x01
+ OPERATE_JOINT_LEAVE = 0x03
+ OPERATE_JOINT_KICK = 0x09
)
// MsgMhfOperateJoint represents the MSG_MHF_OPERATE_JOINT
type MsgMhfOperateJoint struct {
- AckHandle uint32
- AllianceID uint32
- GuildID uint32
- Action OperateJointAction
- UnkData []byte
+ AckHandle uint32
+ AllianceID uint32
+ GuildID uint32
+ Action OperateJointAction
+ UnkData *byteframe.ByteFrame
}
// Opcode returns the ID associated with this packet type.
@@ -32,13 +32,13 @@ func (m *MsgMhfOperateJoint) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfOperateJoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
- m.AckHandle = bf.ReadUint32()
- m.AllianceID = bf.ReadUint32()
- m.GuildID = bf.ReadUint32()
- m.Action = OperateJointAction(bf.ReadUint8())
- m.UnkData = bf.DataFromCurrent()
- bf.Seek(int64(len(bf.Data()) - 2), 0)
- return nil
+ m.AckHandle = bf.ReadUint32()
+ 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)
+ return nil
}
// Build builds a binary packet from the current data.
diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go
index 862462e59..536d35f18 100644
--- a/network/mhfpacket/msg_sys_terminal_log.go
+++ b/network/mhfpacket/msg_sys_terminal_log.go
@@ -1,17 +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"
)
// TerminalLogEntry represents an entry in the MSG_SYS_TERMINAL_LOG packet.
type TerminalLogEntry struct {
- // Unknown fields
- U0, U1, U2, U3, U4, U5, U6, U7, U8 uint32
+ Index uint32
+ Type1 uint8
+ Type2 uint8
+ Data []int16
}
// MsgSysTerminalLog represents the MSG_SYS_TERMINAL_LOG
@@ -37,15 +39,12 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
for i := 0; i < int(m.EntryCount); i++ {
e := &TerminalLogEntry{}
- e.U0 = bf.ReadUint32()
- e.U1 = bf.ReadUint32()
- e.U2 = bf.ReadUint32()
- e.U3 = bf.ReadUint32()
- e.U4 = bf.ReadUint32()
- e.U5 = bf.ReadUint32()
- e.U6 = bf.ReadUint32()
- e.U7 = bf.ReadUint32()
- e.U8 = bf.ReadUint32()
+ e.Index = bf.ReadUint32()
+ e.Type1 = bf.ReadUint8()
+ e.Type2 = bf.ReadUint8()
+ for j := 0; j < 15; j++ {
+ e.Data = append(e.Data, bf.ReadInt16())
+ }
m.Entries = append(m.Entries, e)
}
diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go
index b343dd0c4..0702a5b3e 100644
--- a/network/mhfpacket/msg_sys_update_right.go
+++ b/network/mhfpacket/msg_sys_update_right.go
@@ -2,6 +2,8 @@ package mhfpacket
import (
"errors"
+ ps "erupe-ce/common/pascalstring"
+ "golang.org/x/exp/slices"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -34,6 +36,12 @@ type ClientRight struct {
Timestamp uint32
}
+type Course struct {
+ Aliases []string
+ ID uint16
+ Value uint32
+}
+
// MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT
type MsgSysUpdateRight struct {
ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value.
@@ -63,9 +71,40 @@ func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.Client
bf.WriteUint16(v.Unk0)
bf.WriteUint32(v.Timestamp)
}
-
- bf.WriteUint16(m.UnkSize) // String of upto 0x800 bytes, update client login token / password in the game's launcherstate struct.
- //bf.WriteBytes(m.UpdatedClientLoginToken)
-
+ ps.Uint16(bf, "", false) // update client login token / password in the game's launcherstate struct
return nil
}
+
+func Courses() []Course {
+ var courses = []Course{
+ {[]string{"Trial", "TL"}, 1, 0x00000002},
+ {[]string{"HunterLife", "HL"}, 2, 0x00000004},
+ {[]string{"ExtraA", "Extra", "EX"}, 3, 0x00000008},
+ {[]string{"ExtraB"}, 4, 0x00000010},
+ {[]string{"Mobile"}, 5, 0x00000020},
+ {[]string{"Premium"}, 6, 0x00000040},
+ {[]string{"Pallone"}, 7, 0x00000080},
+ {[]string{"Assist", "Legend", "Rasta"}, 8, 0x00000100}, // Legend
+ {[]string{"Netcafe", "N", "Cafe"}, 9, 0x00000200},
+ {[]string{"Hiden", "Secret"}, 10, 0x00000400}, // Secret
+ {[]string{"HunterSupport", "HunterAid", "Support", "Royal", "Aid"}, 11, 0x00000800}, // Royal
+ {[]string{"NetcafeBoost", "NBoost", "Boost"}, 12, 0x00001000},
+ }
+ return courses
+}
+
+// GetCourseStruct returns a slice of Course(s) from a rights integer
+func GetCourseStruct(rights uint32) []Course {
+ var resp []Course
+ s := Courses()
+ slices.SortStableFunc(s, func(i, j Course) bool {
+ return i.ID > j.ID
+ })
+ for _, course := range s {
+ if rights-course.Value < 0x80000000 {
+ resp = append(resp, course)
+ rights -= course.Value
+ }
+ }
+ return resp
+}
diff --git a/patch-schema/active-feature.sql b/patch-schema/active-feature.sql
new file mode 100644
index 000000000..f8b835100
--- /dev/null
+++ b/patch-schema/active-feature.sql
@@ -0,0 +1,9 @@
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS public.feature_weapon
+(
+ start_time timestamp without time zone NOT NULL,
+ featured integer NOT NULL
+);
+
+END;
\ No newline at end of file
diff --git a/patch-schema/guild-poogie-outfits.sql b/patch-schema/guild-poogie-outfits.sql
new file mode 100644
index 000000000..b6ce2e4fa
--- /dev/null
+++ b/patch-schema/guild-poogie-outfits.sql
@@ -0,0 +1,15 @@
+BEGIN;
+
+ALTER TABLE IF EXISTS public.guilds
+ ADD COLUMN IF NOT EXISTS pugi_outfit_1 int NOT NULL DEFAULT 0;
+
+ALTER TABLE IF EXISTS public.guilds
+ ADD COLUMN IF NOT EXISTS pugi_outfit_2 int NOT NULL DEFAULT 0;
+
+ALTER TABLE IF EXISTS public.guilds
+ ADD COLUMN IF NOT EXISTS pugi_outfit_3 int NOT NULL DEFAULT 0;
+
+ALTER TABLE IF EXISTS public.guilds
+ ADD COLUMN IF NOT EXISTS pugi_outfits int NOT NULL DEFAULT 0;
+
+END;
\ No newline at end of file
diff --git a/patch-schema/netcafe-2.sql b/patch-schema/netcafe-2.sql
new file mode 100644
index 000000000..d2a1f0763
--- /dev/null
+++ b/patch-schema/netcafe-2.sql
@@ -0,0 +1,6 @@
+BEGIN;
+
+ALTER TABLE IF EXISTS public.characters
+ ADD COLUMN cafe_reset timestamp without time zone;
+
+END;
\ No newline at end of file
diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql
index e284742ce..563e10fb0 100644
--- a/patch-schema/netcafe.sql
+++ b/patch-schema/netcafe.sql
@@ -27,14 +27,4 @@ CREATE TABLE IF NOT EXISTS public.cafe_accepted
character_id integer NOT NULL
);
-INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity)
-VALUES
- (1800, 17, 0, 250),
- (3600, 17, 0, 500),
- (7200, 17, 0, 1000),
- (10800, 17, 0, 1500),
- (18000, 17, 0, 1750),
- (28800, 17, 0, 3000),
- (43200, 17, 0, 4000);
-
END;
\ No newline at end of file
diff --git a/patch-schema/persistent-house.sql b/patch-schema/persistent-house.sql
new file mode 100644
index 000000000..43a02da91
--- /dev/null
+++ b/patch-schema/persistent-house.sql
@@ -0,0 +1,37 @@
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS public.user_binary
+(
+ id serial NOT NULL PRIMARY KEY,
+ type2 bytea,
+ type3 bytea,
+ house_tier bytea,
+ house_state int,
+ house_password text,
+ house_data bytea,
+ house_furniture bytea,
+ bookshelf bytea,
+ gallery bytea,
+ tore bytea,
+ garden bytea,
+ mission bytea
+);
+
+-- Create entries for existing users
+INSERT INTO public.user_binary (id) SELECT c.id FROM characters c;
+
+-- Copy existing data
+UPDATE public.user_binary
+ SET house_furniture = (SELECT house FROM characters WHERE user_binary.id = characters.id);
+
+UPDATE public.user_binary
+ SET mission = (SELECT trophy FROM characters WHERE user_binary.id = characters.id);
+
+-- Drop old data location
+ALTER TABLE public.characters
+ DROP COLUMN house;
+
+ALTER TABLE public.characters
+ DROP COLUMN trophy;
+
+END;
\ No newline at end of file
diff --git a/patch-schema/servers_info.sql b/patch-schema/servers_info.sql
new file mode 100644
index 000000000..06389b6ed
--- /dev/null
+++ b/patch-schema/servers_info.sql
@@ -0,0 +1,23 @@
+--adds world_name and land columns
+BEGIN;
+
+CREATE TABLE IF NOT EXISTS public.servers
+(
+ server_id integer NOT NULL,
+ season integer NOT NULL,
+ current_players integer NOT NULL,
+ world_name text COLLATE pg_catalog."default",
+ world_description text,
+ land integer
+);
+
+ALTER TABLE public.servers
+ ADD COLUMN IF NOT EXISTS land integer;
+
+ALTER TABLE public.servers
+ ADD COLUMN IF NOT EXISTS world_name text COLLATE pg_catalog."default";
+
+ALTER TABLE public.servers
+ ADD COLUMN IF NOT EXISTS world_description text;
+
+END;
\ No newline at end of file
diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go
index 6c49ba0af..edd2dc33c 100644
--- a/server/channelserver/handlers.go
+++ b/server/channelserver/handlers.go
@@ -6,7 +6,6 @@ import (
"erupe-ce/common/stringsupport"
"fmt"
"io"
- "math"
"net"
"strings"
"time"
@@ -74,26 +73,16 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
}
func updateRights(s *Session) {
- s.rights = uint32(0x0E)
- s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&s.rights)
-
- rights := make([]mhfpacket.ClientRight, 0)
- tempRights := s.rights
- for i := 30; i > 0; i-- {
- right := uint32(math.Pow(2, float64(i)))
- if tempRights-right < 0x80000000 {
- if i == 1 {
- continue
- }
- rights = append(rights, mhfpacket.ClientRight{ID: uint16(i), Timestamp: 0x70DB59F0})
- tempRights -= right
- }
+ rightsInt := uint32(0x0E)
+ s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt)
+ s.courses = mhfpacket.GetCourseStruct(rightsInt)
+ rights := []mhfpacket.ClientRight{{1, 0, 0}}
+ for _, course := range s.courses {
+ rights = append(rights, mhfpacket.ClientRight{ID: course.ID, Timestamp: 0x70DB59F0})
}
- rights = append(rights, mhfpacket.ClientRight{ID: 1, Timestamp: 0})
-
update := &mhfpacket.MsgSysUpdateRight{
ClientRespAckHandle: 0,
- Bitfield: s.rights,
+ Bitfield: rightsInt,
Rights: rights,
UnkSize: 0,
}
@@ -118,9 +107,14 @@ func handleMsgSysAck(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysTerminalLog)
+ for i := range pkt.Entries {
+ s.server.logger.Info("SysTerminalLog",
+ zap.Uint8("Type1", pkt.Entries[i].Type1),
+ zap.Uint8("Type2", pkt.Entries[i].Type2),
+ zap.Int16s("Data", pkt.Entries[i].Data))
+ }
resp := byteframe.NewByteFrame()
-
- resp.WriteUint32(0x98bd51a9) // LogID to use for requests after this.
+ resp.WriteUint32(pkt.LogID + 1) // LogID to use for requests after this.
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
}
@@ -177,11 +171,29 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
}
func logoutPlayer(s *Session) {
+ s.server.Lock()
if _, exists := s.server.sessions[s.rawConn]; exists {
delete(s.server.sessions, s.rawConn)
- s.rawConn.Close()
- } else {
- return // Prevent re-running logout logic on real logouts
+ }
+ s.rawConn.Close()
+ s.server.Unlock()
+
+ for _, stage := range s.server.stages {
+ // Tell sessions registered to disconnecting players quest to unregister
+ if stage.host != nil && stage.host.charID == s.charID {
+ for _, sess := range s.server.sessions {
+ for rSlot := range stage.reservedClientSlots {
+ if sess.charID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" {
+ sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
+ }
+ }
+ }
+ }
+ for session := range stage.clients {
+ if session.charID == s.charID {
+ delete(stage.clients, session)
+ }
+ }
}
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
@@ -201,7 +213,7 @@ func logoutPlayer(s *Session) {
timePlayed += sessionTime
var rpGained int
- if s.rights >= 0x40000000 { // N Course
+ if s.FindCourse("Netcafe").ID != 0 {
rpGained = timePlayed / 900
timePlayed = timePlayed % 900
} else {
@@ -235,17 +247,11 @@ func logoutPlayer(s *Session) {
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
- panic(err)
+ s.logger.Error("Failed to get savedata")
+ return
}
saveData.RP += uint16(rpGained)
- transaction, err := s.server.db.Begin()
- err = saveData.Save(s, transaction)
- if err != nil {
- transaction.Rollback()
- panic(err)
- } else {
- transaction.Commit()
- }
+ saveData.Save(s)
}
func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {}
@@ -754,6 +760,7 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(gook.NameLen)
bf.WriteBytes(gook.Name)
s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=$1 WHERE id=$2", gook.Index), bf.Data(), s.charID)
+ dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", gook.Index))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
@@ -1749,6 +1756,7 @@ func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
byteInd := (bit / 8)
bitInByte := bit % 8
data[startByte+byteInd] |= bits.Reverse8((1 << uint(bitInByte)))
+ dumpSaveData(s, data, "skinhist")
_, err = s.server.db.Exec("UPDATE characters SET skin_hist=$1 WHERE id=$2", data, s.charID)
if err != nil {
panic(err)
@@ -1758,8 +1766,9 @@ func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfGetUdShopCoin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdShopCoin)
- data, _ := hex.DecodeString("0000000000000001")
- doAckBufSucceed(s, pkt.AckHandle, data)
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint32(0)
+ doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {}
@@ -1778,6 +1787,7 @@ func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata)
+ dumpSaveData(s, pkt.RawDataPayload, "minidata")
_, err := s.server.db.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update minidata in db", zap.Error(err))
@@ -1792,9 +1802,7 @@ func handleMsgMhfGetLobbyCrowd(s *Session, p mhfpacket.MHFPacket) {
// It can be worried about later if we ever get to the point where there are
// full servers to actually need to migrate people from and empty ones to
pkt := p.(*mhfpacket.MsgMhfGetLobbyCrowd)
- blankData := make([]byte, 0x320)
- doAckBufSucceed(s, pkt.AckHandle, blankData)
- doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x320))
}
func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {
diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go
index c7d826429..15b01f510 100644
--- a/server/channelserver/handlers_cafe.go
+++ b/server/channelserver/handlers_cafe.go
@@ -4,6 +4,7 @@ import (
"erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring"
"erupe-ce/network/mhfpacket"
+ "fmt"
"go.uber.org/zap"
"io"
"time"
@@ -71,15 +72,22 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetCafeDuration)
bf := byteframe.NewByteFrame()
+ var cafeReset time.Time
+ err := s.server.db.QueryRow(`SELECT cafe_reset FROM characters WHERE id=$1`, s.charID).Scan(&cafeReset)
+ if Time_Current_Adjusted().After(cafeReset) {
+ cafeReset = TimeWeekNext()
+ s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2; DELETE FROM cafe_accepted WHERE character_id=$2`, cafeReset, s.charID)
+ }
+
var cafeTime uint32
- err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
+ err = s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
if err != nil {
panic(err)
}
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
bf.WriteUint32(cafeTime) // Total cafe time
bf.WriteUint16(0)
- ps.Uint16(bf, "Resets at next maintenance", true)
+ ps.Uint16(bf, fmt.Sprintf("Resets on %s %d", cafeReset.Month().String(), cafeReset.Day()), true)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go
index 35538e0fb..64666d9af 100644
--- a/server/channelserver/handlers_cast_binary.go
+++ b/server/channelserver/handlers_cast_binary.go
@@ -1,21 +1,26 @@
package channelserver
import (
+ "encoding/hex"
+ "erupe-ce/common/byteframe"
+ "erupe-ce/config"
+ "erupe-ce/network/binpacket"
+ "erupe-ce/network/mhfpacket"
"fmt"
+ "golang.org/x/exp/slices"
"math"
"math/rand"
"strings"
"time"
- "erupe-ce/common/byteframe"
- "erupe-ce/network/binpacket"
- "erupe-ce/network/mhfpacket"
+ "go.uber.org/zap"
)
// MSG_SYS_CAST[ED]_BINARY types enum
const (
BinaryMessageTypeState = 0
BinaryMessageTypeChat = 1
+ BinaryMessageTypeQuest = 2
BinaryMessageTypeData = 3
BinaryMessageTypeMailNotify = 4
BinaryMessageTypeEmote = 6
@@ -29,6 +34,28 @@ const (
BroadcastTypeWorld = 0x0a
)
+var commands map[string]config.Command
+
+func init() {
+ commands = make(map[string]config.Command)
+ zapLogger, _ := zap.NewDevelopment()
+ defer zapLogger.Sync()
+ logger := zapLogger.Named("commands")
+ cmds := config.ErupeConfig.Commands
+ for _, cmd := range cmds {
+ commands[cmd.Name] = cmd
+ if cmd.Enabled {
+ logger.Info(fmt.Sprintf("%s command is enabled, prefix: %s", cmd.Name, cmd.Prefix))
+ } else {
+ logger.Info(fmt.Sprintf("%s command is disabled", cmd.Name))
+ }
+ }
+}
+
+func sendDisabledCommandMessage(s *Session, cmd config.Command) {
+ sendServerChatMessage(s, fmt.Sprintf("%s command is disabled", cmd.Name))
+}
+
func sendServerChatMessage(s *Session, message string) {
// Make the inside of the casted binary
bf := byteframe.NewByteFrame()
@@ -64,6 +91,18 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
}
}
+ if s.server.erupeConfig.DevModeOptions.QuestDebugTools == true && s.server.erupeConfig.DevMode {
+ if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
+ // This is only correct most of the time
+ tmp.ReadBytes(20)
+ tmp.SetLE()
+ x := tmp.ReadFloat32()
+ y := tmp.ReadFloat32()
+ z := tmp.ReadFloat32()
+ s.logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z}))
+ }
+ }
+
// Parse out the real casted binary payload
var msgBinTargeted *binpacket.MsgBinTargeted
var authorLen, msgLen uint16
@@ -165,167 +204,249 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
fmt.Printf("Got chat message: %+v\n", chatMessage)
- // Flush all objects and users and reload
- if strings.HasPrefix(chatMessage.Message, "!reload") {
- sendServerChatMessage(s, "Reloading players...")
- var temp mhfpacket.MHFPacket
- deleteNotif := byteframe.NewByteFrame()
- for _, object := range s.stage.objects {
- if object.ownerCharID == s.charID {
- continue
- }
- temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
- deleteNotif.WriteUint16(uint16(temp.Opcode()))
- temp.Build(deleteNotif, s.clientContext)
- }
- for _, session := range s.server.sessions {
- if s == session {
- continue
- }
- temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID}
- deleteNotif.WriteUint16(uint16(temp.Opcode()))
- temp.Build(deleteNotif, s.clientContext)
- }
- deleteNotif.WriteUint16(0x0010)
- s.QueueSend(deleteNotif.Data())
- time.Sleep(500 * time.Millisecond)
- reloadNotif := byteframe.NewByteFrame()
- for _, session := range s.server.sessions {
- if s == session {
- continue
- }
- temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
- reloadNotif.WriteUint16(uint16(temp.Opcode()))
- temp.Build(reloadNotif, s.clientContext)
- for i := 0; i < 3; i++ {
- temp = &mhfpacket.MsgSysNotifyUserBinary{
- CharID: session.charID,
- BinaryType: uint8(i + 1),
- }
- reloadNotif.WriteUint16(uint16(temp.Opcode()))
- temp.Build(reloadNotif, s.clientContext)
- }
- }
- for _, obj := range s.stage.objects {
- if obj.ownerCharID == s.charID {
- continue
- }
- temp = &mhfpacket.MsgSysDuplicateObject{
- ObjID: obj.id,
- X: obj.x,
- Y: obj.y,
- Z: obj.z,
- Unk0: 0,
- OwnerCharID: obj.ownerCharID,
- }
- reloadNotif.WriteUint16(uint16(temp.Opcode()))
- temp.Build(reloadNotif, s.clientContext)
- }
- reloadNotif.WriteUint16(0x0010)
- s.QueueSend(reloadNotif.Data())
- }
-
- // Set account rights
- if strings.HasPrefix(chatMessage.Message, "!rights") {
- var v uint32
- n, err := fmt.Sscanf(chatMessage.Message, "!rights %d", &v)
- if err != nil || n != 1 {
- sendServerChatMessage(s, "Error in command. Format: !rights n")
- } else {
- _, err = s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID)
- if err == nil {
- sendServerChatMessage(s, fmt.Sprintf("Set rights integer: %d", v))
- }
- }
- }
-
// Discord integration
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
}
- // RAVI COMMANDS V2
- if strings.HasPrefix(chatMessage.Message, "!ravi") {
- if getRaviSemaphore(s) != "" {
- s.server.raviente.Lock()
- if !strings.HasPrefix(chatMessage.Message, "!ravi ") {
- sendServerChatMessage(s, "No Raviente command specified!")
- } else {
- if strings.HasPrefix(chatMessage.Message, "!ravi start") {
- if s.server.raviente.register.startTime == 0 {
- s.server.raviente.register.startTime = s.server.raviente.register.postTime
- sendServerChatMessage(s, "The Great Slaying will begin in a moment")
- s.notifyRavi()
- } else {
- sendServerChatMessage(s, "The Great Slaying has already begun!")
+ if strings.HasPrefix(chatMessage.Message, commands["Reload"].Prefix) {
+ // Flush all objects and users and reload
+ if commands["Reload"].Enabled {
+ sendServerChatMessage(s, "Reloading players...")
+ var temp mhfpacket.MHFPacket
+ deleteNotif := byteframe.NewByteFrame()
+ for _, object := range s.stage.objects {
+ if object.ownerCharID == s.charID {
+ continue
+ }
+ temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
+ deleteNotif.WriteUint16(uint16(temp.Opcode()))
+ temp.Build(deleteNotif, s.clientContext)
+ }
+ for _, session := range s.server.sessions {
+ if s == session {
+ continue
+ }
+ temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID}
+ deleteNotif.WriteUint16(uint16(temp.Opcode()))
+ temp.Build(deleteNotif, s.clientContext)
+ }
+ deleteNotif.WriteUint16(0x0010)
+ s.QueueSend(deleteNotif.Data())
+ time.Sleep(500 * time.Millisecond)
+ reloadNotif := byteframe.NewByteFrame()
+ for _, session := range s.server.sessions {
+ if s == session {
+ continue
+ }
+ temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
+ reloadNotif.WriteUint16(uint16(temp.Opcode()))
+ temp.Build(reloadNotif, s.clientContext)
+ for i := 0; i < 3; i++ {
+ temp = &mhfpacket.MsgSysNotifyUserBinary{
+ CharID: session.charID,
+ BinaryType: uint8(i + 1),
}
- } else if strings.HasPrefix(chatMessage.Message, "!ravi sm") || strings.HasPrefix(chatMessage.Message, "!ravi setmultiplier") {
- var num uint16
- n, numerr := fmt.Sscanf(chatMessage.Message, "!ravi sm %d", &num)
- if numerr != nil || n != 1 {
- sendServerChatMessage(s, "Error in command. Format: !ravi sm n")
- } else if s.server.raviente.state.damageMultiplier == 1 {
- if num > 32 {
- sendServerChatMessage(s, "Raviente multiplier too high, defaulting to 32x")
- s.server.raviente.state.damageMultiplier = 32
- } else {
- sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier set to %dx", num))
- s.server.raviente.state.damageMultiplier = uint32(num)
- }
- } else {
- sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is already set to %dx!", s.server.raviente.state.damageMultiplier))
- }
- } else if strings.HasPrefix(chatMessage.Message, "!ravi cm") || strings.HasPrefix(chatMessage.Message, "!ravi checkmultiplier") {
- sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is currently %dx", s.server.raviente.state.damageMultiplier))
- } else if strings.HasPrefix(chatMessage.Message, "!ravi sr") || strings.HasPrefix(chatMessage.Message, "!ravi sendres") {
- if s.server.raviente.state.stateData[28] > 0 {
- sendServerChatMessage(s, "Sending resurrection support!")
- s.server.raviente.state.stateData[28] = 0
- } else {
- sendServerChatMessage(s, "Resurrection support has not been requested!")
- }
- } else if strings.HasPrefix(chatMessage.Message, "!ravi ss") || strings.HasPrefix(chatMessage.Message, "!ravi sendsed") {
- sendServerChatMessage(s, "Sending sedation support if requested!")
- // Total BerRavi HP
- HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4]
- s.server.raviente.support.supportData[1] = HP
- } else if strings.HasPrefix(chatMessage.Message, "!ravi rs") || strings.HasPrefix(chatMessage.Message, "!ravi reqsed") {
- sendServerChatMessage(s, "Requesting sedation support!")
- // Total BerRavi HP
- HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4]
- s.server.raviente.support.supportData[1] = HP + 12
- } else {
- sendServerChatMessage(s, "Raviente command not recognised!")
+ reloadNotif.WriteUint16(uint16(temp.Opcode()))
+ temp.Build(reloadNotif, s.clientContext)
}
}
- s.server.raviente.Unlock()
+ for _, obj := range s.stage.objects {
+ if obj.ownerCharID == s.charID {
+ continue
+ }
+ temp = &mhfpacket.MsgSysDuplicateObject{
+ ObjID: obj.id,
+ X: obj.x,
+ Y: obj.y,
+ Z: obj.z,
+ Unk0: 0,
+ OwnerCharID: obj.ownerCharID,
+ }
+ reloadNotif.WriteUint16(uint16(temp.Opcode()))
+ temp.Build(reloadNotif, s.clientContext)
+ }
+ reloadNotif.WriteUint16(0x0010)
+ s.QueueSend(reloadNotif.Data())
} else {
- sendServerChatMessage(s, "No one has joined the Great Slaying!")
+ sendDisabledCommandMessage(s, commands["Reload"])
}
}
- // END RAVI COMMANDS V2
- if strings.HasPrefix(chatMessage.Message, "!tele ") {
- var x, y int16
- n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y)
- if err != nil || n != 2 {
- sendServerChatMessage(s, "Invalid command. Usage:\"!tele 500 500\"")
+ if strings.HasPrefix(chatMessage.Message, commands["KeyQuest"].Prefix) {
+ if commands["KeyQuest"].Enabled {
+ if strings.HasPrefix(chatMessage.Message, "!kqf get") {
+ sendServerChatMessage(s, fmt.Sprintf("KQF: %x", s.kqf))
+ } else if strings.HasPrefix(chatMessage.Message, "!kqf set") {
+ var hexs string
+ n, numerr := fmt.Sscanf(chatMessage.Message, "!kqf set %s", &hexs)
+ if numerr != nil || n != 1 || len(hexs) != 16 {
+ sendServerChatMessage(s, "Error in command. Format: !kqf set xxxxxxxxxxxxxxxx")
+ } else {
+ hexd, _ := hex.DecodeString(hexs)
+ s.kqf = hexd
+ s.kqfOverride = true
+ sendServerChatMessage(s, "KQF set, please switch Land/World")
+ }
+ }
} else {
- sendServerChatMessage(s, fmt.Sprintf("Teleporting to %d %d", x, y))
+ sendDisabledCommandMessage(s, commands["KeyQuest"])
+ }
+ }
- // Make the inside of the casted binary
- payload := byteframe.NewByteFrame()
- payload.SetLE()
- payload.WriteUint8(2) // SetState type(position == 2)
- payload.WriteInt16(x) // X
- payload.WriteInt16(y) // Y
- payloadBytes := payload.Data()
+ if strings.HasPrefix(chatMessage.Message, commands["Rights"].Prefix) {
+ // Set account rights
+ if commands["Rights"].Enabled {
+ var v uint32
+ n, err := fmt.Sscanf(chatMessage.Message, "!rights %d", &v)
+ if err != nil || n != 1 {
+ sendServerChatMessage(s, "Error in command. Format: !rights n")
+ } else {
+ _, err = s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID)
+ if err == nil {
+ sendServerChatMessage(s, fmt.Sprintf("Set rights integer: %d", v))
+ }
+ }
+ } else {
+ sendDisabledCommandMessage(s, commands["Rights"])
+ }
+ }
- s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{
- CharID: s.charID,
- MessageType: BinaryMessageTypeState,
- RawDataPayload: payloadBytes,
- })
+ if strings.HasPrefix(chatMessage.Message, commands["Course"].Prefix) {
+ if commands["Course"].Enabled {
+ var name string
+ n, err := fmt.Sscanf(chatMessage.Message, "!course %s", &name)
+ if err != nil || n != 1 {
+ sendServerChatMessage(s, "Error in command. Format: !course ")
+ } else {
+ name = strings.ToLower(name)
+ for _, course := range mhfpacket.Courses() {
+ for _, alias := range course.Aliases {
+ if strings.ToLower(name) == strings.ToLower(alias) {
+ if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases[0], Enabled: true}) {
+ if s.FindCourse(name).Value != 0 {
+ ei := slices.IndexFunc(s.courses, func(c mhfpacket.Course) bool {
+ for _, alias := range c.Aliases {
+ if strings.ToLower(name) == strings.ToLower(alias) {
+ return true
+ }
+ }
+ return false
+ })
+ if ei != -1 {
+ s.courses = append(s.courses[:ei], s.courses[ei+1:]...)
+ sendServerChatMessage(s, fmt.Sprintf(`%s Course disabled.`, course.Aliases[0]))
+ }
+ } else {
+ s.courses = append(s.courses, course)
+ sendServerChatMessage(s, fmt.Sprintf(`%s Course enabled.`, course.Aliases[0]))
+ }
+ var newInt uint32
+ for _, course := range s.courses {
+ newInt += course.Value
+ }
+ s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", newInt, s.charID)
+ updateRights(s)
+ } else {
+ sendServerChatMessage(s, fmt.Sprintf(`%s Course is locked.`, course.Aliases[0]))
+ }
+ }
+ }
+ }
+ }
+ } else {
+ sendDisabledCommandMessage(s, commands["Course"])
+ }
+ }
+
+ if strings.HasPrefix(chatMessage.Message, commands["Raviente"].Prefix) {
+ if commands["Raviente"].Enabled {
+ if getRaviSemaphore(s) != "" {
+ s.server.raviente.Lock()
+ if !strings.HasPrefix(chatMessage.Message, "!ravi ") {
+ sendServerChatMessage(s, "No Raviente command specified!")
+ } else {
+ if strings.HasPrefix(chatMessage.Message, "!ravi start") {
+ if s.server.raviente.register.startTime == 0 {
+ s.server.raviente.register.startTime = s.server.raviente.register.postTime
+ sendServerChatMessage(s, "The Great Slaying will begin in a moment")
+ s.notifyRavi()
+ } else {
+ sendServerChatMessage(s, "The Great Slaying has already begun!")
+ }
+ } else if strings.HasPrefix(chatMessage.Message, "!ravi sm") || strings.HasPrefix(chatMessage.Message, "!ravi setmultiplier") {
+ var num uint16
+ n, numerr := fmt.Sscanf(chatMessage.Message, "!ravi sm %d", &num)
+ if numerr != nil || n != 1 {
+ sendServerChatMessage(s, "Error in command. Format: !ravi sm n")
+ } else if s.server.raviente.state.damageMultiplier == 1 {
+ if num > 32 {
+ sendServerChatMessage(s, "Raviente multiplier too high, defaulting to 32x")
+ s.server.raviente.state.damageMultiplier = 32
+ } else {
+ sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier set to %dx", num))
+ s.server.raviente.state.damageMultiplier = uint32(num)
+ }
+ } else {
+ sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is already set to %dx!", s.server.raviente.state.damageMultiplier))
+ }
+ } else if strings.HasPrefix(chatMessage.Message, "!ravi cm") || strings.HasPrefix(chatMessage.Message, "!ravi checkmultiplier") {
+ sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is currently %dx", s.server.raviente.state.damageMultiplier))
+ } else if strings.HasPrefix(chatMessage.Message, "!ravi sr") || strings.HasPrefix(chatMessage.Message, "!ravi sendres") {
+ if s.server.raviente.state.stateData[28] > 0 {
+ sendServerChatMessage(s, "Sending resurrection support!")
+ s.server.raviente.state.stateData[28] = 0
+ } else {
+ sendServerChatMessage(s, "Resurrection support has not been requested!")
+ }
+ } else if strings.HasPrefix(chatMessage.Message, "!ravi ss") || strings.HasPrefix(chatMessage.Message, "!ravi sendsed") {
+ sendServerChatMessage(s, "Sending sedation support if requested!")
+ // Total BerRavi HP
+ HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4]
+ s.server.raviente.support.supportData[1] = HP
+ } else if strings.HasPrefix(chatMessage.Message, "!ravi rs") || strings.HasPrefix(chatMessage.Message, "!ravi reqsed") {
+ sendServerChatMessage(s, "Requesting sedation support!")
+ // Total BerRavi HP
+ HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4]
+ s.server.raviente.support.supportData[1] = HP + 12
+ } else {
+ sendServerChatMessage(s, "Raviente command not recognised!")
+ }
+ }
+ s.server.raviente.Unlock()
+ } else {
+ sendServerChatMessage(s, "No one has joined the Great Slaying!")
+ }
+ } else {
+ sendDisabledCommandMessage(s, commands["Raviente"])
+ }
+ }
+
+ if strings.HasPrefix(chatMessage.Message, commands["Teleport"].Prefix) {
+ if commands["Teleport"].Enabled {
+ var x, y int16
+ n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y)
+ if err != nil || n != 2 {
+ sendServerChatMessage(s, "Invalid command. Usage:\"!tele 500 500\"")
+ } else {
+ sendServerChatMessage(s, fmt.Sprintf("Teleporting to %d %d", x, y))
+
+ // Make the inside of the casted binary
+ payload := byteframe.NewByteFrame()
+ payload.SetLE()
+ payload.WriteUint8(2) // SetState type(position == 2)
+ payload.WriteInt16(x) // X
+ payload.WriteInt16(y) // Y
+ payloadBytes := payload.Data()
+
+ s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{
+ CharID: s.charID,
+ MessageType: BinaryMessageTypeState,
+ RawDataPayload: payloadBytes,
+ })
+ }
+ } else {
+ sendDisabledCommandMessage(s, commands["Teleport"])
}
}
}
diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go
index b712f960e..a3e671969 100644
--- a/server/channelserver/handlers_character.go
+++ b/server/channelserver/handlers_character.go
@@ -1,7 +1,6 @@
package channelserver
import (
- "database/sql"
"encoding/binary"
"erupe-ce/network/mhfpacket"
@@ -10,120 +9,154 @@ import (
)
const (
- CharacterSaveRPPointer = 0x22D16
+ pointerGender = 0x81 // +1
+ pointerRP = 0x22D16 // +2
+ pointerHouseTier = 0x1FB6C // +5
+ pointerHouseData = 0x1FE01 // +195
+ pointerBookshelfData = 0x22298 // +5576
+ // Gallery data also exists at 0x21578, is this the contest submission?
+ pointerGalleryData = 0x22320 // +1748
+ pointerToreData = 0x1FCB4 // +240
+ pointerGardenData = 0x22C58 // +68
+ pointerWeaponType = 0x1F715 // +1
+ pointerWeaponID = 0x1F60A // +2
+ pointerHRP = 0x1FDF6 // +2
+ pointerGRP = 0x1FDFC // +4
+ pointerKQF = 0x23D20 // +8
)
type CharacterSaveData struct {
CharID uint32
Name string
- RP uint16
IsNewCharacter bool
- // Use provided setter/getter
- baseSaveData []byte
+ Gender bool
+ RP uint16
+ HouseTier []byte
+ HouseData []byte
+ BookshelfData []byte
+ GalleryData []byte
+ ToreData []byte
+ GardenData []byte
+ WeaponType uint8
+ WeaponID uint16
+ HRP uint16
+ GR uint16
+ KQF []byte
+
+ compSave []byte
+ decompSave []byte
}
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
-
if err != nil {
- s.logger.Error("failed to retrieve save data for character", zap.Error(err), zap.Uint32("charID", charID))
+ s.logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
-
defer result.Close()
+ if !result.Next() {
+ s.logger.Error("No savedata found", zap.Uint32("charID", charID))
+ return nil, err
+ }
saveData := &CharacterSaveData{}
- var compressedBaseSave []byte
-
- if !result.Next() {
- s.logger.Error("no results found for character save data", zap.Uint32("charID", charID))
- return nil, err
- }
-
- err = result.Scan(&saveData.CharID, &compressedBaseSave, &saveData.IsNewCharacter, &saveData.Name)
-
+ err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil {
- s.logger.Error(
- "failed to retrieve save data for character",
- zap.Error(err),
- zap.Uint32("charID", charID),
- )
-
+ s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
- if compressedBaseSave == nil {
+ if saveData.compSave == nil {
return saveData, nil
}
- decompressedBaseSave, err := nullcomp.Decompress(compressedBaseSave)
-
+ err = saveData.Decompress()
if err != nil {
- s.logger.Error("Failed to decompress savedata from db", zap.Error(err))
+ s.logger.Error("Failed to decompress savedata", zap.Error(err))
return nil, err
}
- saveData.SetBaseSaveData(decompressedBaseSave)
+ saveData.updateStructWithSaveData()
return saveData, nil
}
-func (save *CharacterSaveData) Save(s *Session, transaction *sql.Tx) error {
- // We need to update the save data byte array before we save it back to the DB
+func (save *CharacterSaveData) Save(s *Session) {
+ if !s.kqfOverride {
+ s.kqf = save.KQF
+ } else {
+ save.KQF = s.kqf
+ }
+
save.updateSaveDataWithStruct()
- compressedData, err := save.CompressedBaseData(s)
-
+ err := save.Compress()
if err != nil {
- return err
+ s.logger.Error("Failed to compress savedata", zap.Error(err))
+ return
}
- updateSQL := "UPDATE characters SET savedata=$1, is_new_character=$3 WHERE id=$2"
-
- if transaction != nil {
- _, err = transaction.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
- } else {
- _, err = s.server.db.Exec(updateSQL, compressedData, save.CharID, save.IsNewCharacter)
- }
+ _, err = s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
+ `, save.compSave, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
+ if err != nil {
+ s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
+ }
+
+ s.server.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7
+ `, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.charID)
+}
+
+func (save *CharacterSaveData) Compress() error {
+ var err error
+ save.compSave, err = nullcomp.Compress(save.decompSave)
if err != nil {
- s.logger.Error("failed to save character data", zap.Error(err), zap.Uint32("charID", save.CharID))
return err
}
return nil
}
-func (save *CharacterSaveData) CompressedBaseData(s *Session) ([]byte, error) {
- compressedData, err := nullcomp.Compress(save.baseSaveData)
-
+func (save *CharacterSaveData) Decompress() error {
+ var err error
+ save.decompSave, err = nullcomp.Decompress(save.compSave)
if err != nil {
- s.logger.Error("failed to compress saveData", zap.Error(err), zap.Uint32("charID", save.CharID))
- return nil, err
+ return err
}
- return compressedData, nil
+ return nil
}
-func (save *CharacterSaveData) BaseSaveData() []byte {
- return save.baseSaveData
-}
-
-func (save *CharacterSaveData) SetBaseSaveData(data []byte) {
- save.baseSaveData = data
- // After setting the new save byte array, we can extract the values to update our struct
- // This will be useful when we save it back, we use the struct values to overwrite the saveData
- save.updateStructWithSaveData()
-}
-
-// This will update the save struct with the values stored in the raw savedata arrays
+// This will update the character save with the values stored in the save struct
func (save *CharacterSaveData) updateSaveDataWithStruct() {
rpBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(rpBytes, save.RP)
- copy(save.baseSaveData[CharacterSaveRPPointer:CharacterSaveRPPointer+2], rpBytes)
+ copy(save.decompSave[pointerRP:pointerRP+2], rpBytes)
+ copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF)
}
-// This will update the character save struct with the values stored in the raw savedata arrays
+// This will update the save struct with the values stored in the character save
func (save *CharacterSaveData) updateStructWithSaveData() {
- save.RP = binary.LittleEndian.Uint16(save.baseSaveData[CharacterSaveRPPointer : CharacterSaveRPPointer+2])
+ if save.decompSave[pointerGender] == 1 {
+ save.Gender = true
+ } else {
+ save.Gender = false
+ }
+ if !save.IsNewCharacter {
+ save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2])
+ save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5]
+ save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195]
+ save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576]
+ save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748]
+ save.ToreData = save.decompSave[pointerToreData : pointerToreData+240]
+ save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68]
+ save.WeaponType = save.decompSave[pointerWeaponType]
+ save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2])
+ save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2])
+ if save.HRP == uint16(999) {
+ save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]))
+ }
+ save.KQF = save.decompSave[pointerKQF : pointerKQF+8]
+ }
+ return
}
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go
index 8ed55c54a..28521d10d 100644
--- a/server/channelserver/handlers_data.go
+++ b/server/channelserver/handlers_data.go
@@ -1,8 +1,8 @@
package channelserver
import (
- "encoding/binary"
"encoding/hex"
+ "erupe-ce/common/bfutil"
"erupe-ce/common/stringsupport"
"fmt"
"io"
@@ -10,7 +10,6 @@ import (
"os"
"path/filepath"
- "erupe-ce/common/bfutil"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp"
@@ -26,7 +25,6 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
return
}
// Var to hold the decompressed savedata for updating the launcher response fields.
- var decompressedData []byte
if pkt.SaveType == 1 {
// Diff-based update.
// diffs themselves are also potentially compressed
@@ -36,77 +34,23 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
}
// Perform diff.
s.logger.Info("Diffing...")
- characterSaveData.SetBaseSaveData(deltacomp.ApplyDataDiff(diff, characterSaveData.BaseSaveData()))
+ characterSaveData.decompSave = deltacomp.ApplyDataDiff(diff, characterSaveData.decompSave)
} else {
+ dumpSaveData(s, pkt.RawDataPayload, "savedata")
// Regular blob update.
saveData, err := nullcomp.Decompress(pkt.RawDataPayload)
if err != nil {
s.logger.Fatal("Failed to decompress savedata from packet", zap.Error(err))
}
s.logger.Info("Updating save with blob")
- characterSaveData.SetBaseSaveData(saveData)
- }
- characterSaveData.IsNewCharacter = false
- characterBaseSaveData := characterSaveData.BaseSaveData()
- // Make a copy for updating the launcher fields.
- decompressedData = make([]byte, len(characterBaseSaveData))
- copy(decompressedData, characterBaseSaveData)
- err = characterSaveData.Save(s, nil)
- if err != nil {
- s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
+ characterSaveData.decompSave = saveData
}
+ characterSaveData.updateStructWithSaveData()
+ characterSaveData.Save(s)
s.logger.Info("Wrote recompressed savedata back to DB.")
- dumpSaveData(s, pkt.RawDataPayload, "savedata")
- _, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
- if err != nil {
- s.logger.Fatal("Failed to character weapon type in db", zap.Error(err))
- }
-
- s.myseries.houseTier = decompressedData[129900:129905] // 0x1FB6C + 5
- s.myseries.houseData = decompressedData[130561:130756] // 0x1FE01 + 195
- s.myseries.bookshelfData = decompressedData[139928:145504] // 0x22298 + 5576
- // Gallery data also exists at 0x21578, is this the contest submission?
- s.myseries.galleryData = decompressedData[140064:141812] // 0x22320 + 1748
- s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240
- s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68
-
- isFemale := decompressedData[81] // 0x51
- if isFemale == 1 {
- _, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID)
- } else {
- _, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID)
- }
- if err != nil {
- s.logger.Fatal("Failed to character gender in db", zap.Error(err))
- }
-
- weaponId := binary.LittleEndian.Uint16(decompressedData[128522:128524]) // 0x1F60A
- _, err = s.server.db.Exec("UPDATE characters SET weapon_id=$1 WHERE id=$2", weaponId, s.charID)
- if err != nil {
- s.logger.Fatal("Failed to update character weapon id in db", zap.Error(err))
- }
-
- hrp := binary.LittleEndian.Uint16(decompressedData[130550:130552]) // 0x1FDF6
- _, err = s.server.db.Exec("UPDATE characters SET hrp=$1 WHERE id=$2", hrp, s.charID)
- if err != nil {
- s.logger.Fatal("Failed to update character hrp in db", zap.Error(err))
- }
-
- grp := binary.LittleEndian.Uint32(decompressedData[130556:130560]) // 0x1FDFC
- var gr uint16
- if grp > 0 {
- gr = grpToGR(grp)
- } else {
- gr = 0
- }
- _, err = s.server.db.Exec("UPDATE characters SET gr=$1 WHERE id=$2", gr, s.charID)
- if err != nil {
- s.logger.Fatal("Failed to update character gr in db", zap.Error(err))
- }
-
- characterName := s.clientContext.StrConv.MustDecode(bfutil.UpToNull(decompressedData[88:100]))
- _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterName, s.charID)
+ characterSaveData.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(characterSaveData.decompSave[88:100]))
+ _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID)
if err != nil {
s.logger.Fatal("Failed to update character name in db", zap.Error(err))
}
@@ -275,15 +219,24 @@ func dumpSaveData(s *Session, data []byte, suffix string) {
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
return
} else {
- dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name))
- path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix))
-
- if _, err := os.Stat(dir); os.IsNotExist(err) {
- os.Mkdir(dir, os.ModeDir)
- }
- err := ioutil.WriteFile(path, data, 0644)
+ dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID))
+ path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix))
+ _, err := os.Stat(dir)
if err != nil {
- s.logger.Fatal("Error dumping savedata", zap.Error(err))
+ if os.IsNotExist(err) {
+ err = os.Mkdir(dir, os.ModeDir)
+ if err != nil {
+ s.logger.Warn("Error dumping savedata, could not create folder")
+ return
+ }
+ } else {
+ s.logger.Warn("Error dumping savedata")
+ return
+ }
+ }
+ err = os.WriteFile(path, data, 0644)
+ if err != nil {
+ s.logger.Warn("Error dumping savedata, could not write file", zap.Error(err))
}
}
}
@@ -320,7 +273,8 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveScenarioData)
- _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE characters.id = $2", pkt.RawDataPayload, int(s.charID))
+ dumpSaveData(s, pkt.RawDataPayload, "scenario")
+ _, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update scenario data in db", zap.Error(err))
}
@@ -337,7 +291,7 @@ func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadScenarioData)
var scenarioData []byte
bf := byteframe.NewByteFrame()
- err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE characters.id = $1", int(s.charID)).Scan(&scenarioData)
+ err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.charID).Scan(&scenarioData)
if err != nil {
s.logger.Fatal("Failed to get scenario data contents in db", zap.Error(err))
} else {
diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go
index b6765f998..95227709f 100644
--- a/server/channelserver/handlers_event.go
+++ b/server/channelserver/handlers_event.go
@@ -53,38 +53,46 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
}
type activeFeature struct {
- StartTime time.Time
- ActiveFeatures uint32
- Unk1 uint16
+ StartTime time.Time `db:"start_time"`
+ ActiveFeatures uint32 `db:"featured"`
}
func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
- persistentEventSchedule := make([]activeFeature, 8) // generate day after weekly restart
- for x := -1; x < 7; x++ {
- feat := generateActiveWeapons(14) // number of active weapons
- // TODO: only generate this once per restart (server should be restarted weekly)
- // then load data from db instead of regenerating
- persistentEventSchedule[x+1] = activeFeature{
- StartTime: Time_Current_Midnight().Add(time.Duration(24*x) * time.Hour),
- ActiveFeatures: uint32(feat),
- Unk1: 0,
+
+ var features []activeFeature
+ rows, _ := s.server.db.Queryx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1 OR start_time=$2`, Time_Current_Midnight().Add(-24*time.Hour), Time_Current_Midnight())
+ for rows.Next() {
+ var feature activeFeature
+ rows.StructScan(&feature)
+ features = append(features, feature)
+ }
+
+ if len(features) < 2 {
+ if len(features) == 0 {
+ feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
+ feature.StartTime = Time_Current_Midnight().Add(-24 * time.Hour)
+ features = append(features, feature)
+ s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
}
+ feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
+ feature.StartTime = Time_Current_Midnight()
+ features = append(features, feature)
+ s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
}
- resp := byteframe.NewByteFrame()
- resp.WriteUint8(uint8(len(persistentEventSchedule))) // Entry count, client only parses the first 7 or 8.
- resp.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) // 5 minutes ago server time
-
- for _, es := range persistentEventSchedule {
- resp.WriteUint32(uint32(es.StartTime.Unix()))
- resp.WriteUint32(es.ActiveFeatures)
- resp.WriteUint16(es.Unk1)
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint8(2)
+ bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
+ for _, feature := range features {
+ bf.WriteUint32(uint32(feature.StartTime.Unix()))
+ bf.WriteUint32(feature.ActiveFeatures)
+ bf.WriteUint16(0)
}
- doAckBufSucceed(s, pkt.AckHandle, resp.Data())
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
-func generateActiveWeapons(count int) int {
+func generateFeatureWeapons(count int) activeFeature {
nums := make([]int, 0)
var result int
r := rand.New(rand.NewSource(time.Now().UnixNano()))
@@ -104,7 +112,7 @@ func generateActiveWeapons(count int) int {
for _, num := range nums {
result += int(math.Pow(2, float64(num)))
}
- return result
+ return activeFeature{ActiveFeatures: uint32(result)}
}
type loginBoost struct {
diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go
index 6e5080ac3..9f925f3dc 100644
--- a/server/channelserver/handlers_festa.go
+++ b/server/channelserver/handlers_festa.go
@@ -6,6 +6,7 @@ import (
ps "erupe-ce/common/pascalstring"
"erupe-ce/network/mhfpacket"
"math/rand"
+ "sort"
"time"
)
@@ -253,11 +254,19 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
- var souls uint32
+ var souls, exists uint32
s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls)
+ err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists)
bf := byteframe.NewByteFrame()
bf.WriteUint32(souls)
- bf.WriteUint32(0) // unk
+ if err != nil {
+ bf.WriteBool(true)
+ bf.WriteBool(false)
+ } else {
+ bf.WriteBool(false)
+ bf.WriteBool(true)
+ }
+ bf.WriteUint16(0) // Unk
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
@@ -280,10 +289,10 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
return
}
resp.WriteUint32(guild.Souls)
- resp.WriteUint32(0) // unk
- resp.WriteUint32(0) // unk, rank?
- resp.WriteUint32(0) // unk
- resp.WriteUint32(0) // unk
+ resp.WriteUint32(1) // unk
+ resp.WriteUint32(1) // unk
+ resp.WriteUint32(1) // unk, rank?
+ resp.WriteUint32(1) // unk
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
@@ -302,6 +311,9 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(members)))
bf.WriteUint16(0) // Unk
+ sort.Slice(members, func(i, j int) bool {
+ return members[i].Souls > members[j].Souls
+ })
for _, member := range members {
bf.WriteUint32(member.CharID)
bf.WriteUint32(member.Souls)
@@ -342,6 +354,7 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireFesta)
+ s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
@@ -368,7 +381,7 @@ type Prize struct {
func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize)
- rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='personal'")
+ rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='personal'`, s.charID)
var count uint32
prizeData := byteframe.NewByteFrame()
for rows.Next() {
@@ -394,7 +407,7 @@ func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket)
func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize)
- rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='guild'")
+ rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='guild'`, s.charID)
var count uint32
prizeData := byteframe.NewByteFrame()
for rows.Next() {
diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go
index 6bbee7eba..3f0df48b3 100644
--- a/server/channelserver/handlers_guild.go
+++ b/server/channelserver/handlers_guild.go
@@ -8,6 +8,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "math"
"sort"
"strings"
"time"
@@ -54,6 +55,10 @@ type Guild struct {
PugiName1 string `db:"pugi_name_1"`
PugiName2 string `db:"pugi_name_2"`
PugiName3 string `db:"pugi_name_3"`
+ PugiOutfit1 uint8 `db:"pugi_outfit_1"`
+ PugiOutfit2 uint8 `db:"pugi_outfit_2"`
+ PugiOutfit3 uint8 `db:"pugi_outfit_3"`
+ PugiOutfits uint32 `db:"pugi_outfits"`
Recruiting bool `db:"recruiting"`
FestivalColour FestivalColour `db:"festival_colour"`
Souls uint32 `db:"souls"`
@@ -125,6 +130,10 @@ SELECT
COALESCE(pugi_name_1, '') AS pugi_name_1,
COALESCE(pugi_name_2, '') AS pugi_name_2,
COALESCE(pugi_name_3, '') AS pugi_name_3,
+ pugi_outfit_1,
+ pugi_outfit_2,
+ pugi_outfit_3,
+ pugi_outfits,
recruiting,
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour,
(SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls,
@@ -151,8 +160,10 @@ SELECT
func (guild *Guild) Save(s *Session) error {
_, err := s.server.db.Exec(`
- UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7, icon=$8, leader_id=$9 WHERE id=$1
- `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3, guild.Icon, guild.GuildLeader.LeaderCharID)
+ UPDATE guilds SET main_motto=$2, sub_motto=$3, comment=$4, pugi_name_1=$5, pugi_name_2=$6, pugi_name_3=$7,
+ pugi_outfit_1=$8, pugi_outfit_2=$9, pugi_outfit_3=$10, pugi_outfits=$11, icon=$12, leader_id=$13 WHERE id=$1
+ `, guild.ID, guild.MainMotto, guild.SubMotto, guild.Comment, guild.PugiName1, guild.PugiName2, guild.PugiName3,
+ guild.PugiOutfit1, guild.PugiOutfit2, guild.PugiOutfit3, guild.PugiOutfits, guild.Icon, guild.GuildLeader.LeaderCharID)
if err != nil {
s.logger.Error("failed to update guild data", zap.Error(err), zap.Uint32("guildID", guild.ID))
@@ -164,7 +175,7 @@ func (guild *Guild) Save(s *Session) error {
func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType GuildApplicationType, transaction *sql.Tx) error {
- sql := `
+ query := `
INSERT INTO guild_applications (guild_id, character_id, actor_id, application_type)
VALUES ($1, $2, $3, $4)
`
@@ -172,9 +183,9 @@ func (guild *Guild) CreateApplication(s *Session, charID uint32, applicationType
var err error
if transaction == nil {
- _, err = s.server.db.Exec(sql, guild.ID, charID, s.charID, applicationType)
+ _, err = s.server.db.Exec(query, guild.ID, charID, s.charID, applicationType)
} else {
- _, err = transaction.Exec(sql, guild.ID, charID, s.charID, applicationType)
+ _, err = transaction.Exec(query, guild.ID, charID, s.charID, applicationType)
}
if err != nil {
@@ -222,7 +233,7 @@ func (guild *Guild) Disband(s *Session) error {
return err
}
- _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=NULL WHERE sub1_id=$1", guild.ID)
+ _, err = transaction.Exec("UPDATE guild_alliances SET sub1_id=sub2_id, sub2_id=NULL WHERE sub1_id=$1", guild.ID)
if err != nil {
s.logger.Error("failed to remove guild from alliance", zap.Error(err), zap.Uint32("guildID", guild.ID))
@@ -634,11 +645,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint32(uint32(response))
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_RESIGN:
guildMembers, err := GetGuildMembers(s, guild.ID, false)
- success := false
if err == nil {
sort.Slice(guildMembers[:], func(i, j int) bool {
return guildMembers[i].OrderIndex < guildMembers[j].OrderIndex
@@ -651,29 +659,17 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
guildMembers[0].Save(s)
guildMembers[i].Save(s)
bf.WriteUint32(guildMembers[i].CharID)
- success = true
break
}
}
guild.Save(s)
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
- if !success {
- bf.WriteUint32(0)
- doAckBufSucceed(s, pkt.AckHandle, bf.Data())
- }
- return
case mhfpacket.OPERATE_GUILD_APPLY:
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
- if err != nil {
- // All successful acks return 0x01, assuming 0x00 is failure
- bf.WriteUint32(0x00)
- } else {
+ if err == nil {
bf.WriteUint32(guild.LeaderCharID)
}
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_LEAVE:
var err error
@@ -698,10 +694,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint32(uint32(response))
- doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
- return
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
- handleDonateRP(s, pkt, bf, guild, false)
+ bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
@@ -711,46 +705,55 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
handleAvoidLeadershipUpdate(s, pkt, false)
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
- pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
- _ = pbf.ReadUint8() // len
- _ = pbf.ReadUint32()
- guild.Comment = stringsupport.SJISToUTF8(pbf.ReadNullTerminatedBytes())
+ guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())
guild.Save(s)
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
- guild.SubMotto = pkt.UnkData[3]
- guild.MainMotto = pkt.UnkData[4]
+ _ = pkt.Data1.ReadUint16()
+ guild.SubMotto = pkt.Data1.ReadUint8()
+ guild.MainMotto = pkt.Data1.ReadUint8()
guild.Save(s)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
- handleRenamePugi(s, pkt.UnkData, guild, 1)
+ handleRenamePugi(s, pkt.Data2, guild, 1)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
- handleRenamePugi(s, pkt.UnkData, guild, 2)
+ handleRenamePugi(s, pkt.Data2, guild, 2)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
- handleRenamePugi(s, pkt.UnkData, guild, 3)
+ handleRenamePugi(s, pkt.Data2, guild, 3)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
- // TODO: decode guild poogie outfits
+ handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
+ handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
+ handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3)
+ case mhfpacket.OPERATE_GUILD_UNLOCK_OUTFIT:
+ // TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time
+ s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID)
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
- handleDonateRP(s, pkt, bf, guild, true)
+ bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, true))
+ case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE:
+ rp := uint16(pkt.Data1.ReadUint32())
+ var balance uint32
+ s.server.db.QueryRow(`UPDATE guilds SET event_rp=event_rp-$1 WHERE id=$2 RETURNING event_rp`, rp, guild.ID).Scan(&balance)
+ bf.WriteUint32(balance)
default:
panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action))
}
- doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ if len(bf.Data()) > 0 {
+ doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
+ } else {
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ }
}
-func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
- bf := byteframe.NewByteFrameFromBytes(data)
- _ = bf.ReadUint8() // len
- _ = bf.ReadUint32() // unk
+func handleRenamePugi(s *Session, bf *byteframe.ByteFrame, guild *Guild, num int) {
name := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
switch num {
case 1:
@@ -763,33 +766,35 @@ func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
guild.Save(s)
}
-func handleDonateRP(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, bf *byteframe.ByteFrame, guild *Guild, isEvent bool) error {
- rp := binary.BigEndian.Uint16(pkt.UnkData[3:5])
+func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) {
+ switch num {
+ case 1:
+ guild.PugiOutfit1 = outfit
+ case 2:
+ guild.PugiOutfit2 = outfit
+ case 3:
+ guild.PugiOutfit3 = outfit
+ }
+ guild.Save(s)
+}
+
+func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte {
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint32(0)
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
- return err
- }
- saveData.RP -= rp
- transaction, err := s.server.db.Begin()
- err = saveData.Save(s, transaction)
- if err != nil {
- transaction.Rollback()
- return err
+ return bf.Data()
}
+ saveData.RP -= amount
+ saveData.Save(s)
updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2"
if isEvent {
updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2"
}
- _, err = s.server.db.Exec(updateSQL, rp, guild.ID)
- if err != nil {
- s.logger.Error("Failed to donate rank RP to guild", zap.Error(err), zap.Uint32("guildID", guild.ID))
- transaction.Rollback()
- return err
- } else {
- transaction.Commit()
- }
+ s.server.db.Exec(updateSQL, amount, guild.ID)
+ bf.Seek(0, 0)
bf.WriteUint32(uint32(saveData.RP))
- return nil
+ return bf.Data()
}
func handleAvoidLeadershipUpdate(s *Session, pkt *mhfpacket.MsgMhfOperateGuild, avoidLeadership bool) {
@@ -952,11 +957,13 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
ps.Uint8(bf, guild.PugiName1, true)
ps.Uint8(bf, guild.PugiName2, true)
ps.Uint8(bf, guild.PugiName3, true)
-
- // probably guild pugi properties, should be status, stamina and luck outfits
- bf.WriteBytes([]byte{
- 0x04, 0x02, 0x03, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00, 0x4E,
- })
+ bf.WriteUint8(guild.PugiOutfit1)
+ bf.WriteUint8(guild.PugiOutfit2)
+ bf.WriteUint8(guild.PugiOutfit3)
+ bf.WriteUint8(guild.PugiOutfit1)
+ bf.WriteUint8(guild.PugiOutfit2)
+ bf.WriteUint8(guild.PugiOutfit3)
+ bf.WriteUint32(guild.PugiOutfits)
// Unk flags
bf.WriteUint8(0x3C) // also seen as 0x32 on JP and 0x64 on TW
@@ -1027,15 +1034,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
}
applicants, err := GetGuildMembers(s, guild.ID, true)
-
- if err != nil {
- resp := byteframe.NewByteFrame()
- resp.WriteUint32(0) // Count
- resp.WriteUint8(0) // Unk, read if count == 0.
-
- doAckBufSucceed(s, pkt.AckHandle, resp.Data())
- }
- if err != nil || characterGuildData.IsApplicant {
+ if err != nil || (characterGuildData != nil && !characterGuildData.CanRecruit()) {
bf.WriteUint16(0)
} else {
bf.WriteUint16(uint16(len(applicants)))
@@ -1049,7 +1048,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
- bf.WriteUint16(0x0000)
+ bf.WriteUint16(0x0000) // lenAllianceApplications
/*
alliance application format
@@ -1095,15 +1094,16 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuild)
var guilds []*Guild
+ var alliances []*GuildAlliance
var rows *sqlx.Rows
var err error
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
switch pkt.Type {
case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME:
- bf.ReadBytes(10)
+ bf.ReadBytes(8)
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
if err == nil {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
@@ -1111,9 +1111,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME:
- bf.ReadBytes(10)
+ bf.ReadBytes(8)
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
if err == nil {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
@@ -1121,7 +1121,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID:
- bf.ReadBytes(2)
ID := bf.ReadUint32()
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID)
if err == nil {
@@ -1131,11 +1130,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS:
- sorting := bf.ReadUint8()
- if sorting == 1 {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ if pkt.Sorting {
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
} else {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
}
if err == nil {
for rows.Next() {
@@ -1144,11 +1142,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION:
- sorting := bf.ReadUint8()
- if sorting == 1 {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ if pkt.Sorting {
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
} else {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
}
if err == nil {
for rows.Next() {
@@ -1157,11 +1154,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK:
- sorting := bf.ReadUint8()
- if sorting == 1 {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ if pkt.Sorting {
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
} else {
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
}
if err == nil {
for rows.Next() {
@@ -1170,10 +1166,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
}
case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO:
- bf.ReadBytes(2)
mainMotto := bf.ReadUint16()
subMotto := bf.ReadUint16()
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3 LIMIT 11`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10)
if err == nil {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
@@ -1182,48 +1177,126 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
}
case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING:
// Assume the player wants the newest guilds with open recruitment
- rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
+ rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10)
if err == nil {
for rows.Next() {
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
guilds = append(guilds, guild)
}
}
- case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
- //
- case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
- //
- case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID:
- //
- case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS:
- //
- case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION:
- //
- default:
- panic(fmt.Sprintf("no handler for guild search type '%d'", pkt.Type))
}
- if err != nil || guilds == nil {
+ if pkt.Type > 8 {
+ var tempAlliances []*GuildAlliance
+ rows, err = s.server.db.Queryx(allianceInfoSelectQuery)
+ if err == nil {
+ for rows.Next() {
+ alliance, _ := buildAllianceObjectFromDbResult(rows, err, s)
+ tempAlliances = append(tempAlliances, alliance)
+ }
+ }
+ switch pkt.Type {
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
+ bf.ReadBytes(8)
+ searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ for _, alliance := range tempAlliances {
+ if strings.Contains(alliance.Name, searchTerm) {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
+ bf.ReadBytes(8)
+ searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
+ for _, alliance := range tempAlliances {
+ if strings.Contains(alliance.ParentGuild.LeaderName, searchTerm) {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID:
+ ID := bf.ReadUint32()
+ for _, alliance := range tempAlliances {
+ if alliance.ParentGuild.LeaderCharID == ID {
+ alliances = append(alliances, alliance)
+ }
+ }
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS:
+ if pkt.Sorting {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers
+ })
+ } else {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].TotalMembers < tempAlliances[j].TotalMembers
+ })
+ }
+ alliances = tempAlliances
+ case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION:
+ if pkt.Sorting {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix()
+ })
+ } else {
+ sort.Slice(tempAlliances, func(i, j int) bool {
+ return tempAlliances[i].CreatedAt.Unix() < tempAlliances[j].CreatedAt.Unix()
+ })
+ }
+ alliances = tempAlliances
+ }
+ }
+
+ if err != nil || (guilds == nil && alliances == nil) {
stubEnumerateNoResults(s, pkt.AckHandle)
return
}
bf = byteframe.NewByteFrame()
- bf.WriteUint16(uint16(len(guilds)))
- bf.WriteUint8(0x01) // Unk
-
- for _, guild := range guilds {
- bf.WriteUint32(guild.ID)
- bf.WriteUint32(guild.LeaderCharID)
- bf.WriteUint16(guild.MemberCount)
- bf.WriteUint16(0x0000) // Unk
- bf.WriteUint16(guild.Rank) // OR guilds in alliance
- bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
- ps.Uint8(bf, guild.Name, true)
- ps.Uint8(bf, guild.LeaderName, true)
- bf.WriteUint8(0x01) // Unk
- bf.WriteBool(!guild.Recruiting)
+ if pkt.Type > 8 {
+ hasNextPage := false
+ if len(alliances) > 10 {
+ hasNextPage = true
+ alliances = alliances[:10]
+ }
+ bf.WriteUint16(uint16(len(alliances)))
+ bf.WriteBool(hasNextPage)
+ for _, alliance := range alliances {
+ bf.WriteUint32(alliance.ID)
+ bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
+ bf.WriteUint16(alliance.TotalMembers)
+ bf.WriteUint16(0x0000)
+ if alliance.SubGuild1ID == 0 && alliance.SubGuild2ID == 0 {
+ bf.WriteUint16(1)
+ } else if alliance.SubGuild1ID > 0 && alliance.SubGuild2ID == 0 || alliance.SubGuild1ID == 0 && alliance.SubGuild2ID > 0 {
+ bf.WriteUint16(2)
+ } else {
+ bf.WriteUint16(3)
+ }
+ bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
+ ps.Uint8(bf, alliance.Name, true)
+ ps.Uint8(bf, alliance.ParentGuild.LeaderName, true)
+ bf.WriteUint8(0x01) // Unk
+ bf.WriteBool(true) // TODO: Enable GuildAlliance applications
+ }
+ } else {
+ hasNextPage := false
+ if len(guilds) > 10 {
+ hasNextPage = true
+ guilds = guilds[:10]
+ }
+ bf.WriteUint16(uint16(len(guilds)))
+ bf.WriteBool(hasNextPage)
+ for _, guild := range guilds {
+ bf.WriteUint32(guild.ID)
+ bf.WriteUint32(guild.LeaderCharID)
+ bf.WriteUint16(guild.MemberCount)
+ bf.WriteUint16(0x0000) // Unk
+ bf.WriteUint16(guild.Rank) // OR guilds in alliance
+ bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
+ ps.Uint8(bf, guild.Name, true)
+ ps.Uint8(bf, guild.LeaderName, true)
+ bf.WriteUint8(0x01) // Unk
+ bf.WriteBool(!guild.Recruiting)
+ }
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
@@ -1712,14 +1785,29 @@ func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) {
}
func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildWeeklyBonusActiveCount)
-
- // Values taken from brand new guild capture
- doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x03))
+ bf := byteframe.NewByteFrame()
+ bf.WriteUint8(0x3C) // Active count
+ bf.WriteUint8(0x3C) // Current active count
+ bf.WriteUint8(0x00) // New active count
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGuildHuntdata)
- doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ bf := byteframe.NewByteFrame()
+ switch pkt.Operation {
+ case 0: // Unk
+ doAckBufSucceed(s, pkt.AckHandle, []byte{})
+ case 1: // Get Huntdata
+ 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})
+ }
}
type MessageBoardPost struct {
diff --git a/server/channelserver/handlers_guild_alliance.go b/server/channelserver/handlers_guild_alliance.go
index 77f204adc..7e2a9a2ac 100644
--- a/server/channelserver/handlers_guild_alliance.go
+++ b/server/channelserver/handlers_guild_alliance.go
@@ -1,6 +1,8 @@
package channelserver
import (
+ "erupe-ce/common/byteframe"
+ ps "erupe-ce/common/pascalstring"
"fmt"
"time"
@@ -139,8 +141,15 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
}
case mhfpacket.OPERATE_JOINT_LEAVE:
if guild.LeaderCharID == s.charID {
- // delete alliance application
- // or leave alliance
+ if guild.ID == 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 guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
+ } else {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
+ }
+ // TODO: Handle deleting Alliance applications
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
s.logger.Warn(
"Non-owner of guild attempted alliance leave",
@@ -148,10 +157,75 @@ func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
+ case mhfpacket.OPERATE_JOINT_KICK:
+ if alliance.ParentGuild.LeaderCharID == s.charID {
+ _ = pkt.UnkData.ReadUint8()
+ kickedGuildID := pkt.UnkData.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 {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
+ } else {
+ s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
+ }
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ } else {
+ s.logger.Warn(
+ "Non-owner of alliance attempted kick",
+ zap.Uint32("CharID", s.charID),
+ zap.Uint32("AllyID", alliance.ID),
+ )
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ }
default:
- panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
+ panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
}
}
-func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfInfoJoint)
+ bf := byteframe.NewByteFrame()
+ alliance, err := GetAllianceData(s, pkt.AllianceID)
+ if err != nil {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ } else {
+ bf.WriteUint32(alliance.ID)
+ bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
+ bf.WriteUint16(alliance.TotalMembers)
+ bf.WriteUint16(0x0000) // Unk
+ ps.Uint16(bf, alliance.Name, true)
+ if alliance.SubGuild1ID > 0 {
+ if alliance.SubGuild2ID > 0 {
+ bf.WriteUint8(3)
+ } else {
+ bf.WriteUint8(2)
+ }
+ } else {
+ bf.WriteUint8(1)
+ }
+ bf.WriteUint32(alliance.ParentGuildID)
+ bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
+ bf.WriteUint16(alliance.ParentGuild.Rank)
+ bf.WriteUint16(alliance.ParentGuild.MemberCount)
+ ps.Uint16(bf, alliance.ParentGuild.Name, true)
+ ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
+ if alliance.SubGuild1ID > 0 {
+ bf.WriteUint32(alliance.SubGuild1ID)
+ bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
+ bf.WriteUint16(alliance.SubGuild1.Rank)
+ bf.WriteUint16(alliance.SubGuild1.MemberCount)
+ ps.Uint16(bf, alliance.SubGuild1.Name, true)
+ ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
+ }
+ if alliance.SubGuild2ID > 0 {
+ bf.WriteUint32(alliance.SubGuild2ID)
+ bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
+ bf.WriteUint16(alliance.SubGuild2.Rank)
+ bf.WriteUint16(alliance.SubGuild2.MemberCount)
+ ps.Uint16(bf, alliance.SubGuild2.Name, true)
+ ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
+ }
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
+ }
+}
diff --git a/server/channelserver/handlers_house.go b/server/channelserver/handlers_house.go
index 280f27238..b3aadf372 100644
--- a/server/channelserver/handlers_house.go
+++ b/server/channelserver/handlers_house.go
@@ -38,24 +38,26 @@ FROM warehouse
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
- _, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID)
- if err != nil {
- panic(err)
- }
+ s.server.db.Exec(`UPDATE user_binary SET house_furniture=$1 WHERE id=$2`, pkt.InteriorData, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type HouseData struct {
- CharID uint32 `db:"id"`
- HRP uint16 `db:"hrp"`
- GR uint16 `db:"gr"`
- Name string `db:"name"`
+ CharID uint32 `db:"id"`
+ HRP uint16 `db:"hrp"`
+ GR uint16 `db:"gr"`
+ Name string `db:"name"`
+ HouseState uint8 `db:"house_state"`
+ HousePassword string `db:"house_password"`
}
func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateHouse)
bf := byteframe.NewByteFrame()
+ bf.WriteUint16(0)
var houses []HouseData
+ houseQuery := `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
+ FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1`
switch pkt.Method {
case 1:
var friendsList string
@@ -63,17 +65,15 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
cids := stringsupport.CSVElems(friendsList)
for _, cid := range cids {
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", cid)
+ row := s.server.db.QueryRowx(houseQuery, cid)
err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ if err == nil {
houses = append(houses, house)
}
}
case 2:
guild, err := GetGuildInfoByCharacterId(s, s.charID)
- if err != nil {
+ if err != nil || guild == nil {
break
}
guildMembers, err := GetGuildMembers(s, guild.ID, false)
@@ -82,58 +82,48 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
}
for _, member := range guildMembers {
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", member.CharID)
- err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ row := s.server.db.QueryRowx(houseQuery, member.CharID)
+ err = row.StructScan(&house)
+ if err == nil {
houses = append(houses, house)
}
}
case 3:
+ houseQuery = `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
+ FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name))
- err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
- houses = append(houses, house)
+ rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
+ for rows.Next() {
+ err := rows.StructScan(&house)
+ if err == nil {
+ houses = append(houses, house)
+ }
}
case 4:
house := HouseData{}
- row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", pkt.CharID)
+ row := s.server.db.QueryRowx(houseQuery, pkt.CharID)
err := row.StructScan(&house)
- if err != nil {
- panic(err)
- } else {
+ if err == nil {
houses = append(houses, house)
}
case 5: // Recent visitors
break
}
- var exists int
for _, house := range houses {
- for _, session := range s.server.sessions {
- if session.charID == house.CharID {
- exists++
- bf.WriteUint32(house.CharID)
- bf.WriteUint8(session.myseries.state)
- if len(session.myseries.password) > 0 {
- bf.WriteUint8(3)
- } else {
- bf.WriteUint8(0)
- }
- bf.WriteUint16(house.HRP)
- bf.WriteUint16(house.GR)
- ps.Uint8(bf, house.Name, true)
- break
- }
+ bf.WriteUint32(house.CharID)
+ bf.WriteUint8(house.HouseState)
+ if len(house.HousePassword) > 0 {
+ bf.WriteUint8(3)
+ } else {
+ bf.WriteUint8(0)
}
+ bf.WriteUint16(house.HRP)
+ bf.WriteUint16(house.GR)
+ ps.Uint8(bf, house.Name, true)
}
- resp := byteframe.NewByteFrame()
- resp.WriteUint16(uint16(exists))
- resp.WriteBytes(bf.Data())
- doAckBufSucceed(s, pkt.AckHandle, resp.Data())
+ bf.Seek(0, 0)
+ bf.WriteUint16(uint16(len(houses)))
+ doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
@@ -143,72 +133,89 @@ func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
// 03 = open friends
// 04 = open guild
// 05 = open friends+guild
- s.myseries.state = pkt.State
- s.myseries.password = pkt.Password
+ s.server.db.Exec(`UPDATE user_binary SET house_state=$1, house_password=$2 WHERE id=$3`, pkt.State, pkt.Password, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHouse)
bf := byteframe.NewByteFrame()
+
+ var state uint8
+ var password string
+ s.server.db.QueryRow(`SELECT COALESCE(house_state, 2) as house_state, COALESCE(house_password, '') as house_password FROM user_binary WHERE id=$1
+ `, pkt.CharID).Scan(&state, &password)
+
if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass {
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID && pkt.Password != session.myseries.password {
- doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
- return
- }
+ if pkt.Password != password {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ return
}
}
- var furniture []byte
- err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", pkt.CharID).Scan(&furniture)
- if err != nil {
- panic(err)
+ if pkt.Destination != 9 && state > 2 {
+ allowed := false
+
+ // Friends list verification
+ if state == 3 || state == 5 {
+ var friendsList string
+ s.server.db.QueryRow(`SELECT friends FROM characters WHERE id=$1`, pkt.CharID).Scan(&friendsList)
+ cids := stringsupport.CSVElems(friendsList)
+ for _, cid := range cids {
+ if uint32(cid) == s.charID {
+ allowed = true
+ break
+ }
+ }
+ }
+
+ // Guild verification
+ if state > 3 {
+ ownGuild, err := GetGuildInfoByCharacterId(s, s.charID)
+ isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.charID)
+ if err == nil && ownGuild != nil {
+ othersGuild, err := GetGuildInfoByCharacterId(s, pkt.CharID)
+ if err == nil && othersGuild != nil {
+ if othersGuild.ID == ownGuild.ID && !isApplicant {
+ allowed = true
+ }
+ }
+ }
+ }
+
+ if !allowed {
+ doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
+ return
+ }
}
- if furniture == nil {
- furniture = make([]byte, 20)
+
+ var houseTier, houseData, houseFurniture, bookshelf, gallery, tore, garden []byte
+ s.server.db.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1
+ `, pkt.CharID).Scan(&houseTier, &houseData, &houseFurniture, &bookshelf, &gallery, &tore, &garden)
+ if houseFurniture == nil {
+ houseFurniture = make([]byte, 20)
}
switch pkt.Destination {
case 3: // Others house
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.houseTier)
- bf.WriteBytes(session.myseries.houseData)
- bf.WriteBytes(make([]byte, 19)) // Padding?
- bf.WriteBytes(furniture)
- }
- }
+ bf.WriteBytes(houseTier)
+ bf.WriteBytes(houseData)
+ bf.WriteBytes(make([]byte, 19)) // Padding?
+ bf.WriteBytes(houseFurniture)
case 4: // Bookshelf
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.bookshelfData)
- }
- }
+ bf.WriteBytes(bookshelf)
case 5: // Gallery
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.galleryData)
- }
- }
+ bf.WriteBytes(gallery)
case 8: // Tore
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.toreData)
- }
- }
+ bf.WriteBytes(tore)
case 9: // Own house
- bf.WriteBytes(furniture)
+ bf.WriteBytes(houseFurniture)
case 10: // Garden
- for _, session := range s.server.sessions {
- if session.charID == pkt.CharID {
- bf.WriteBytes(session.myseries.gardenData)
- c, d := getGookData(s, pkt.CharID)
- bf.WriteUint16(c)
- bf.WriteUint16(0)
- bf.WriteBytes(d)
- }
- }
+ bf.WriteBytes(garden)
+ c, d := getGookData(s, pkt.CharID)
+ bf.WriteUint16(c)
+ bf.WriteUint16(0)
+ bf.WriteBytes(d)
}
if len(bf.Data()) == 0 {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
@@ -219,26 +226,18 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetMyhouseInfo)
-
var data []byte
- err := s.server.db.QueryRow("SELECT trophy FROM characters WHERE id = $1", s.charID).Scan(&data)
- if err != nil {
- panic(err)
- }
+ s.server.db.QueryRow(`SELECT mission FROM user_binary WHERE id=$1`, s.charID).Scan(&data)
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
- doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 9))
}
}
func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateMyhouseInfo)
-
- _, err := s.server.db.Exec("UPDATE characters SET trophy=$1 WHERE id=$2", pkt.Unk0, s.charID)
- if err != nil {
- panic(err)
- }
+ 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})
}
@@ -311,6 +310,7 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
}
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.Fatal("Failed to update decomyset savedata in db", zap.Error(err))
diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go
index 31bc26249..bdeb924de 100644
--- a/server/channelserver/handlers_mercenary.go
+++ b/server/channelserver/handlers_mercenary.go
@@ -104,6 +104,7 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
s.logger.Info("Wrote recompressed hunternavi back to DB.")
} else {
+ dumpSaveData(s, pkt.RawDataPayload, "hunternavi")
// simply update database, no extra processing
_, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
@@ -162,6 +163,7 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveMercenary)
+ dumpSaveData(s, pkt.MercData, "mercenary")
if len(pkt.MercData) > 0 {
s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", pkt.MercData, s.charID)
}
@@ -236,19 +238,31 @@ func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
return
}
bf := byteframe.NewByteFrameFromBytes(decomp)
+ save := byteframe.NewByteFrame()
+ var catsExist uint8
+ save.WriteUint8(0)
+
cats := bf.ReadUint8()
for i := 0; i < int(cats); i++ {
dataLen := bf.ReadUint32()
catID := bf.ReadUint32()
if catID == 0 {
- var nextID uint32
- _ = s.server.db.QueryRow("SELECT nextval('airou_id_seq')").Scan(&nextID)
- bf.Seek(-4, io.SeekCurrent)
- bf.WriteUint32(nextID)
+ _ = s.server.db.QueryRow("SELECT nextval('airou_id_seq')").Scan(&catID)
+ }
+ exists := bf.ReadBool()
+ data := bf.ReadBytes(uint(dataLen) - 5)
+ if exists {
+ catsExist++
+ save.WriteUint32(dataLen)
+ save.WriteUint32(catID)
+ save.WriteBool(exists)
+ save.WriteBytes(data)
}
- _ = bf.ReadBytes(uint(dataLen) - 4)
}
- comp, err := nullcomp.Compress(bf.Data())
+ save.WriteBytes(bf.DataFromCurrent())
+ save.Seek(0, 0)
+ save.WriteUint8(catsExist)
+ comp, err := nullcomp.Compress(save.Data())
if err != nil {
s.logger.Error("Failed to compress airou", zap.Error(err))
} else {
diff --git a/server/channelserver/handlers_plate.go b/server/channelserver/handlers_plate.go
index 72121ef13..172a5f134 100644
--- a/server/channelserver/handlers_plate.go
+++ b/server/channelserver/handlers_plate.go
@@ -12,20 +12,20 @@ func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) {
var data []byte
err := s.server.db.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.charID).Scan(&data)
if err != nil {
- s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err))
+ s.logger.Error("Failed to get plate data savedata from db", zap.Error(err))
}
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
- doAckBufSucceed(s, pkt.AckHandle, []byte{})
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
}
func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateData)
- dumpSaveData(s, pkt.RawDataPayload, "_platedata")
+ dumpSaveData(s, pkt.RawDataPayload, "platedata")
if pkt.IsDataDiff {
var data []byte
@@ -77,20 +77,20 @@ func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {
var data []byte
err := s.server.db.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.charID).Scan(&data)
if err != nil {
- s.logger.Fatal("Failed to get sigil box savedata from db", zap.Error(err))
+ s.logger.Error("Failed to get sigil box savedata from db", zap.Error(err))
}
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
- doAckBufSucceed(s, pkt.AckHandle, []byte{})
+ doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
}
func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateBox)
- dumpSaveData(s, pkt.RawDataPayload, "_platebox")
+ dumpSaveData(s, pkt.RawDataPayload, "platebox")
if pkt.IsDataDiff {
var data []byte
@@ -156,7 +156,7 @@ func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSavePlateMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateMyset)
// looks to always return the full thing, simply update database, no extra processing
-
+ dumpSaveData(s, pkt.RawDataPayload, "platemyset")
_, err := s.server.db.Exec("UPDATE characters SET platemyset=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update platemyset savedata in db", zap.Error(err))
diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go
index 816152072..bfa32fb73 100644
--- a/server/channelserver/handlers_quest.go
+++ b/server/channelserver/handlers_quest.go
@@ -2,7 +2,6 @@ package channelserver
import (
"fmt"
- "io"
"io/ioutil"
"os"
"path/filepath"
@@ -14,14 +13,24 @@ import (
func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysGetFile)
- // Debug print the request.
if pkt.IsScenario {
- fmt.Printf("%+v\n", pkt.ScenarioIdentifer)
+ if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
+ s.logger.Debug(
+ "Scenario",
+ zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID),
+ zap.Uint32("MainID", pkt.ScenarioIdentifer.MainID),
+ zap.Uint8("ChapterID", pkt.ScenarioIdentifer.ChapterID),
+ zap.Uint8("Flags", pkt.ScenarioIdentifer.Flags),
+ )
+ }
filename := fmt.Sprintf("%d_0_0_0_S%d_T%d_C%d", pkt.ScenarioIdentifer.CategoryID, pkt.ScenarioIdentifer.MainID, pkt.ScenarioIdentifer.Flags, pkt.ScenarioIdentifer.ChapterID)
// Read the scenario file.
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("scenarios/%s.bin", filename)))
if err != nil {
- panic(err)
+ s.logger.Error(fmt.Sprintf("Failed to open file: %s/scenarios/%s.bin", s.server.erupeConfig.BinPath, filename))
+ // This will crash the game.
+ doAckBufSucceed(s, pkt.AckHandle, data)
+ return
}
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
@@ -32,10 +41,19 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
}
doAckBufSucceed(s, pkt.AckHandle, data)
} else {
+ if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
+ s.logger.Debug(
+ "Quest",
+ zap.String("Filename", pkt.Filename),
+ )
+ }
// Get quest file.
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
if err != nil {
- s.logger.Fatal(fmt.Sprintf("Failed to open quest file: quests/%s.bin", pkt.Filename))
+ s.logger.Error(fmt.Sprintf("Failed to open file: %s/quests/%s.bin", s.server.erupeConfig.BinPath, pkt.Filename))
+ // This will crash the game.
+ doAckBufSucceed(s, pkt.AckHandle, data)
+ return
}
doAckBufSucceed(s, pkt.AckHandle, data)
}
@@ -55,6 +73,7 @@ func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveFavoriteQuest)
+ dumpSaveData(s, pkt.Data, "favquest")
s.server.db.Exec("UPDATE characters SET savefavoritequest=$1 WHERE id=$2", pkt.Data, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go
index 6fdce7c22..c1e8d2dbd 100644
--- a/server/channelserver/handlers_rengoku.go
+++ b/server/channelserver/handlers_rengoku.go
@@ -3,6 +3,7 @@ package channelserver
import (
ps "erupe-ce/common/pascalstring"
"fmt"
+ "github.com/jmoiron/sqlx"
"io/ioutil"
"path/filepath"
@@ -15,6 +16,7 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
// saved every floor on road, holds values such as floors progressed, points etc.
// can be safely handled by the client
pkt := p.(*mhfpacket.MsgMhfSaveRengokuData)
+ dumpSaveData(s, pkt.RawDataPayload, "rengoku")
_, err := s.server.db.Exec("UPDATE characters SET rengokudata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
if err != nil {
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
@@ -95,20 +97,13 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, data)
}
-const rengokuScoreQuery = `
-SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id
-FROM rengoku_score rs
+const rengokuScoreQuery = `, c.name FROM rengoku_score rs
LEFT JOIN characters c ON c.id = rs.character_id
-LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id
-`
+LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id `
type RengokuScore struct {
- Name string `db:"name"`
- GuildID int `db:"guild_id"`
- MaxStagesMP uint32 `db:"max_stages_mp"`
- MaxPointsMP uint32 `db:"max_points_mp"`
- MaxStagesSP uint32 `db:"max_stages_sp"`
- MaxPointsSP uint32 `db:"max_points_sp"`
+ Name string `db:"name"`
+ Score uint32 `db:"score"`
}
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
@@ -120,155 +115,61 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
guild = nil
}
+ if pkt.Leaderboard == 2 || pkt.Leaderboard == 3 || pkt.Leaderboard == 6 || pkt.Leaderboard == 7 {
+ if guild == nil {
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 11))
+ return
+ }
+ }
+
var score RengokuScore
+ var selfExist bool
i := uint32(1)
bf := byteframe.NewByteFrame()
scoreData := byteframe.NewByteFrame()
+
+ var rows *sqlx.Rows
switch pkt.Leaderboard {
- case 0: // Max stage overall MP
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxStagesMP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxStagesMP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
+ case 0:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
+ case 1:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s ORDER BY max_points_mp DESC", rengokuScoreQuery))
+ case 2:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID)
+ case 3:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID)
+ case 4:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s ORDER BY max_stages_sp DESC", rengokuScoreQuery))
+ case 5:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s ORDER BY max_points_sp DESC", rengokuScoreQuery))
+ case 6:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID)
+ case 7:
+ rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
+ }
+
+ for rows.Next() {
+ rows.StructScan(&score)
+ if score.Name == s.Name {
+ bf.WriteUint32(i)
+ bf.WriteUint32(score.Score)
+ ps.Uint8(bf, s.Name, true)
+ ps.Uint8(bf, "", false)
+ selfExist = true
+ }
+ if i > 100 {
i++
+ continue
}
- case 1: // Max RdP overall MP
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery))
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxPointsMP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxPointsMP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- case 2: // Max stage guild MP
- if guild != nil {
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID)
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxStagesMP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxStagesMP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- } else {
- bf.WriteBytes(make([]byte, 11))
- }
- case 3: // Max RdP guild MP
- if guild != nil {
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID)
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxPointsMP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxPointsMP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- } else {
- bf.WriteBytes(make([]byte, 11))
- }
- case 4: // Max stage overall SP
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery))
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxStagesSP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxStagesSP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- case 5: // Max RdP overall SP
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery))
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxPointsSP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxPointsSP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- case 6: // Max stage guild SP
- if guild != nil {
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID)
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxStagesSP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxStagesSP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- } else {
- bf.WriteBytes(make([]byte, 11))
- }
- case 7: // Max RdP guild SP
- if guild != nil {
- rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
- for rows.Next() {
- rows.StructScan(&score)
- if score.Name == s.Name {
- bf.WriteUint32(i)
- bf.WriteUint32(score.MaxPointsSP)
- ps.Uint8(bf, s.Name, true)
- ps.Uint8(bf, "", false)
- }
- scoreData.WriteUint32(i)
- scoreData.WriteUint32(score.MaxPointsSP)
- ps.Uint8(scoreData, score.Name, true)
- ps.Uint8(scoreData, "", false)
- i++
- }
- } else {
- bf.WriteBytes(make([]byte, 11))
- }
+ scoreData.WriteUint32(i)
+ scoreData.WriteUint32(score.Score)
+ ps.Uint8(scoreData, score.Name, true)
+ ps.Uint8(scoreData, "", false)
+ i++
+ }
+
+ if !selfExist {
+ bf.WriteBytes(make([]byte, 10))
}
bf.WriteUint8(uint8(i) - 1)
bf.WriteBytes(scoreData.Data())
diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go
index 906480be7..90ecdec99 100644
--- a/server/channelserver/handlers_stage.go
+++ b/server/channelserver/handlers_stage.go
@@ -19,6 +19,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} else {
stage := NewStage(pkt.StageID)
+ stage.host = s
stage.maxPlayers = uint16(pkt.PlayerCount)
s.server.stages[stage.id] = stage
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
@@ -42,6 +43,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
stage = s.server.stages[stageID]
s.server.Unlock()
stage.Lock()
+ stage.host = s
stage.clients[s] = s.charID
stage.Unlock()
}
diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go
index 49029753f..24cc64033 100644
--- a/server/channelserver/handlers_tower.go
+++ b/server/channelserver/handlers_tower.go
@@ -13,7 +13,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
type:
1 == TOWER_RANK_POINT,
2 == GET_OWN_TOWER_SKILL
- 3 == ?
+ 3 == GET_OWN_TOWER_LEVEL_V3
4 == TOWER_TOUHA_HISTORY
5 = ?
@@ -39,8 +39,8 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
case mhfpacket.TowerInfoTypeGetOwnTowerSkill:
//data, err = hex.DecodeString("0A218EAD000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000001C0000000500050000000000020000000000000000000000000000000000030003000000000003000500050000000300030003000300030003000200030001000300020002000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
- case mhfpacket.TowerInfoTypeUnk3:
- panic("No known response values for TowerInfoTypeUnk3")
+ case mhfpacket.TowerInfoTypeGetOwnTowerLevelV3:
+ panic("No known response values for GetOwnTowerLevelV3")
case mhfpacket.TowerInfoTypeTowerTouhaHistory:
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000000000000000000000000000")
case mhfpacket.TowerInfoTypeUnk5:
@@ -58,6 +58,9 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
-func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {}
+func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
+ pkt := p.(*mhfpacket.MsgMhfGetGemInfo)
+ doAckBufSucceed(s, pkt.AckHandle, make([]byte, 8))
+}
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {}
diff --git a/server/channelserver/handlers_users.go b/server/channelserver/handlers_users.go
index 3bebcc97a..c360b82a6 100644
--- a/server/channelserver/handlers_users.go
+++ b/server/channelserver/handlers_users.go
@@ -17,12 +17,12 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.userBinaryPartsLock.Unlock()
var exists []byte
- err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID).Scan(&exists)
+ err := s.server.db.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.charID).Scan(&exists)
if err != nil {
- s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID)
+ s.server.db.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.charID)
}
- s.server.db.Exec(fmt.Sprintf("UPDATE user_binaries SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID)
+ s.server.db.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID)
msg := &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
@@ -42,7 +42,7 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
// If we can't get the real data, try to get it from the database.
if !ok {
- err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
+ err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
} else {
diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go
index 20fc432f8..39a96f40f 100644
--- a/server/channelserver/sys_session.go
+++ b/server/channelserver/sys_session.go
@@ -1,22 +1,28 @@
package channelserver
import (
+ "encoding/binary"
"encoding/hex"
"fmt"
"io"
"net"
+ "strings"
"sync"
+ "time"
"erupe-ce/common/byteframe"
"erupe-ce/common/stringstack"
- "erupe-ce/common/stringsupport"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/network/mhfpacket"
"go.uber.org/zap"
- "golang.org/x/text/encoding/japanese"
)
+type packet struct {
+ data []byte
+ nonBlocking bool
+}
+
// Session holds state for the channel server connection.
type Session struct {
sync.Mutex
@@ -24,11 +30,10 @@ type Session struct {
server *Server
rawConn net.Conn
cryptConn *network.CryptConn
- sendPackets chan []byte
+ sendPackets chan packet
clientContext *clientctx.ClientContext
userEnteredStage bool // If the user has entered a stage before
- myseries MySeries
stageID string
stage *Stage
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
@@ -37,8 +42,10 @@ type Session struct {
charID uint32
logKey []byte
sessionStart int64
- rights uint32
+ courses []mhfpacket.Course
token string
+ kqf []byte
+ kqfOverride bool
semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet.
@@ -53,36 +60,21 @@ type Session struct {
mailList []int
// For Debuging
- Name string
-}
-
-type MySeries struct {
- houseTier []byte
- houseData []byte
- bookshelfData []byte
- galleryData []byte
- toreData []byte
- gardenData []byte
- state uint8
- password string
+ Name string
+ closed bool
}
// NewSession creates a new Session type.
func NewSession(server *Server, conn net.Conn) *Session {
s := &Session{
- logger: server.logger.Named(conn.RemoteAddr().String()),
- server: server,
- rawConn: conn,
- cryptConn: network.NewCryptConn(conn),
- sendPackets: make(chan []byte, 20),
- clientContext: &clientctx.ClientContext{
- StrConv: &stringsupport.StringConverter{
- Encoding: japanese.ShiftJIS,
- },
- },
- userEnteredStage: false,
- sessionStart: Time_Current_Adjusted().Unix(),
- stageMoveStack: stringstack.New(),
+ logger: server.logger.Named(conn.RemoteAddr().String()),
+ server: server,
+ rawConn: conn,
+ cryptConn: network.NewCryptConn(conn),
+ sendPackets: make(chan packet, 20),
+ clientContext: &clientctx.ClientContext{}, // Unused
+ sessionStart: Time_Current_Adjusted().Unix(),
+ stageMoveStack: stringstack.New(),
}
return s
}
@@ -100,19 +92,34 @@ func (s *Session) Start() {
// QueueSend queues a packet (raw []byte) to be sent.
func (s *Session) QueueSend(data []byte) {
- bf := byteframe.NewByteFrameFromBytes(data[:2])
- s.logMessage(bf.ReadUint16(), data, "Server", s.Name)
- s.sendPackets <- data
+ s.logMessage(binary.BigEndian.Uint16(data[0:2]), data, "Server", s.Name)
+ select {
+ case s.sendPackets <- packet{data, false}:
+ // Enqueued data
+ default:
+ s.logger.Warn("Packet queue too full, flushing!")
+ var tempPackets []packet
+ for len(s.sendPackets) > 0 {
+ tempPacket := <-s.sendPackets
+ if !tempPacket.nonBlocking {
+ tempPackets = append(tempPackets, tempPacket)
+ }
+ }
+ for _, tempPacket := range tempPackets {
+ s.sendPackets <- tempPacket
+ }
+ s.sendPackets <- packet{data, false}
+ }
}
// QueueSendNonBlocking queues a packet (raw []byte) to be sent, dropping the packet entirely if the queue is full.
func (s *Session) QueueSendNonBlocking(data []byte) {
select {
- case s.sendPackets <- data:
- // Enqueued properly.
+ case s.sendPackets <- packet{data, true}:
+ // Enqueued data
default:
- // Couldn't enqueue, likely something wrong with the connection.
- s.logger.Warn("Dropped packet for session because of full send buffer, something is probably wrong")
+ s.logger.Warn("Packet queue too full, dropping!")
+ // Queue too full
}
}
@@ -140,29 +147,25 @@ func (s *Session) QueueAck(ackHandle uint32, data []byte) {
func (s *Session) sendLoop() {
for {
- // TODO(Andoryuuta): Test making this into a buffered channel and grouping the packet together before sending.
- rawPacket := <-s.sendPackets
-
- if rawPacket == nil {
- s.logger.Debug("Got nil from s.SendPackets, exiting send loop")
+ if s.closed {
return
}
-
- // Make a copy of the data.
- terminatedPacket := make([]byte, len(rawPacket))
- copy(terminatedPacket, rawPacket)
-
- // Append the MSG_SYS_END tailing opcode.
- terminatedPacket = append(terminatedPacket, []byte{0x00, 0x10}...)
-
- s.cryptConn.SendPacket(terminatedPacket)
+ pkt := <-s.sendPackets
+ err := s.cryptConn.SendPacket(append(pkt.data, []byte{0x00, 0x10}...))
+ if err != nil {
+ s.logger.Warn("Failed to send packet")
+ }
+ time.Sleep(10 * time.Millisecond)
}
}
func (s *Session) recvLoop() {
for {
+ if s.closed {
+ logoutPlayer(s)
+ return
+ }
pkt, err := s.cryptConn.ReadPacket()
-
if err == io.EOF {
s.logger.Info(fmt.Sprintf("[%s] Disconnected", s.Name))
logoutPlayer(s)
@@ -174,6 +177,7 @@ func (s *Session) recvLoop() {
return
}
s.handlePacketGroup(pkt)
+ time.Sleep(10 * time.Millisecond)
}
}
@@ -193,7 +197,8 @@ func (s *Session) handlePacketGroup(pktGroup []byte) {
s.logMessage(opcodeUint16, pktGroup, s.Name, "Server")
if opcode == network.MSG_SYS_LOGOUT {
- s.rawConn.Close()
+ s.closed = true
+ return
}
// Get the packet parser and handler for this opcode.
mhfPkt := mhfpacket.FromOpcode(opcode)
@@ -258,3 +263,14 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data))
}
}
+
+func (s *Session) FindCourse(name string) mhfpacket.Course {
+ for _, course := range s.courses {
+ for _, alias := range course.Aliases {
+ if strings.ToLower(name) == strings.ToLower(alias) {
+ return course
+ }
+ }
+ }
+ return mhfpacket.Course{}
+}
diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go
index 827b2d6be..4cd96a7f8 100644
--- a/server/channelserver/sys_stage.go
+++ b/server/channelserver/sys_stage.go
@@ -46,6 +46,7 @@ type Stage struct {
// other clients expect the server to echo them back in the exact same format.
rawBinaryData map[stageBinaryKey][]byte
+ host *Session
maxPlayers uint16
password string
createdAt string
diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go
index 56f300c27..706aac8b3 100644
--- a/server/entranceserver/entrance_server.go
+++ b/server/entranceserver/entrance_server.go
@@ -90,6 +90,7 @@ func (s *Server) acceptClients() {
}
func (s *Server) handleEntranceServerConnection(conn net.Conn) {
+ defer conn.Close()
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
n, err := io.ReadFull(conn, nullInit)
@@ -118,5 +119,4 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
cc.SendPacket(data)
// Close because we only need to send the response once.
// Any further requests from the client will come from a new connection.
- conn.Close()
}
diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go
index c0358faef..0df241260 100644
--- a/server/entranceserver/make_resp.go
+++ b/server/entranceserver/make_resp.go
@@ -25,7 +25,7 @@ func encodeServerInfo(config *config.Config, s *Server) []byte {
sid := (4096 + serverIdx*256) + 16
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
if err != nil {
- panic(err)
+ season = 0
}
if si.IP == "" {
si.IP = config.Host
@@ -50,7 +50,7 @@ func encodeServerInfo(config *config.Config, s *Server) []byte {
bf.WriteUint16(ci.MaxPlayers)
err := s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(¤tplayers)
if err != nil {
- panic(err)
+ currentplayers = 0
}
bf.WriteUint16(currentplayers)
bf.WriteUint32(0)
diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go
index d39d96c92..213a74a14 100644
--- a/server/signserver/dbutils.go
+++ b/server/signserver/dbutils.go
@@ -63,7 +63,7 @@ func (s *Server) registerDBAccount(username string, password string) error {
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hrp, gr, weapon_type, last_login)
- VALUES($1, False, True, '', '', 1, 0, 0, $2)`,
+ VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
id,
uint32(time.Now().Unix()),
)
@@ -148,7 +148,7 @@ func (s *Server) getFriendsForCharacters(chars []character) []members {
if err != nil {
continue
}
- for i, _ := range charFriends {
+ for i := range charFriends {
charFriends[i].CID = char.ID
}
friends = append(friends, charFriends...)
diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go
index a169d93a1..3966af8d6 100644
--- a/server/signserver/dsgn_resp.go
+++ b/server/signserver/dsgn_resp.go
@@ -42,13 +42,23 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf := byteframe.NewByteFrame()
- bf.WriteUint8(1) // resp_code
- bf.WriteUint8(0) // file/patch server count
+ bf.WriteUint8(1) // resp_code
+ if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" {
+ bf.WriteUint8(2)
+ } else {
+ bf.WriteUint8(0)
+ }
bf.WriteUint8(1) // entrance server count
bf.WriteUint8(uint8(len(chars))) // character count
bf.WriteUint32(0xFFFFFFFF) // login_token_number
bf.WriteBytes([]byte(token)) // login_token
bf.WriteUint32(uint32(time.Now().Unix())) // current time
+ if s.server.erupeConfig.DevMode {
+ if s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" {
+ ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerManifest, false)
+ ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerFile, false)
+ }
+ }
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
lastPlayed := uint32(0)
@@ -112,14 +122,22 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint32(s.server.getLastCID(uid))
bf.WriteUint32(s.server.getUserRights(uid))
ps.Uint16(bf, "", false) // filters
- bf.WriteUint32(0xCA104E20)
- ps.Uint16(bf, "", false) // encryption
+ bf.WriteUint16(0xCA10)
+ bf.WriteUint16(0x4E20)
+ ps.Uint16(bf, "", false) // unk key
bf.WriteUint8(0x00)
- bf.WriteUint32(0xCA110001)
- bf.WriteUint32(0x4E200000)
- bf.WriteUint32(uint32(returnExpiry.Unix()))
+ bf.WriteUint16(0xCA11)
+ bf.WriteUint16(0x0001)
+ bf.WriteUint16(0x4E20)
+ ps.Uint16(bf, "", false) // unk ipv4
+ if returnExpiry.Before(time.Now()) {
+ // Hack to make Return work while having a non-adjusted expiry
+ bf.WriteUint32(0)
+ } else {
+ bf.WriteUint32(uint32(returnExpiry.Unix()))
+ }
bf.WriteUint32(0x00000000)
- bf.WriteUint32(0x0A5197DF)
+ bf.WriteUint32(0x0A5197DF) // unk id
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
diff --git a/server/signserver/session.go b/server/signserver/session.go
index aef6508b1..6b2e38b8c 100644
--- a/server/signserver/session.go
+++ b/server/signserver/session.go
@@ -16,32 +16,19 @@ import (
type Session struct {
sync.Mutex
logger *zap.Logger
- sid int
server *Server
- rawConn *net.Conn
+ rawConn net.Conn
cryptConn *network.CryptConn
}
-func (s *Session) fail() {
- s.server.Lock()
- delete(s.server.sessions, s.sid)
- s.server.Unlock()
-
-}
-
func (s *Session) work() {
- for {
- pkt, err := s.cryptConn.ReadPacket()
- if err != nil {
- s.fail()
- return
- }
-
- err = s.handlePacket(pkt)
- if err != nil {
- s.fail()
- return
- }
+ pkt, err := s.cryptConn.ReadPacket()
+ if err != nil {
+ return
+ }
+ err = s.handlePacket(pkt)
+ if err != nil {
+ return
}
}
@@ -61,6 +48,7 @@ func (s *Session) handlePacket(pkt []byte) error {
case "DELETE:100":
loginTokenString := string(bf.ReadNullTerminatedBytes())
characterID := int(bf.ReadUint32())
+ _ = int(bf.ReadUint32()) // login_token_number
s.server.deleteCharacter(characterID, loginTokenString)
sugar.Infof("Deleted character ID: %v\n", characterID)
err := s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS
@@ -78,13 +66,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
reqUsername := string(bf.ReadNullTerminatedBytes())
reqPassword := string(bf.ReadNullTerminatedBytes())
- reqUnk := string(bf.ReadNullTerminatedBytes())
+ reqSkey := string(bf.ReadNullTerminatedBytes())
s.server.logger.Info(
"Got sign in request",
zap.String("reqUsername", reqUsername),
zap.String("reqPassword", reqPassword),
- zap.String("reqUnk", reqUnk),
+ zap.String("reqSkey", reqSkey),
)
newCharaReq := false
@@ -105,12 +93,15 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
s.logger.Info("Account not found", zap.String("reqUsername", reqUsername))
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
- // HACK(Andoryuuta): Create a new account if it doesn't exit.
- s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword))
- err = s.server.registerDBAccount(reqUsername, reqPassword)
- if err != nil {
- s.logger.Info("Error on creating new account", zap.Error(err))
- serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
+ if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount {
+ s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword))
+ err = s.server.registerDBAccount(reqUsername, reqPassword)
+ if err != nil {
+ s.logger.Info("Error on creating new account", zap.Error(err))
+ serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
+ break
+ }
+ } else {
break
}
diff --git a/server/signserver/sign_server.go b/server/signserver/sign_server.go
index 02bafb10d..6d567e126 100644
--- a/server/signserver/sign_server.go
+++ b/server/signserver/sign_server.go
@@ -24,7 +24,6 @@ type Server struct {
sync.Mutex
logger *zap.Logger
erupeConfig *config.Config
- sid int
sessions map[int]*Session
db *sqlx.DB
listener net.Listener
@@ -36,8 +35,6 @@ func NewServer(config *Config) *Server {
s := &Server{
logger: config.Logger,
erupeConfig: config.ErupeConfig,
- sid: 0,
- sessions: make(map[int]*Session),
db: config.DB,
}
return s
@@ -84,20 +81,19 @@ func (s *Server) acceptClients() {
}
}
- go s.handleConnection(s.sid, conn)
- s.sid++
+ go s.handleConnection(conn)
}
}
-func (s *Server) handleConnection(sid int, conn net.Conn) {
+func (s *Server) handleConnection(conn net.Conn) {
s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String()))
+ defer conn.Close()
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
_, err := io.ReadFull(conn, nullInit)
if err != nil {
- fmt.Println(err)
- conn.Close()
+ s.logger.Error("Error initialising sign server connection", zap.Error(err))
return
}
@@ -105,15 +101,10 @@ func (s *Server) handleConnection(sid int, conn net.Conn) {
session := &Session{
logger: s.logger,
server: s,
- rawConn: &conn,
+ rawConn: conn,
cryptConn: network.NewCryptConn(conn),
}
- // Add the session to the server's sessions map.
- s.Lock()
- s.sessions[sid] = session
- s.Unlock()
-
// Do the session's work.
session.work()
}