diff --git a/bundled-schema/RoadShopItems.sql b/bundled-schema/RoadShopItems.sql index 593a3a3cf..31c157344 100644 --- a/bundled-schema/RoadShopItems.sql +++ b/bundled-schema/RoadShopItems.sql @@ -1,918 +1,868 @@ BEGIN; -INSERT INTO public.normal_shop_items -(shoptype, shopid, itemhash, itemid, points, tradequantity, rankreqlow, rankreqhigh, rankreqg, storelevelreq, maximumquantity, boughtquantity, roadfloorsrequired, weeklyfataliskills) +INSERT INTO public.shop_items +(shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis) VALUES - (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); + (10,4,11664,20000,1,0,0,1,1,0,0,0), + (10,4,11665,20000,1,0,0,1,1,0,0,0), + (10,4,11666,20000,1,0,0,1,1,0,0,0), + (10,4,11667,20000,1,0,0,1,1,0,0,0), + (10,4,11668,20000,1,0,0,1,1,0,0,0), + (10,4,11669,20000,1,0,0,1,1,0,0,0), + (10,4,11670,20000,1,0,0,1,1,0,0,0), + (10,4,11671,20000,1,0,0,1,1,0,0,0), + (10,4,11672,20000,1,0,0,1,1,0,0,0), + (10,4,11673,20000,1,0,0,1,1,0,0,0), + (10,4,11674,20000,1,0,0,1,1,0,0,0), + (10,4,11675,20000,1,0,0,1,1,0,0,0), + (10,4,11676,20000,1,0,0,1,1,0,0,0), + (10,4,11677,20000,1,0,0,1,1,0,0,0), + (10,4,11678,20000,1,0,0,1,1,0,0,0), + (10,4,11679,20000,1,0,0,1,1,0,0,0), + (10,4,11680,20000,1,0,0,1,1,0,0,0), + (10,4,11681,20000,1,0,0,1,1,0,0,0), + (10,4,11682,20000,1,0,0,1,1,0,0,0), + (10,4,11683,20000,1,0,0,1,1,0,0,0), + (10,4,11684,20000,1,0,0,1,1,0,0,0), + (10,4,11685,20000,1,0,0,1,1,0,0,0), + (10,4,11686,20000,1,0,0,1,1,0,0,0), + (10,4,11687,20000,1,0,0,1,1,0,0,0), + (10,4,11688,20000,1,0,0,1,1,0,0,0), + (10,4,11689,20000,1,0,0,1,1,0,0,0), + (10,4,11690,20000,1,0,0,1,1,0,0,0), + (10,4,11691,20000,1,0,0,1,1,0,0,0), + (10,4,11692,20000,1,0,0,1,1,0,0,0), + (10,4,11693,20000,1,0,0,1,1,0,0,0), + (10,4,11694,20000,1,0,0,1,1,0,0,0), + (10,4,11695,20000,1,0,0,1,1,0,0,0), + (10,4,11696,20000,1,0,0,1,1,0,0,0), + (10,4,11697,20000,1,0,0,1,1,0,0,0), + (10,4,12893,20000,1,0,0,1,1,0,0,0), + (10,4,12894,20000,1,0,0,1,1,0,0,0), + (10,4,12895,20000,1,0,0,1,1,0,0,0), + (10,4,12896,20000,1,0,0,1,1,0,0,0), + (10,4,12897,20000,1,0,0,1,1,0,0,0), + (10,4,12898,20000,1,0,0,1,1,0,0,0), + (10,4,12899,20000,1,0,0,1,1,0,0,0), + (10,4,14337,20000,1,0,0,1,1,0,0,0), + (10,4,14338,20000,1,0,0,1,1,0,0,0), + (10,4,14339,20000,1,0,0,1,1,0,0,0), + (10,4,14340,20000,1,0,0,1,1,0,0,0), + (10,4,14341,20000,1,0,0,1,1,0,0,0), + (10,4,14342,20000,1,0,0,1,1,0,0,0), + (10,4,14343,20000,1,0,0,1,1,0,0,0), + (10,4,14344,20000,1,0,0,1,1,0,0,0), + (10,4,14345,20000,1,0,0,1,1,0,0,0), + (10,4,9254,10000,1,0,0,1,1,0,0,0), + (10,4,9255,10000,1,0,0,1,1,0,0,0), + (10,4,9256,10000,1,0,0,1,1,0,0,0), + (10,4,9257,10000,1,0,0,1,1,0,0,0), + (10,4,9258,10000,1,0,0,1,1,0,0,0), + (10,4,9259,10000,1,0,0,1,1,0,0,0), + (10,4,9260,10000,1,0,0,1,1,0,0,0), + (10,4,9261,10000,1,0,0,1,1,0,0,0), + (10,4,9262,10000,1,0,0,1,1,0,0,0), + (10,4,9263,10000,1,0,0,1,1,0,0,0), + (10,4,9264,10000,1,0,0,1,1,0,0,0), + (10,4,9265,10000,1,0,0,1,1,0,0,0), + (10,4,9266,10000,1,0,0,1,1,0,0,0), + (10,4,9267,10000,1,0,0,1,1,0,0,0), + (10,4,9268,10000,1,0,0,1,1,0,0,0), + (10,4,9269,10000,1,0,0,1,1,0,0,0), + (10,4,9270,10000,1,0,0,1,1,0,0,0), + (10,4,9271,10000,1,0,0,1,1,0,0,0), + (10,4,9272,10000,1,0,0,1,1,0,0,0), + (10,4,9273,10000,1,0,0,1,1,0,0,0), + (10,4,9274,10000,1,0,0,1,1,0,0,0), + (10,4,9275,10000,1,0,0,1,1,0,0,0), + (10,4,9276,10000,1,0,0,1,1,0,0,0), + (10,4,9277,10000,1,0,0,1,1,0,0,0), + (10,4,9278,10000,1,0,0,1,1,0,0,0), + (10,4,9279,10000,1,0,0,1,1,0,0,0), + (10,4,9280,10000,1,0,0,1,1,0,0,0), + (10,4,9281,10000,1,0,0,1,1,0,0,0), + (10,4,9282,10000,1,0,0,1,1,0,0,0), + (10,4,9283,10000,1,0,0,1,1,0,0,0), + (10,4,9284,10000,1,0,0,1,1,0,0,0), + (10,4,9285,10000,1,0,0,1,1,0,0,0), + (10,4,9286,10000,1,0,0,1,1,0,0,0), + (10,4,9287,10000,1,0,0,1,1,0,0,0), + (10,4,9288,10000,1,0,0,1,1,0,0,0), + (10,4,9289,10000,1,0,0,1,1,0,0,0), + (10,4,9290,10000,1,0,0,1,1,0,0,0), + (10,4,9291,10000,1,0,0,1,1,0,0,0), + (10,4,9292,10000,1,0,0,1,1,0,0,0), + (10,4,9293,10000,1,0,0,1,1,0,0,0), + (10,4,9294,10000,1,0,0,1,1,0,0,0), + (10,4,9295,10000,1,0,0,1,1,0,0,0), + (10,4,9296,10000,1,0,0,1,1,0,0,0), + (10,4,9297,10000,1,0,0,1,1,0,0,0), + (10,4,9298,10000,1,0,0,1,1,0,0,0), + (10,4,9299,10000,1,0,0,1,1,0,0,0), + (10,4,9300,10000,1,0,0,1,1,0,0,0), + (10,4,9301,10000,1,0,0,1,1,0,0,0), + (10,4,13196,10000,1,0,0,1,1,0,0,0), + (10,4,13197,10000,1,0,0,1,1,0,0,0), + (10,4,13198,10000,1,0,0,1,1,0,0,0), + (10,4,13199,10000,1,0,0,1,1,0,0,0), + (10,4,15542,10000,1,0,0,1,1,0,0,0), + (10,4,15543,10000,1,0,0,1,1,0,0,0), + (10,4,15544,10000,1,0,0,1,1,0,0,0), + (10,4,15545,10000,1,0,0,1,1,0,0,0), + (10,4,13640,20000,1,0,0,1,1,0,0,0), + (10,4,13641,20000,1,0,0,1,1,0,0,0), + (10,4,13642,20000,1,0,0,1,1,0,0,0), + (10,4,13643,20000,1,0,0,1,1,0,0,0), + (10,4,13644,20000,1,0,0,1,1,0,0,0), + (10,4,13645,20000,1,0,0,1,1,0,0,0), + (10,4,13646,20000,1,0,0,1,1,0,0,0), + (10,4,13647,20000,1,0,0,1,1,0,0,0), + (10,4,13648,20000,1,0,0,1,1,0,0,0), + (10,4,13649,20000,1,0,0,1,1,0,0,0), + (10,4,13650,20000,1,0,0,1,1,0,0,0), + (10,4,13651,20000,1,0,0,1,1,0,0,0), + (10,4,13652,20000,1,0,0,1,1,0,0,0), + (10,4,13653,20000,1,0,0,1,1,0,0,0), + (10,4,13654,20000,1,0,0,1,1,0,0,0), + (10,4,13655,20000,1,0,0,1,1,0,0,0), + (10,4,13656,20000,1,0,0,1,1,0,0,0), + (10,4,13657,20000,1,0,0,1,1,0,0,0), + (10,4,13658,20000,1,0,0,1,1,0,0,0), + (10,4,13659,20000,1,0,0,1,1,0,0,0), + (10,4,13660,20000,1,0,0,1,1,0,0,0), + (10,4,13661,20000,1,0,0,1,1,0,0,0), + (10,4,13662,20000,1,0,0,1,1,0,0,0), + (10,4,13663,20000,1,0,0,1,1,0,0,0), + (10,4,13664,20000,1,0,0,1,1,0,0,0), + (10,4,13665,20000,1,0,0,1,1,0,0,0), + (10,4,13666,20000,1,0,0,1,1,0,0,0), + (10,4,13667,20000,1,0,0,1,1,0,0,0), + (10,4,13668,20000,1,0,0,1,1,0,0,0), + (10,4,13669,20000,1,0,0,1,1,0,0,0), + (10,4,13670,20000,1,0,0,1,1,0,0,0), + (10,4,13671,20000,1,0,0,1,1,0,0,0), + (10,4,13672,20000,1,0,0,1,1,0,0,0), + (10,4,13673,20000,1,0,0,1,1,0,0,0), + (10,4,13674,20000,1,0,0,1,1,0,0,0), + (10,4,13675,20000,1,0,0,1,1,0,0,0), + (10,4,13676,20000,1,0,0,1,1,0,0,0), + (10,4,13677,20000,1,0,0,1,1,0,0,0), + (10,4,13678,20000,1,0,0,1,1,0,0,0), + (10,4,13679,20000,1,0,0,1,1,0,0,0), + (10,4,13680,20000,1,0,0,1,1,0,0,0), + (10,4,13681,20000,1,0,0,1,1,0,0,0), + (10,4,13682,20000,1,0,0,1,1,0,0,0), + (10,4,13683,20000,1,0,0,1,1,0,0,0), + (10,4,13684,20000,1,0,0,1,1,0,0,0), + (10,4,13685,20000,1,0,0,1,1,0,0,0), + (10,4,13686,20000,1,0,0,1,1,0,0,0), + (10,4,13687,20000,1,0,0,1,1,0,0,0), + (10,4,13688,20000,1,0,0,1,1,0,0,0), + (10,4,13689,20000,1,0,0,1,1,0,0,0), + (10,4,13690,20000,1,0,0,1,1,0,0,0), + (10,4,13691,20000,1,0,0,1,1,0,0,0), + (10,4,15546,20000,1,0,0,1,1,0,0,0), + (10,4,15547,20000,1,0,0,1,1,0,0,0), + (10,4,15548,20000,1,0,0,1,1,0,0,0), + (10,4,15549,20000,1,0,0,1,1,0,0,0), + (10,4,16162,35000,1,0,0,1,1,0,0,0), + (10,4,16163,35000,1,0,0,1,1,0,0,0), + (10,4,16164,35000,1,0,0,1,1,0,0,0), + (10,4,16165,35000,1,0,0,1,1,0,0,0), + (10,4,16166,35000,1,0,0,1,1,0,0,0), + (10,4,16167,35000,1,0,0,1,1,0,0,0), + (10,4,16168,35000,1,0,0,1,1,0,0,0), + (10,4,16169,35000,1,0,0,1,1,0,0,0), + (10,4,16172,35000,1,0,0,1,1,0,0,0), + (10,4,16173,35000,1,0,0,1,1,0,0,0), + (10,4,16174,35000,1,0,0,1,1,0,0,0), + (10,4,16175,35000,1,0,0,1,1,0,0,0), + (10,4,16176,35000,1,0,0,1,1,0,0,0), + (10,4,16177,35000,1,0,0,1,1,0,0,0), + (10,4,16178,35000,1,0,0,1,1,0,0,0), + (10,4,16179,35000,1,0,0,1,1,0,0,0), + (10,4,16182,35000,1,0,0,1,1,0,0,0), + (10,4,16183,35000,1,0,0,1,1,0,0,0), + (10,4,16184,35000,1,0,0,1,1,0,0,0), + (10,4,16185,35000,1,0,0,1,1,0,0,0), + (10,4,16186,35000,1,0,0,1,1,0,0,0), + (10,4,16187,35000,1,0,0,1,1,0,0,0), + (10,4,16188,35000,1,0,0,1,1,0,0,0), + (10,4,16189,35000,1,0,0,1,1,0,0,0), + (10,4,16192,35000,1,0,0,1,1,0,0,0), + (10,4,16193,35000,1,0,0,1,1,0,0,0), + (10,4,16194,35000,1,0,0,1,1,0,0,0), + (10,4,16195,35000,1,0,0,1,1,0,0,0), + (10,4,16196,35000,1,0,0,1,1,0,0,0), + (10,4,16197,35000,1,0,0,1,1,0,0,0), + (10,4,16198,35000,1,0,0,1,1,0,0,0), + (10,4,16199,35000,1,0,0,1,1,0,0,0), + (10,4,16202,35000,1,0,0,1,1,0,0,0), + (10,4,16203,35000,1,0,0,1,1,0,0,0), + (10,4,16204,35000,1,0,0,1,1,0,0,0), + (10,4,16205,35000,1,0,0,1,1,0,0,0), + (10,4,16206,35000,1,0,0,1,1,0,0,0), + (10,4,16207,35000,1,0,0,1,1,0,0,0), + (10,4,16208,35000,1,0,0,1,1,0,0,0), + (10,4,16209,35000,1,0,0,1,1,0,0,0), + (10,4,16212,35000,1,0,0,1,1,0,0,0), + (10,4,16213,35000,1,0,0,1,1,0,0,0), + (10,4,16214,35000,1,0,0,1,1,0,0,0), + (10,4,16215,35000,1,0,0,1,1,0,0,0), + (10,4,16216,35000,1,0,0,1,1,0,0,0), + (10,4,16217,35000,1,0,0,1,1,0,0,0), + (10,4,16218,35000,1,0,0,1,1,0,0,0), + (10,4,16219,35000,1,0,0,1,1,0,0,0), + (10,4,16222,35000,1,0,0,1,1,0,0,0), + (10,4,16223,35000,1,0,0,1,1,0,0,0), + (10,4,16224,35000,1,0,0,1,1,0,0,0), + (10,4,16225,35000,1,0,0,1,1,0,0,0), + (10,4,16226,35000,1,0,0,1,1,0,0,0), + (10,4,16227,35000,1,0,0,1,1,0,0,0), + (10,4,16228,35000,1,0,0,1,1,0,0,0), + (10,4,16229,35000,1,0,0,1,1,0,0,0), + (10,4,16232,35000,1,0,0,1,1,0,0,0), + (10,4,16233,35000,1,0,0,1,1,0,0,0), + (10,4,16234,35000,1,0,0,1,1,0,0,0), + (10,4,16235,35000,1,0,0,1,1,0,0,0), + (10,4,16236,35000,1,0,0,1,1,0,0,0), + (10,4,16237,35000,1,0,0,1,1,0,0,0), + (10,4,16238,35000,1,0,0,1,1,0,0,0), + (10,4,16239,35000,1,0,0,1,1,0,0,0), + (10,4,16242,35000,1,0,0,1,1,0,0,0), + (10,4,16243,35000,1,0,0,1,1,0,0,0), + (10,4,16244,35000,1,0,0,1,1,0,0,0), + (10,4,16245,35000,1,0,0,1,1,0,0,0), + (10,4,16246,35000,1,0,0,1,1,0,0,0), + (10,4,16247,35000,1,0,0,1,1,0,0,0), + (10,4,16248,35000,1,0,0,1,1,0,0,0), + (10,4,16249,35000,1,0,0,1,1,0,0,0), + (10,4,16252,35000,1,0,0,1,1,0,0,0), + (10,4,16253,35000,1,0,0,1,1,0,0,0), + (10,4,16254,35000,1,0,0,1,1,0,0,0), + (10,4,16255,35000,1,0,0,1,1,0,0,0), + (10,4,16256,35000,1,0,0,1,1,0,0,0), + (10,4,16257,35000,1,0,0,1,1,0,0,0), + (10,4,16258,35000,1,0,0,1,1,0,0,0), + (10,4,16259,35000,1,0,0,1,1,0,0,0), + (10,4,16262,35000,1,0,0,1,1,0,0,0), + (10,4,16263,35000,1,0,0,1,1,0,0,0), + (10,4,16264,35000,1,0,0,1,1,0,0,0), + (10,4,16265,35000,1,0,0,1,1,0,0,0), + (10,4,16266,35000,1,0,0,1,1,0,0,0), + (10,4,16267,35000,1,0,0,1,1,0,0,0), + (10,4,16268,35000,1,0,0,1,1,0,0,0), + (10,4,16269,35000,1,0,0,1,1,0,0,0), + (10,4,16272,35000,1,0,0,1,1,0,0,0), + (10,4,16273,35000,1,0,0,1,1,0,0,0), + (10,4,16274,35000,1,0,0,1,1,0,0,0), + (10,4,16275,35000,1,0,0,1,1,0,0,0), + (10,4,16276,35000,1,0,0,1,1,0,0,0), + (10,4,16277,35000,1,0,0,1,1,0,0,0), + (10,4,16278,35000,1,0,0,1,1,0,0,0), + (10,4,16279,35000,1,0,0,1,1,0,0,0), + (10,4,16282,35000,1,0,0,1,1,0,0,0), + (10,4,16283,35000,1,0,0,1,1,0,0,0), + (10,4,16284,35000,1,0,0,1,1,0,0,0), + (10,4,16285,35000,1,0,0,1,1,0,0,0), + (10,4,16286,35000,1,0,0,1,1,0,0,0), + (10,4,16287,35000,1,0,0,1,1,0,0,0), + (10,4,16288,35000,1,0,0,1,1,0,0,0), + (10,4,16289,35000,1,0,0,1,1,0,0,0), + (10,4,16292,35000,1,0,0,1,1,0,0,0), + (10,4,16293,35000,1,0,0,1,1,0,0,0), + (10,4,16294,35000,1,0,0,1,1,0,0,0), + (10,4,16295,35000,1,0,0,1,1,0,0,0), + (10,4,16296,35000,1,0,0,1,1,0,0,0), + (10,4,16297,35000,1,0,0,1,1,0,0,0), + (10,4,16298,35000,1,0,0,1,1,0,0,0), + (10,4,16299,35000,1,0,0,1,1,0,0,0), + (10,8,14136,15000,1,0,0,1,1,0,0,0), + (10,8,14137,15000,1,0,0,1,1,0,0,0), + (10,8,14138,15000,1,0,0,1,1,0,0,0), + (10,8,14139,15000,1,0,0,1,1,0,0,0), + (10,8,14140,15000,1,0,0,1,1,0,0,0), + (10,8,14141,15000,1,0,0,1,1,0,0,0), + (10,8,14142,15000,1,0,0,1,1,0,0,0), + (10,8,14143,15000,1,0,0,1,1,0,0,0), + (10,8,14144,15000,1,0,0,1,1,0,0,0), + (10,8,14145,15000,1,0,0,1,1,0,0,0), + (10,8,14454,30000,1,0,0,1,1,0,0,0), + (10,8,14455,30000,1,0,0,1,1,0,0,0), + (10,8,14456,30000,1,0,0,1,1,0,0,0), + (10,8,14457,30000,1,0,0,1,1,0,0,0), + (10,8,14458,30000,1,0,0,1,1,0,0,0), + (10,8,14459,30000,1,0,0,1,1,0,0,0), + (10,8,14460,30000,1,0,0,1,1,0,0,0), + (10,8,14461,30000,1,0,0,1,1,0,0,0), + (10,8,14462,30000,1,0,0,1,1,0,0,0), + (10,8,14463,30000,1,0,0,1,1,0,0,0), + (10,8,12724,50000,1,0,0,1,1,0,0,0), + (10,8,12725,50000,1,0,0,1,1,0,0,0), + (10,8,12726,50000,1,0,0,1,1,0,0,0), + (10,8,12727,50000,1,0,0,1,1,0,0,0), + (10,8,12728,50000,1,0,0,1,1,0,0,0), + (10,8,12729,50000,1,0,0,1,1,0,0,0), + (10,8,12730,50000,1,0,0,1,1,0,0,0), + (10,8,12731,50000,1,0,0,1,1,0,0,0), + (10,8,12732,50000,1,0,0,1,1,0,0,0), + (10,8,12733,50000,1,0,0,1,1,0,0,0), + (10,8,12734,50000,1,0,0,1,1,0,0,0), + (10,8,12735,50000,1,0,0,1,1,0,0,0), + (10,8,12736,50000,1,0,0,1,1,0,0,0), + (10,8,12737,50000,1,0,0,1,1,0,0,0), + (10,8,12738,50000,1,0,0,1,1,0,0,0), + (10,8,12739,50000,1,0,0,1,1,0,0,0), + (10,8,12740,50000,1,0,0,1,1,0,0,0), + (10,8,12741,50000,1,0,0,1,1,0,0,0), + (10,8,12742,50000,1,0,0,1,1,0,0,0), + (10,8,12743,50000,1,0,0,1,1,0,0,0), + (10,8,12744,50000,1,0,0,1,1,0,0,0), + (10,8,12745,50000,1,0,0,1,1,0,0,0), + (10,8,12746,50000,1,0,0,1,1,0,0,0), + (10,8,12747,50000,1,0,0,1,1,0,0,0), + (10,8,12748,50000,1,0,0,1,1,0,0,0), + (10,8,12749,50000,1,0,0,1,1,0,0,0), + (10,8,12750,50000,1,0,0,1,1,0,0,0), + (10,8,12751,50000,1,0,0,1,1,0,0,0), + (10,8,12752,50000,1,0,0,1,1,0,0,0), + (10,8,12753,50000,1,0,0,1,1,0,0,0), + (10,8,15070,50000,1,0,0,1,1,0,0,0), + (10,8,15071,50000,1,0,0,1,1,0,0,0), + (10,8,15072,50000,1,0,0,1,1,0,0,0), + (10,8,15073,50000,1,0,0,1,1,0,0,0), + (10,8,15074,50000,1,0,0,1,1,0,0,0), + (10,8,15075,50000,1,0,0,1,1,0,0,0), + (10,8,15076,50000,1,0,0,1,1,0,0,0), + (10,8,15077,50000,1,0,0,1,1,0,0,0), + (10,8,15078,50000,1,0,0,1,1,0,0,0), + (10,8,15079,50000,1,0,0,1,1,0,0,0), + (10,8,15567,20000,1,0,0,1,1,0,0,0), + (10,8,15568,20000,1,0,0,1,1,0,0,0), + (10,8,15569,20000,1,0,0,1,1,0,0,0), + (10,8,15570,20000,1,0,0,1,1,0,0,0), + (10,8,15571,20000,1,0,0,1,1,0,0,0), + (10,8,15572,20000,1,0,0,1,1,0,0,0), + (10,8,15573,20000,1,0,0,1,1,0,0,0), + (10,8,15574,20000,1,0,0,1,1,0,0,0), + (10,8,15575,20000,1,0,0,1,1,0,0,0), + (10,8,15576,20000,1,0,0,1,1,0,0,0), + (10,8,15577,20000,1,0,0,1,1,0,0,0), + (10,8,15578,20000,1,0,0,1,1,0,0,0), + (10,8,15579,20000,1,0,0,1,1,0,0,0), + (10,8,15580,20000,1,0,0,1,1,0,0,0), + (10,8,15581,20000,1,0,0,1,1,0,0,0), + (10,8,15582,20000,1,0,0,1,1,0,0,0), + (10,8,15583,20000,1,0,0,1,1,0,0,0), + (10,8,15584,20000,1,0,0,1,1,0,0,0), + (10,8,15585,20000,1,0,0,1,1,0,0,0), + (10,8,15586,20000,1,0,0,1,1,0,0,0), + (10,8,15587,20000,1,0,0,1,1,0,0,0), + (10,8,15588,20000,1,0,0,1,1,0,0,0), + (10,8,15589,20000,1,0,0,1,1,0,0,0), + (10,8,15590,20000,1,0,0,1,1,0,0,0), + (10,8,15591,20000,1,0,0,1,1,0,0,0), + (10,8,15592,20000,1,0,0,1,1,0,0,0), + (10,8,15593,20000,1,0,0,1,1,0,0,0), + (10,8,15594,20000,1,0,0,1,1,0,0,0), + (10,8,15595,20000,1,0,0,1,1,0,0,0), + (10,8,15596,20000,1,0,0,1,1,0,0,0), + (10,8,15597,20000,1,0,0,1,1,0,0,0), + (10,8,15598,20000,1,0,0,1,1,0,0,0), + (10,8,15599,20000,1,0,0,1,1,0,0,0), + (10,8,15600,20000,1,0,0,1,1,0,0,0), + (10,8,15601,20000,1,0,0,1,1,0,0,0), + (10,8,15602,20000,1,0,0,1,1,0,0,0), + (10,8,15603,20000,1,0,0,1,1,0,0,0), + (10,8,15604,20000,1,0,0,1,1,0,0,0), + (10,8,15605,20000,1,0,0,1,1,0,0,0), + (10,8,15606,20000,1,0,0,1,1,0,0,0), + (10,8,15607,20000,1,0,0,1,1,0,0,0), + (10,8,15608,20000,1,0,0,1,1,0,0,0), + (10,8,15609,20000,1,0,0,1,1,0,0,0), + (10,8,15610,20000,1,0,0,1,1,0,0,0), + (10,8,15611,20000,1,0,0,1,1,0,0,0), + (10,8,15612,20000,1,0,0,1,1,0,0,0), + (10,8,15613,20000,1,0,0,1,1,0,0,0), + (10,8,15614,20000,1,0,0,1,1,0,0,0), + (10,8,15615,20000,1,0,0,1,1,0,0,0), + (10,8,15616,20000,1,0,0,1,1,0,0,0), + (10,8,15617,20000,1,0,0,1,1,0,0,0), + (10,8,15618,20000,1,0,0,1,1,0,0,0), + (10,8,15619,20000,1,0,0,1,1,0,0,0), + (10,8,15620,20000,1,0,0,1,1,0,0,0), + (10,8,15621,20000,1,0,0,1,1,0,0,0), + (10,8,15622,20000,1,0,0,1,1,0,0,0), + (10,8,15623,20000,1,0,0,1,1,0,0,0), + (10,8,15624,20000,1,0,0,1,1,0,0,0), + (10,8,15625,20000,1,0,0,1,1,0,0,0), + (10,8,15626,20000,1,0,0,1,1,0,0,0), + (10,8,15627,20000,1,0,0,1,1,0,0,0), + (10,8,15628,20000,1,0,0,1,1,0,0,0), + (10,8,15629,20000,1,0,0,1,1,0,0,0), + (10,8,15630,20000,1,0,0,1,1,0,0,0), + (10,8,15631,20000,1,0,0,1,1,0,0,0), + (10,8,15632,20000,1,0,0,1,1,0,0,0), + (10,8,15633,20000,1,0,0,1,1,0,0,0), + (10,8,15634,20000,1,0,0,1,1,0,0,0), + (10,8,15635,20000,1,0,0,1,1,0,0,0), + (10,8,15636,20000,1,0,0,1,1,0,0,0), + (10,8,15637,20000,1,0,0,1,1,0,0,0), + (10,8,15638,20000,1,0,0,1,1,0,0,0), + (10,8,15639,20000,1,0,0,1,1,0,0,0), + (10,8,15640,20000,1,0,0,1,1,0,0,0), + (10,8,15641,20000,1,0,0,1,1,0,0,0), + (10,8,15642,20000,1,0,0,1,1,0,0,0), + (10,8,15643,20000,1,0,0,1,1,0,0,0), + (10,8,15644,20000,1,0,0,1,1,0,0,0), + (10,8,15645,20000,1,0,0,1,1,0,0,0), + (10,8,15646,20000,1,0,0,1,1,0,0,0), + (10,8,15647,20000,1,0,0,1,1,0,0,0), + (10,8,15648,20000,1,0,0,1,1,0,0,0), + (10,8,15649,20000,1,0,0,1,1,0,0,0), + (10,8,15650,20000,1,0,0,1,1,0,0,0), + (10,8,15651,20000,1,0,0,1,1,0,0,0), + (10,8,15652,20000,1,0,0,1,1,0,0,0), + (10,8,15653,20000,1,0,0,1,1,0,0,0), + (10,8,15654,20000,1,0,0,1,1,0,0,0), + (10,8,15655,20000,1,0,0,1,1,0,0,0), + (10,8,15656,20000,1,0,0,1,1,0,0,0), + (10,8,15657,20000,1,0,0,1,1,0,0,0), + (10,8,15658,20000,1,0,0,1,1,0,0,0), + (10,8,15659,20000,1,0,0,1,1,0,0,0), + (10,8,15660,20000,1,0,0,1,1,0,0,0), + (10,8,15661,20000,1,0,0,1,1,0,0,0), + (10,8,15662,20000,1,0,0,1,1,0,0,0), + (10,8,15663,20000,1,0,0,1,1,0,0,0), + (10,8,15664,20000,1,0,0,1,1,0,0,0), + (10,8,15665,20000,1,0,0,1,1,0,0,0), + (10,8,15666,20000,1,0,0,1,1,0,0,0), + (10,8,15667,20000,1,0,0,1,1,0,0,0), + (10,8,15668,20000,1,0,0,1,1,0,0,0), + (10,8,15669,20000,1,0,0,1,1,0,0,0), + (10,8,15670,20000,1,0,0,1,1,0,0,0), + (10,8,15671,20000,1,0,0,1,1,0,0,0), + (10,8,15672,20000,1,0,0,1,1,0,0,0), + (10,8,15673,20000,1,0,0,1,1,0,0,0), + (10,8,15674,20000,1,0,0,1,1,0,0,0), + (10,8,15675,20000,1,0,0,1,1,0,0,0), + (10,8,15676,20000,1,0,0,1,1,0,0,0), + (10,8,15677,20000,1,0,0,1,1,0,0,0), + (10,8,15678,20000,1,0,0,1,1,0,0,0), + (10,8,15679,20000,1,0,0,1,1,0,0,0), + (10,8,15680,20000,1,0,0,1,1,0,0,0), + (10,8,15681,20000,1,0,0,1,1,0,0,0), + (10,8,15682,20000,1,0,0,1,1,0,0,0), + (10,8,15683,20000,1,0,0,1,1,0,0,0), + (10,8,15684,20000,1,0,0,1,1,0,0,0), + (10,8,15685,20000,1,0,0,1,1,0,0,0), + (10,8,15686,20000,1,0,0,1,1,0,0,0), + (10,8,15687,20000,1,0,0,1,1,0,0,0), + (10,8,15688,20000,1,0,0,1,1,0,0,0), + (10,8,15689,20000,1,0,0,1,1,0,0,0), + (10,8,15690,20000,1,0,0,1,1,0,0,0), + (10,8,15691,20000,1,0,0,1,1,0,0,0), + (10,8,15692,20000,1,0,0,1,1,0,0,0), + (10,8,15693,20000,1,0,0,1,1,0,0,0), + (10,8,15694,20000,1,0,0,1,1,0,0,0), + (10,8,15695,20000,1,0,0,1,1,0,0,0), + (10,8,15696,20000,1,0,0,1,1,0,0,0), + (10,8,15697,20000,1,0,0,1,1,0,0,0), + (10,8,15698,20000,1,0,0,1,1,0,0,0), + (10,8,15699,20000,1,0,0,1,1,0,0,0), + (10,8,15700,20000,1,0,0,1,1,0,0,0), + (10,8,15701,20000,1,0,0,1,1,0,0,0), + (10,8,15702,20000,1,0,0,1,1,0,0,0), + (10,8,15703,20000,1,0,0,1,1,0,0,0), + (10,8,15704,20000,1,0,0,1,1,0,0,0), + (10,8,15705,20000,1,0,0,1,1,0,0,0), + (10,8,15706,20000,1,0,0,1,1,0,0,0), + (10,8,15707,20000,1,0,0,1,1,0,0,0), + (10,8,15708,20000,1,0,0,1,1,0,0,0), + (10,8,15709,20000,1,0,0,1,1,0,0,0), + (10,8,15710,20000,1,0,0,1,1,0,0,0), + (10,8,15711,20000,1,0,0,1,1,0,0,0), + (10,8,15712,20000,1,0,0,1,1,0,0,0), + (10,8,15713,20000,1,0,0,1,1,0,0,0), + (10,8,15714,20000,1,0,0,1,1,0,0,0), + (10,8,15715,20000,1,0,0,1,1,0,0,0), + (10,8,15716,20000,1,0,0,1,1,0,0,0), + (10,8,15717,20000,1,0,0,1,1,0,0,0), + (10,8,15718,20000,1,0,0,1,1,0,0,0), + (10,8,15719,20000,1,0,0,1,1,0,0,0), + (10,8,15720,20000,1,0,0,1,1,0,0,0), + (10,8,15721,20000,1,0,0,1,1,0,0,0), + (10,8,15722,20000,1,0,0,1,1,0,0,0), + (10,8,15723,20000,1,0,0,1,1,0,0,0), + (10,8,15724,20000,1,0,0,1,1,0,0,0), + (10,8,15725,20000,1,0,0,1,1,0,0,0), + (10,8,15726,20000,1,0,0,1,1,0,0,0), + (10,8,15727,20000,1,0,0,1,1,0,0,0), + (10,8,15728,20000,1,0,0,1,1,0,0,0), + (10,8,15729,20000,1,0,0,1,1,0,0,0), + (10,8,15730,20000,1,0,0,1,1,0,0,0), + (10,8,15731,20000,1,0,0,1,1,0,0,0), + (10,8,15732,20000,1,0,0,1,1,0,0,0), + (10,8,15733,20000,1,0,0,1,1,0,0,0), + (10,8,15734,20000,1,0,0,1,1,0,0,0), + (10,8,15735,20000,1,0,0,1,1,0,0,0), + (10,8,15736,20000,1,0,0,1,1,0,0,0), + (10,8,15737,20000,1,0,0,1,1,0,0,0), + (10,8,15738,20000,1,0,0,1,1,0,0,0), + (10,8,15739,20000,1,0,0,1,1,0,0,0), + (10,8,15740,20000,1,0,0,1,1,0,0,0), + (10,8,15741,20000,1,0,0,1,1,0,0,0), + (10,8,15742,20000,1,0,0,1,1,0,0,0), + (10,8,15743,20000,1,0,0,1,1,0,0,0), + (10,8,15744,20000,1,0,0,1,1,0,0,0), + (10,8,15745,20000,1,0,0,1,1,0,0,0), + (10,8,15746,20000,1,0,0,1,1,0,0,0), + (10,8,15747,20000,1,0,0,1,1,0,0,0), + (10,8,15748,20000,1,0,0,1,1,0,0,0), + (10,8,15749,20000,1,0,0,1,1,0,0,0), + (10,8,15750,20000,1,0,0,1,1,0,0,0), + (10,8,15751,20000,1,0,0,1,1,0,0,0), + (10,8,15752,20000,1,0,0,1,1,0,0,0), + (10,8,15753,20000,1,0,0,1,1,0,0,0), + (10,8,15754,20000,1,0,0,1,1,0,0,0), + (10,8,15755,20000,1,0,0,1,1,0,0,0), + (10,8,15756,20000,1,0,0,1,1,0,0,0), + (10,8,15757,20000,1,0,0,1,1,0,0,0), + (10,8,15758,20000,1,0,0,1,1,0,0,0), + (10,8,15759,20000,1,0,0,1,1,0,0,0), + (10,8,15760,20000,1,0,0,1,1,0,0,0), + (10,8,15761,20000,1,0,0,1,1,0,0,0), + (10,8,15762,20000,1,0,0,1,1,0,0,0), + (10,8,15763,20000,1,0,0,1,1,0,0,0), + (10,8,15764,20000,1,0,0,1,1,0,0,0), + (10,8,15765,20000,1,0,0,1,1,0,0,0), + (10,8,15766,20000,1,0,0,1,1,0,0,0), + (10,8,15919,20000,1,0,0,1,1,0,0,0), + (10,8,15920,20000,1,0,0,1,1,0,0,0), + (10,8,15921,20000,1,0,0,1,1,0,0,0), + (10,8,15922,20000,1,0,0,1,1,0,0,0), + (10,8,15923,20000,1,0,0,1,1,0,0,0), + (10,8,15924,20000,1,0,0,1,1,0,0,0), + (10,8,15925,20000,1,0,0,1,1,0,0,0), + (10,8,15926,20000,1,0,0,1,1,0,0,0), + (10,8,15927,20000,1,0,0,1,1,0,0,0), + (10,8,15928,20000,1,0,0,1,1,0,0,0), + (10,8,15929,20000,1,0,0,1,1,0,0,0), + (10,8,15930,20000,1,0,0,1,1,0,0,0), + (10,8,15931,20000,1,0,0,1,1,0,0,0), + (10,8,15932,20000,1,0,0,1,1,0,0,0), + (10,8,15933,20000,1,0,0,1,1,0,0,0), + (10,8,15934,20000,1,0,0,1,1,0,0,0), + (10,8,15935,20000,1,0,0,1,1,0,0,0), + (10,8,15936,20000,1,0,0,1,1,0,0,0), + (10,8,15937,20000,1,0,0,1,1,0,0,0), + (10,8,15938,20000,1,0,0,1,1,0,0,0), + (10,8,15939,20000,1,0,0,1,1,0,0,0), + (10,8,15940,20000,1,0,0,1,1,0,0,0), + (10,8,15941,20000,1,0,0,1,1,0,0,0), + (10,8,15942,20000,1,0,0,1,1,0,0,0), + (10,8,15943,20000,1,0,0,1,1,0,0,0), + (10,8,15944,20000,1,0,0,1,1,0,0,0), + (10,8,15945,20000,1,0,0,1,1,0,0,0), + (10,8,15946,20000,1,0,0,1,1,0,0,0), + (10,8,15947,20000,1,0,0,1,1,0,0,0), + (10,8,15948,20000,1,0,0,1,1,0,0,0), + (10,8,15949,20000,1,0,0,1,1,0,0,0), + (10,8,15950,20000,1,0,0,1,1,0,0,0), + (10,8,15951,20000,1,0,0,1,1,0,0,0), + (10,8,15952,20000,1,0,0,1,1,0,0,0), + (10,8,15953,20000,1,0,0,1,1,0,0,0), + (10,8,15954,20000,1,0,0,1,1,0,0,0), + (10,8,15955,20000,1,0,0,1,1,0,0,0), + (10,8,15956,20000,1,0,0,1,1,0,0,0), + (10,8,15957,20000,1,0,0,1,1,0,0,0), + (10,8,15958,20000,1,0,0,1,1,0,0,0), + (10,8,15959,20000,1,0,0,1,1,0,0,0), + (10,8,15960,20000,1,0,0,1,1,0,0,0), + (10,8,15961,20000,1,0,0,1,1,0,0,0), + (10,8,15962,20000,1,0,0,1,1,0,0,0), + (10,8,15963,20000,1,0,0,1,1,0,0,0), + (10,8,15964,20000,1,0,0,1,1,0,0,0), + (10,8,15965,20000,1,0,0,1,1,0,0,0), + (10,8,15966,20000,1,0,0,1,1,0,0,0), + (10,8,15967,20000,1,0,0,1,1,0,0,0), + (10,8,15968,20000,1,0,0,1,1,0,0,0), + (10,7,13506,250,1,0,0,1,1,0,50,0), + (10,7,15011,250,1,0,0,1,1,0,50,0), + (10,7,13636,250,1,0,0,1,1,0,50,0), + (10,7,15022,250,1,0,0,1,1,0,50,0), + (10,8,4407,1000,1,0,0,1,1,0,0,0), + (10,8,4408,1000,1,0,0,1,1,0,0,0), + (10,8,4409,1000,1,0,0,1,1,0,0,0), + (10,8,4410,1000,1,0,0,1,1,0,0,0), + (10,8,4411,1000,1,0,0,1,1,0,0,0), + (10,8,4412,1000,1,0,0,1,1,0,0,0), + (10,8,4413,1000,1,0,0,1,1,0,0,0), + (10,8,4414,1000,1,0,0,1,1,0,0,0), + (10,8,4823,1000,1,0,0,1,1,0,0,0), + (10,8,4824,1000,1,0,0,1,1,0,0,0), + (10,8,4825,1000,1,0,0,1,1,0,0,0), + (10,8,4826,1000,1,0,0,1,1,0,0,0), + (10,8,4827,1000,1,0,0,1,1,0,0,0), + (10,8,4828,1000,1,0,0,1,1,0,0,0), + (10,8,4829,1000,1,0,0,1,1,0,0,0), + (10,8,4830,1000,1,0,0,1,1,0,0,0), + (10,8,5194,1000,1,0,0,1,1,0,0,0), + (10,8,5195,1000,1,0,0,1,1,0,0,0), + (10,8,5196,1000,1,0,0,1,1,0,0,0), + (10,8,5197,1000,1,0,0,1,1,0,0,0), + (10,8,5198,1000,1,0,0,1,1,0,0,0), + (10,8,5199,1000,1,0,0,1,1,0,0,0), + (10,8,5200,1000,1,0,0,1,1,0,0,0), + (10,8,5201,1000,1,0,0,1,1,0,0,0), + (10,8,13630,1000,1,0,0,1,1,0,0,0), + (10,8,13631,1000,1,0,0,1,1,0,0,0), + (10,8,13632,1000,1,0,0,1,1,0,0,0), + (10,8,13633,1000,1,0,0,1,1,0,0,0), + (10,8,13634,1000,1,0,0,1,1,0,0,0), + (10,8,13635,1000,1,0,0,1,1,0,0,0), + (10,8,15103,1000,1,0,0,1,1,0,0,0), + (10,8,15104,1000,1,0,0,1,1,0,0,0), + (10,8,15105,1000,1,0,0,1,1,0,0,0), + (10,8,15106,1000,1,0,0,1,1,0,0,0), + (10,8,15107,1000,1,0,0,1,1,0,0,0), + (10,8,15108,1000,1,0,0,1,1,0,0,0), + (10,8,16459,1000,1,0,0,1,1,0,0,0), + (10,8,16460,1000,1,0,0,1,1,0,0,0), + (10,8,16461,1000,1,0,0,1,1,0,0,0), + (10,8,16462,1000,1,0,0,1,1,0,0,0), + (10,8,16463,1000,1,0,0,1,1,0,0,0), + (10,8,16464,1000,1,0,0,1,1,0,0,0), + (10,8,16465,1000,1,0,0,1,1,0,0,0), + (10,8,16466,1000,1,0,0,1,1,0,0,0), + (10,8,16467,1000,1,0,0,1,1,0,0,0), + (10,8,16468,1000,1,0,0,1,1,0,0,0), + (10,8,16469,1000,1,0,0,1,1,0,0,0), + (10,8,16470,1000,1,0,0,1,1,0,0,0), + (10,8,16471,1000,1,0,0,1,1,0,0,0), + (10,8,16472,1000,1,0,0,1,1,0,0,0), + (10,8,13416,1000,1,0,0,1,1,0,0,0), + (10,8,13417,1000,1,0,0,1,1,0,0,0), + (10,8,13418,1000,1,0,0,1,1,0,0,0), + (10,8,13419,1000,1,0,0,1,1,0,0,0), + (10,8,13420,1000,1,0,0,1,1,0,0,0), + (10,8,14283,1000,1,0,0,1,1,0,0,0), + (10,8,14284,1000,1,0,0,1,1,0,0,0), + (10,8,14285,1000,1,0,0,1,1,0,0,0), + (10,8,14286,1000,1,0,0,1,1,0,0,0), + (10,8,13182,1000,1,0,0,1,1,0,0,0), + (10,8,13507,1000,1,0,0,1,1,0,0,0), + (10,8,13981,1000,1,0,0,1,1,0,0,0), + (10,8,14744,1000,1,0,0,1,1,0,0,0), + (10,8,14893,1000,1,0,0,1,1,0,0,0), + (10,8,15785,1000,1,0,0,1,1,0,0,0), + (10,8,16419,1000,1,0,0,1,1,0,0,0), + (10,8,11470,1000,1,0,0,1,1,0,0,0), + (10,8,12512,1000,1,0,0,1,1,0,0,0), + (10,8,12884,1000,1,0,0,1,1,0,0,0), + (10,8,12513,1000,1,0,0,1,1,0,0,0), + (10,8,12514,1000,1,0,0,1,1,0,0,0), + (10,8,12515,1000,1,0,0,1,1,0,0,0), + (10,8,12516,1000,1,0,0,1,1,0,0,0), + (10,8,12517,1000,1,0,0,1,1,0,0,0), + (10,8,12518,1000,1,0,0,1,1,0,0,0), + (10,8,12519,1000,1,0,0,1,1,0,0,0), + (10,8,12520,1000,1,0,0,1,1,0,0,0), + (10,8,12521,1000,1,0,0,1,1,0,0,0), + (10,8,8179,1000,1,0,0,1,1,0,0,0), + (10,8,9704,1000,1,0,0,1,1,0,0,0), + (10,8,15448,1000,1,0,0,1,1,0,0,0), + (10,8,11162,1000,1,0,0,1,1,0,0,0), + (10,8,11163,1000,1,0,0,1,1,0,0,0), + (10,8,11164,1000,1,0,0,1,1,0,0,0), + (10,8,11165,1000,1,0,0,1,1,0,0,0), + (10,8,11661,1000,1,0,0,1,1,0,0,0), + (10,8,11662,1000,1,0,0,1,1,0,0,0), + (10,8,14639,1000,1,0,0,1,1,0,0,0), + (10,7,15774,3000,1,0,0,1,1,0,100,0), + (10,7,15775,3000,1,0,0,1,1,0,100,0), + (10,7,11420,3000,1,0,0,1,1,0,100,0), + (10,7,14704,3000,1,0,0,1,1,0,100,0), + (10,7,13177,3000,1,0,0,1,1,0,100,0), + (10,7,14191,3000,1,0,0,1,1,0,100,0), + (10,7,13449,3000,1,0,0,1,1,0,100,0), + (10,7,14192,3000,1,0,0,1,1,0,100,0), + (10,7,15772,3000,1,0,0,1,1,0,100,0), + (10,7,13791,3000,1,0,0,1,1,0,100,0), + (10,7,14006,3000,1,0,0,1,1,0,100,0), + (10,7,15768,3000,1,0,0,1,1,0,100,0), + (10,7,14069,3000,1,0,0,1,1,0,100,0), + (10,7,14124,3000,1,0,0,1,1,0,100,0), + (10,7,15507,3000,1,0,0,1,1,0,100,0), + (10,7,15508,3000,1,0,0,1,1,0,100,0), + (10,7,14855,3000,1,0,0,1,1,0,100,0), + (10,7,14894,3000,1,0,0,1,1,0,100,0), + (10,7,16444,3000,1,0,0,1,1,0,100,0), + (10,7,16445,3000,1,0,0,1,1,0,100,0), + (10,7,12509,3000,1,0,0,1,1,0,100,0), + (10,7,14126,3000,1,0,0,1,1,0,100,0), + (10,7,15062,3000,1,0,0,1,1,0,100,0), + (10,7,15063,3000,1,0,0,1,1,0,100,0), + (10,7,14891,3000,1,0,0,1,1,0,100,0), + (10,7,14895,3000,1,0,0,1,1,0,100,0), + (10,7,14091,3000,1,0,0,1,1,0,100,0), + (10,7,14092,3000,1,0,0,1,1,0,100,0), + (10,7,14501,3000,1,0,0,1,1,0,100,0), + (10,7,14506,3000,1,0,0,1,1,0,100,0), + (10,7,15285,3000,1,0,0,1,1,0,100,0), + (10,7,15286,3000,1,0,0,1,1,0,100,0), + (10,7,16442,3000,1,0,0,1,1,0,100,0), + (10,7,16443,3000,1,0,0,1,1,0,100,0), + (10,7,15027,3000,1,0,0,1,1,0,100,0), + (10,7,15028,3000,1,0,0,1,1,0,100,0), + (10,7,13453,3000,1,0,0,1,1,0,100,0), + (10,7,14193,3000,1,0,0,1,1,0,100,0), + (10,7,13178,3000,1,0,0,1,1,0,100,0), + (10,7,14194,3000,1,0,0,1,1,0,100,0), + (10,7,16454,3000,1,0,0,1,1,0,100,0), + (10,7,16455,3000,1,0,0,1,1,0,100,0), + (10,7,15030,3000,1,0,0,1,1,0,100,0), + (10,7,15031,3000,1,0,0,1,1,0,100,0), + (10,7,13790,3000,1,0,0,1,1,0,100,0), + (10,7,14005,3000,1,0,0,1,1,0,100,0), + (10,7,14406,3000,1,0,0,1,1,0,100,0), + (10,7,14413,3000,1,0,0,1,1,0,100,0), + (10,7,16448,3000,1,0,0,1,1,0,100,0), + (10,7,16449,3000,1,0,0,1,1,0,100,0), + (10,7,12872,3000,1,0,0,1,1,0,100,0), + (10,7,14187,3000,1,0,0,1,1,0,100,0), + (10,7,14125,3000,1,0,0,1,1,0,100,0), + (10,7,14500,3000,1,0,0,1,1,0,100,0), + (10,7,14505,3000,1,0,0,1,1,0,100,0), + (10,7,15118,3000,1,0,0,1,1,0,100,0), + (10,7,15119,3000,1,0,0,1,1,0,100,0), + (10,7,14662,3000,1,0,0,1,1,0,100,0), + (10,7,14663,3000,1,0,0,1,1,0,100,0), + (10,7,15771,3000,1,0,0,1,1,0,100,0), + (10,7,9700,3000,1,0,0,1,1,0,100,0), + (10,7,14498,3000,1,0,0,1,1,0,100,0), + (10,7,14913,3000,1,0,0,1,1,0,100,0), + (10,7,14914,3000,1,0,0,1,1,0,100,0), + (10,7,13508,3000,1,0,0,1,1,0,100,0), + (10,7,15115,3000,1,0,0,1,1,0,100,0), + (10,7,15116,3000,1,0,0,1,1,0,100,0), + (10,7,15113,3000,1,0,0,1,1,0,100,0), + (10,7,15114,3000,1,0,0,1,1,0,100,0), + (10,7,15222,3000,1,0,0,1,1,0,100,0), + (10,7,15223,3000,1,0,0,1,1,0,100,0), + (10,7,10750,3000,1,0,0,1,1,0,100,0), + (10,7,14705,3000,1,0,0,1,1,0,100,0), + (10,7,15027,3000,1,0,0,1,1,0,100,0), + (10,7,15028,3000,1,0,0,1,1,0,100,0), + (10,7,10380,3000,1,0,0,1,1,0,100,0), + (10,7,15060,3000,1,0,0,1,1,0,100,0), + (10,7,13963,3000,1,0,0,1,1,0,100,0), + (10,7,14026,3000,1,0,0,1,1,0,100,0), + (10,7,13964,3000,1,0,0,1,1,0,100,0), + (10,7,14027,3000,1,0,0,1,1,0,100,0), + (10,7,15064,3000,1,0,0,1,1,0,100,0), + (10,7,15065,3000,1,0,0,1,1,0,100,0), + (10,7,15524,3000,1,0,0,1,1,0,100,0), + (10,7,15525,3000,1,0,0,1,1,0,100,0), + (10,7,16450,3000,1,0,0,1,1,0,100,0), + (10,7,16451,3000,1,0,0,1,1,0,100,0), + (10,7,16344,3000,1,0,0,1,1,0,100,0), + (10,7,16345,3000,1,0,0,1,1,0,100,0), + (10,7,16342,3000,1,0,0,1,1,0,100,0), + (10,7,16343,3000,1,0,0,1,1,0,100,0), + (10,7,15220,3000,1,0,0,1,1,0,100,0), + (10,7,15221,3000,1,0,0,1,1,0,100,0), + (10,7,15066,3000,1,0,0,1,1,0,100,0), + (10,7,15067,3000,1,0,0,1,1,0,100,0), + (10,7,14089,3000,1,0,0,1,1,0,100,0), + (10,7,14090,3000,1,0,0,1,1,0,100,0), + (10,7,14195,3000,1,0,0,1,1,0,100,0), + (10,7,14196,3000,1,0,0,1,1,0,100,0), + (10,7,13965,3000,1,0,0,1,1,0,100,0), + (10,7,14028,3000,1,0,0,1,1,0,100,0), + (10,7,13508,3000,1,0,0,1,1,0,100,0), + (10,7,13962,3000,1,0,0,1,1,0,100,0), + (10,7,14314,3000,1,0,0,1,1,0,100,0), + (10,7,13404,3000,1,0,0,1,1,0,100,0), + (10,7,14188,3000,1,0,0,1,1,0,100,0), + (10,7,14032,3000,1,0,0,1,1,0,100,0), + (10,7,13960,3000,1,0,0,1,1,0,100,0), + (10,7,15819,3000,1,0,0,1,1,0,100,0), + (10,7,15820,3000,1,0,0,1,1,0,100,0), + (10,7,10750,3000,1,0,0,1,1,0,100,0), + (10,7,14705,3000,1,0,0,1,1,0,100,0), + (10,7,14407,3000,1,0,0,1,1,0,100,0), + (10,7,14414,3000,1,0,0,1,1,0,100,0), + (10,7,16352,3000,1,0,0,1,1,0,100,0), + (10,7,16353,3000,1,0,0,1,1,0,100,0), + (10,7,14502,3000,1,0,0,1,1,0,100,0), + (10,7,14507,3000,1,0,0,1,1,0,100,0), + (10,7,10811,3000,1,0,0,1,1,0,100,0), + (10,7,15061,3000,1,0,0,1,1,0,100,0), + (10,7,15823,3000,1,0,0,1,1,0,100,0), + (10,7,15824,3000,1,0,0,1,1,0,100,0), + (10,7,15224,3000,1,0,0,1,1,0,100,0), + (10,7,15225,3000,1,0,0,1,1,0,100,0), + (10,7,14503,3000,1,0,0,1,1,0,100,0), + (10,7,14510,3000,1,0,0,1,1,0,100,0), + (10,7,15776,3000,1,0,0,1,1,0,100,0), + (10,7,15777,3000,1,0,0,1,1,0,100,0), + (10,7,15821,3000,1,0,0,1,1,0,100,0), + (10,7,15822,3000,1,0,0,1,1,0,100,0), + (10,7,14198,3000,1,0,0,1,1,0,100,0), + (10,7,14197,3000,1,0,0,1,1,0,100,0), + (10,7,16446,3000,1,0,0,1,1,0,100,0), + (10,7,16447,3000,1,0,0,1,1,0,100,0), + (10,7,14905,3000,1,0,0,1,1,0,100,0), + (10,7,14907,3000,1,0,0,1,1,0,100,0), + (10,7,14904,3000,1,0,0,1,1,0,100,0), + (10,7,14906,3000,1,0,0,1,1,0,100,0), + (10,7,14659,3000,1,0,0,1,1,0,100,0), + (10,7,14660,3000,1,0,0,1,1,0,100,0), + (10,7,13326,3000,1,0,0,1,1,0,100,0), + (10,7,14416,3000,1,0,0,1,1,0,100,0), + (10,7,13450,3000,1,0,0,1,1,0,100,0), + (10,7,14031,3000,1,0,0,1,1,0,100,0), + (10,7,16492,3000,1,0,0,1,1,0,100,0), + (10,7,16493,3000,1,0,0,1,1,0,100,0), + (10,7,14299,500,1,0,0,1,1,0,20,0), + (10,7,14389,500,1,0,0,1,1,0,20,0), + (10,7,15177,500,1,0,0,1,1,0,20,0), + (10,7,14537,500,1,0,0,1,1,0,20,0), + (10,7,14758,500,1,0,0,1,1,0,20,0), + (10,7,14854,500,1,0,0,1,1,0,20,0), + (10,7,13974,500,1,0,0,1,1,0,20,0), + (10,7,15021,500,1,0,0,1,1,0,20,0), + (10,7,15111,500,1,0,0,1,1,0,20,0), + (10,7,15226,500,1,0,0,1,1,0,20,0), + (10,7,15773,500,1,0,0,1,1,0,20,0), + (10,7,15825,500,1,0,0,1,1,0,20,0), + (10,7,15827,500,1,0,0,1,1,0,20,0), + (10,7,16340,500,1,0,0,1,1,0,20,0), + (10,7,16341,500,1,0,0,1,1,0,20,0), + (10,7,16457,500,1,0,0,1,1,0,20,0), + (10,7,16458,500,1,0,0,1,1,0,20,0), + (10,7,11698,250,1,0,0,1,1,0,50,0), + (10,7,11700,250,1,0,0,1,1,0,50,0), + (10,8,9958,20,1,0,0,1,1,0,0,999), + (10,8,11284,15,1,0,0,1,1,0,0,0), + (10,8,11285,15,1,0,0,1,1,0,0,0), + (10,8,11286,15,1,0,0,1,1,0,0,0), + (10,8,10356,500,1,0,0,1,1,0,0,0), + (10,8,12511,500,1,0,0,1,1,0,0,0), + (10,8,13238,500,1,0,0,1,1,0,0,0), + (10,8,11383,10,1,0,0,1,1,0,0,0), + (10,8,11382,10,1,0,0,1,1,0,0,0), + (10,8,11381,10,1,0,0,1,1,0,0,0), + (10,7,16348,3000,1,0,0,1,1,0,100,0), + (10,8,11386,10,1,0,0,1,1,0,0,0), + (10,8,14444,10,1,0,0,1,1,0,0,0), + (10,8,14443,10,1,0,0,1,1,0,0,0), + (10,8,14445,10,1,0,0,1,1,0,0,0), + (10,8,15068,500,1,0,0,1,1,0,20,0), + (10,7,16532,1000,1,0,0,1,1,0,0,0), + (10,7,14368,3000,1,0,0,1,1,0,50,0), + (10,7,1622,3000,1,0,0,1,1,0,0,0), + (10,7,16456,500,1,0,0,1,1,0,0,0); END; \ No newline at end of file diff --git a/config.json b/config.json index 4bc051030..9b578fe6e 100644 --- a/config.json +++ b/config.json @@ -73,6 +73,7 @@ {"Name": "HunterSupport", "Enabled": false}, {"Name": "NBoost", "Enabled": false}, {"Name": "NetCafe", "Enabled": true}, + {"Name": "OfficialCafe", "Enabled": true}, {"Name": "HLRenewing", "Enabled": true}, {"Name": "EXRenewing", "Enabled": true} ], diff --git a/go.mod b/go.mod index 1d7f83e7b..21ac00f07 100644 --- a/go.mod +++ b/go.mod @@ -8,18 +8,16 @@ require ( github.com/gorilla/mux v1.8.0 github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 - 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 + golang.org/x/text v0.7.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 @@ -32,7 +30,7 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect - golang.org/x/sys v0.1.0 // indirect + golang.org/x/sys v0.5.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 ca2bd44a9..863dc42a3 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -229,8 +227,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 h1:BanNeULiV7hOXjHPUQt3tgF6qVHGZ0uLMnCr0WZ5CTk= -github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96/go.mod h1:NuxNqEW5jNyYkZ5WSBB70WQXtRKY1jUPMzX74wr5JFo= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -430,8 +426,8 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210510120138-977fb7262007/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/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.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= @@ -440,8 +436,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.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.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= @@ -496,7 +492,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 02f699b30..d4815cd96 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "net" "os" "os/signal" + "runtime/debug" "syscall" "time" @@ -29,13 +30,24 @@ func cleanDB(db *sqlx.DB) { _ = db.MustExec("DELETE FROM users") } +var Commit = func() string { + if info, ok := debug.ReadBuildInfo(); ok { + for _, setting := range info.Settings { + if setting.Key == "vcs.revision" { + return setting.Value[:7] + } + } + } + return "unknown" +} + func main() { var err error zapLogger, _ := zap.NewDevelopment() defer zapLogger.Sync() logger := zapLogger.Named("main") - logger.Info("Starting Erupe (9.2b)") + logger.Info(fmt.Sprintf("Starting Erupe (9.2b-%s)", Commit())) if config.ErupeConfig.Database.Password == "" { preventClose("Database password is blank") @@ -113,6 +125,8 @@ func main() { logger.Info("Done cleaning DB") } + logger.Info(fmt.Sprintf("Server Time: %s", channelserver.TimeAdjusted().String())) + // Now start our server(s). // Entrance server. @@ -211,12 +225,14 @@ func main() { } } + logger.Info("Finished starting Erupe") + // Wait for exit or interrupt with ctrl+C. c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c - logger.Info("Trying to shutdown gracefully") + logger.Info("Shutting down...") if config.ErupeConfig.Channel.Enabled { for _, c := range channels { diff --git a/network/mhfpacket/msg_mhf_get_box_gacha_info.go b/network/mhfpacket/msg_mhf_get_box_gacha_info.go index 54e7eb111..b944e6c11 100644 --- a/network/mhfpacket/msg_mhf_get_box_gacha_info.go +++ b/network/mhfpacket/msg_mhf_get_box_gacha_info.go @@ -1,17 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetBoxGachaInfo represents the MSG_MHF_GET_BOX_GACHA_INFO -type MsgMhfGetBoxGachaInfo struct{ +type MsgMhfGetBoxGachaInfo struct { AckHandle uint32 - GachaHash uint32 + GachaID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,7 +22,7 @@ func (m *MsgMhfGetBoxGachaInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetBoxGachaInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() return nil } diff --git a/network/mhfpacket/msg_mhf_get_stepup_status.go b/network/mhfpacket/msg_mhf_get_stepup_status.go index 1f04c7740..dcced8f4f 100644 --- a/network/mhfpacket/msg_mhf_get_stepup_status.go +++ b/network/mhfpacket/msg_mhf_get_stepup_status.go @@ -1,18 +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" ) // MsgMhfGetStepupStatus represents the MSG_MHF_GET_STEPUP_STATUS -type MsgMhfGetStepupStatus struct{ +type MsgMhfGetStepupStatus struct { AckHandle uint32 - GachaHash uint32 - Unk uint8 + GachaID uint32 + Unk uint8 } // Opcode returns the ID associated with this packet type. @@ -23,7 +23,7 @@ func (m *MsgMhfGetStepupStatus) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetStepupStatus) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() m.Unk = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_get_weekly_seibatu_ranking_reward.go b/network/mhfpacket/msg_mhf_get_weekly_seibatu_ranking_reward.go index 3b8b07c01..2823ff525 100644 --- a/network/mhfpacket/msg_mhf_get_weekly_seibatu_ranking_reward.go +++ b/network/mhfpacket/msg_mhf_get_weekly_seibatu_ranking_reward.go @@ -1,15 +1,21 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfGetWeeklySeibatuRankingReward represents the MSG_MHF_GET_WEEKLY_SEIBATU_RANKING_REWARD -type MsgMhfGetWeeklySeibatuRankingReward struct{} +type MsgMhfGetWeeklySeibatuRankingReward struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 + Unk2 uint32 + Unk3 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetWeeklySeibatuRankingReward) Opcode() network.PacketID { @@ -18,7 +24,12 @@ func (m *MsgMhfGetWeeklySeibatuRankingReward) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetWeeklySeibatuRankingReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_play_box_gacha.go b/network/mhfpacket/msg_mhf_play_box_gacha.go index c47100e6f..f09c018f8 100644 --- a/network/mhfpacket/msg_mhf_play_box_gacha.go +++ b/network/mhfpacket/msg_mhf_play_box_gacha.go @@ -1,19 +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" ) // MsgMhfPlayBoxGacha represents the MSG_MHF_PLAY_BOX_GACHA -type MsgMhfPlayBoxGacha struct{ +type MsgMhfPlayBoxGacha struct { AckHandle uint32 - GachaHash uint32 - RollType uint8 - CurrencyMode uint8 + GachaID uint32 + RollType uint8 + GachaType uint8 } // Opcode returns the ID associated with this packet type. @@ -24,9 +24,9 @@ func (m *MsgMhfPlayBoxGacha) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPlayBoxGacha) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() m.RollType = bf.ReadUint8() - m.CurrencyMode = bf.ReadUint8() + m.GachaType = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_play_normal_gacha.go b/network/mhfpacket/msg_mhf_play_normal_gacha.go index bc75f3ca1..46f2706a6 100644 --- a/network/mhfpacket/msg_mhf_play_normal_gacha.go +++ b/network/mhfpacket/msg_mhf_play_normal_gacha.go @@ -1,19 +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" ) // MsgMhfPlayNormalGacha represents the MSG_MHF_PLAY_NORMAL_GACHA -type MsgMhfPlayNormalGacha struct{ +type MsgMhfPlayNormalGacha struct { AckHandle uint32 - GachaHash uint32 - RollType uint8 - CurrencyMode uint8 + GachaID uint32 + RollType uint8 + GachaType uint8 } // Opcode returns the ID associated with this packet type. @@ -24,9 +24,9 @@ func (m *MsgMhfPlayNormalGacha) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPlayNormalGacha) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() m.RollType = bf.ReadUint8() - m.CurrencyMode = bf.ReadUint8() + m.GachaType = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_play_stepup_gacha.go b/network/mhfpacket/msg_mhf_play_stepup_gacha.go index 653fc799f..83808ae1a 100644 --- a/network/mhfpacket/msg_mhf_play_stepup_gacha.go +++ b/network/mhfpacket/msg_mhf_play_stepup_gacha.go @@ -1,19 +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" ) // MsgMhfPlayStepupGacha represents the MSG_MHF_PLAY_STEPUP_GACHA -type MsgMhfPlayStepupGacha struct{ +type MsgMhfPlayStepupGacha struct { AckHandle uint32 - GachaHash uint32 - RollType uint8 - CurrencyMode uint8 + GachaID uint32 + RollType uint8 + GachaType uint8 } // Opcode returns the ID associated with this packet type. @@ -24,9 +24,9 @@ func (m *MsgMhfPlayStepupGacha) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPlayStepupGacha) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() m.RollType = bf.ReadUint8() - m.CurrencyMode = bf.ReadUint8() + m.GachaType = bf.ReadUint8() return nil } diff --git a/network/mhfpacket/msg_mhf_post_tiny_bin.go b/network/mhfpacket/msg_mhf_post_tiny_bin.go index a43ed7b85..4c06a51e0 100644 --- a/network/mhfpacket/msg_mhf_post_tiny_bin.go +++ b/network/mhfpacket/msg_mhf_post_tiny_bin.go @@ -1,15 +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" ) // MsgMhfPostTinyBin represents the MSG_MHF_POST_TINY_BIN -type MsgMhfPostTinyBin struct{} +type MsgMhfPostTinyBin struct { + AckHandle uint32 + Unk []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostTinyBin) Opcode() network.PacketID { @@ -18,7 +21,9 @@ func (m *MsgMhfPostTinyBin) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk = bf.ReadBytes(14) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_present_box.go b/network/mhfpacket/msg_mhf_present_box.go index 77bdd0db2..c3da92e31 100644 --- a/network/mhfpacket/msg_mhf_present_box.go +++ b/network/mhfpacket/msg_mhf_present_box.go @@ -1,15 +1,26 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfPresentBox represents the MSG_MHF_PRESENT_BOX -type MsgMhfPresentBox struct{} +type MsgMhfPresentBox struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 + Unk2 uint32 + Unk3 uint32 + Unk4 uint32 + Unk5 uint32 + Unk6 uint32 + Unk7 uint32 + Unk8 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPresentBox) Opcode() network.PacketID { @@ -18,7 +29,17 @@ func (m *MsgMhfPresentBox) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPresentBox) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint32() + m.Unk4 = bf.ReadUint32() + m.Unk5 = bf.ReadUint32() + m.Unk6 = bf.ReadUint32() + m.Unk7 = bf.ReadUint32() + m.Unk8 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_receive_gacha_item.go b/network/mhfpacket/msg_mhf_receive_gacha_item.go index 841c5deab..3f83d1d1b 100644 --- a/network/mhfpacket/msg_mhf_receive_gacha_item.go +++ b/network/mhfpacket/msg_mhf_receive_gacha_item.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" ) // MsgMhfReceiveGachaItem represents the MSG_MHF_RECEIVE_GACHA_ITEM -type MsgMhfReceiveGachaItem struct{ - AckHandle uint32 - Unk0 uint16 +type MsgMhfReceiveGachaItem struct { + AckHandle uint32 + Max uint8 + Freeze bool } // Opcode returns the ID associated with this packet type. @@ -22,7 +23,8 @@ func (m *MsgMhfReceiveGachaItem) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfReceiveGachaItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() + m.Max = bf.ReadUint8() + m.Freeze = bf.ReadBool() return nil } diff --git a/network/mhfpacket/msg_mhf_reset_box_gacha_info.go b/network/mhfpacket/msg_mhf_reset_box_gacha_info.go index 233bc49d9..7a8c3ffcf 100644 --- a/network/mhfpacket/msg_mhf_reset_box_gacha_info.go +++ b/network/mhfpacket/msg_mhf_reset_box_gacha_info.go @@ -1,17 +1,17 @@ package mhfpacket -import ( - "errors" +import ( + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfResetBoxGachaInfo represents the MSG_MHF_RESET_BOX_GACHA_INFO -type MsgMhfResetBoxGachaInfo struct{ +type MsgMhfResetBoxGachaInfo struct { AckHandle uint32 - GachaHash uint32 + GachaID uint32 } // Opcode returns the ID associated with this packet type. @@ -22,7 +22,7 @@ func (m *MsgMhfResetBoxGachaInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfResetBoxGachaInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.GachaHash = bf.ReadUint32() + m.GachaID = bf.ReadUint32() return nil } diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index 99f0da4ae..d6260c934 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -89,8 +89,10 @@ func Courses() []Course { {Aliases: []string{"Hiden", "Secret"}, ID: 10}, // Secret {Aliases: []string{"HunterSupport", "HunterAid", "Support", "Aid", "Royal"}, ID: 11}, // Royal {Aliases: []string{"NBoost", "NetCafeBoost", "Boost"}, ID: 12}, - // 13-25 do nothing - {Aliases: []string{"NetCafe", "Cafe", "InternetCafe"}, ID: 26}, + // 13-19 = (unknown), 20 = DEBUG, 21 = COG_LINK_EXPIRED, 22 = 360_GOLD, 23 = PS3_TROP + {Aliases: []string{"COG"}, ID: 24}, + {Aliases: []string{"NetCafe", "Cafe", "InternetCafe"}, ID: 25}, + {Aliases: []string{"OfficialCafe", "Official"}, ID: 26}, {Aliases: []string{"HLRenewing", "HLR", "HLRenewal", "HLRenew"}, ID: 27}, {Aliases: []string{"EXRenewing", "EXR", "EXRenewal", "EXRenew"}, ID: 28}, {Aliases: []string{"Free"}, ID: 29}, diff --git a/patch-schema/gacha-db-2.sql b/patch-schema/gacha-db-2.sql new file mode 100644 index 000000000..d3faa1ed9 --- /dev/null +++ b/patch-schema/gacha-db-2.sql @@ -0,0 +1,77 @@ +BEGIN; + +ALTER TABLE characters + DROP COLUMN IF EXISTS gacha_prem; + +ALTER TABLE characters + DROP COLUMN IF EXISTS gacha_trial; + +ALTER TABLE characters + DROP COLUMN IF EXISTS frontier_points; + +ALTER TABLE users + ADD IF NOT EXISTS gacha_premium INT; + +ALTER TABLE users + ADD IF NOT EXISTS gacha_trial INT; + +ALTER TABLE users + ADD IF NOT EXISTS frontier_points INT; + +DROP TABLE IF EXISTS public.gacha_shop; + +CREATE TABLE IF NOT EXISTS public.gacha_shop ( + id SERIAL PRIMARY KEY, + min_gr INTEGER, + min_hr INTEGER, + name TEXT, + url_banner TEXT, + url_feature TEXT, + url_thumbnail TEXT, + wide BOOLEAN, + recommended BOOLEAN, + gacha_type INTEGER, + hidden BOOLEAN +); + +DROP TABLE IF EXISTS public.gacha_shop_items; + +CREATE TABLE IF NOT EXISTS public.gacha_entries ( + id SERIAL PRIMARY KEY, + gacha_id INTEGER, + entry_type INTEGER, + item_type INTEGER, + item_number INTEGER, + item_quantity INTEGER, + weight INTEGER, + rarity INTEGER, + rolls INTEGER, + frontier_points INTEGER, + daily_limit INTEGER +); + +CREATE TABLE IF NOT EXISTS public.gacha_items ( + id SERIAL PRIMARY KEY, + entry_id INTEGER, + item_type INTEGER, + item_id INTEGER, + quantity INTEGER +); + +DROP TABLE IF EXISTS public.stepup_state; + +CREATE TABLE IF NOT EXISTS public.gacha_stepup ( + gacha_id INTEGER, + step INTEGER, + character_id INTEGER +); + +DROP TABLE IF EXISTS public.lucky_box_state; + +CREATE TABLE IF NOT EXISTS public.gacha_box ( + gacha_id INTEGER, + entry_id INTEGER, + character_id INTEGER +); + +END; \ No newline at end of file diff --git a/patch-schema/login-boost.sql b/patch-schema/login-boost.sql new file mode 100644 index 000000000..3b451a65f --- /dev/null +++ b/patch-schema/login-boost.sql @@ -0,0 +1,12 @@ +BEGIN; + +DROP TABLE IF EXISTS public.login_boost_state; + +CREATE TABLE IF NOT EXISTS public.login_boost ( + char_id INTEGER, + week_req INTEGER, + expiration TIMESTAMP WITH TIME ZONE, + reset TIMESTAMP WITH TIME ZONE +); + +END; \ No newline at end of file diff --git a/patch-schema/mezfes-save.sql b/patch-schema/mezfes-save.sql new file mode 100644 index 000000000..a4445dc15 --- /dev/null +++ b/patch-schema/mezfes-save.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE public.characters + ADD COLUMN mezfes BYTEA; + +END; \ No newline at end of file diff --git a/patch-schema/time-fix.sql b/patch-schema/time-fix.sql new file mode 100644 index 000000000..8c5d890c0 --- /dev/null +++ b/patch-schema/time-fix.sql @@ -0,0 +1,69 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.characters + ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters + ALTER COLUMN guild_post_checked TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters + ALTER COLUMN boost_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters + ALTER COLUMN cafe_reset TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.distribution + ALTER COLUMN deadline TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.events + ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.feature_weapon + ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_alliances + ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_applications + ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_characters + ALTER COLUMN joined_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_posts + ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.characters + ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guilds + ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.mail + ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.stamps + ALTER COLUMN hl_next TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.stamps + ALTER COLUMN ex_next TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.titles + ALTER COLUMN unlocked_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.titles + ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.users + ALTER COLUMN last_login TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.users + ALTER COLUMN return_expires TYPE TIMESTAMP WITH TIME ZONE; + +ALTER TABLE IF EXISTS public.guild_meals + DROP COLUMN expires; + +ALTER TABLE IF EXISTS public.guild_meals + ADD COLUMN created_at TIMESTAMP WITH TIME ZONE; + +END; \ No newline at end of file diff --git a/patch-schema/unused-tables.sql b/patch-schema/unused-tables.sql new file mode 100644 index 000000000..a3411d517 --- /dev/null +++ b/patch-schema/unused-tables.sql @@ -0,0 +1,17 @@ +BEGIN; + +DROP TABLE IF EXISTS public.account_ban; + +DROP TABLE IF EXISTS public.account_history; + +DROP TABLE IF EXISTS public.account_moderation; + +DROP TABLE IF EXISTS public.account_sub; + +DROP TABLE IF EXISTS public.history; + +DROP TABLE IF EXISTS public.questlists; + +DROP TABLE IF EXISTS public.schema_migrations; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 3dd1fa76c..433c29e44 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -79,7 +79,7 @@ func updateRights(s *Session) { rights := []mhfpacket.ClientRight{{1, 0, 0}} var netcafeBitSet bool for _, course := range s.courses { - if (course.ID == 9 || course.ID == 26) && !netcafeBitSet { + if (course.ID == 9 || course.ID == 25 || course.ID == 26) && !netcafeBitSet { netcafeBitSet = true rightsInt += 0x40000000 // set netcafe bit rights = append(rights, mhfpacket.ClientRight{ID: 30}) @@ -144,7 +144,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { updateRights(s) bf := byteframe.NewByteFrame() - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp + bf.WriteUint32(uint32(TimeAdjusted().Unix())) // Unix timestamp _, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID) if err != nil { @@ -156,7 +156,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { panic(err) } - _, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID) + _, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", TimeAdjusted().Unix(), s.charID) if err != nil { panic(err) } @@ -216,7 +216,7 @@ func logoutPlayer(s *Session) { var timePlayed int var sessionTime int _ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed) - sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart) + sessionTime = int(TimeAdjusted().Unix()) - int(s.sessionStart) timePlayed += sessionTime var rpGained int @@ -276,7 +276,7 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) { resp := &mhfpacket.MsgSysTime{ GetRemoteTime: false, - Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone + Timestamp: uint32(TimeAdjusted().Unix()), // JP timezone } s.QueueSendMHF(resp) } @@ -528,8 +528,6 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAnnounce(s *Session, p mhfpacket.MHFPacket) { @@ -1507,7 +1505,7 @@ func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) { var dailyTime time.Time _ = s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime) - if Time_Current_Adjusted().After(dailyTime) { + if TimeAdjusted().After(dailyTime) { s.server.db.Exec("UPDATE characters SET bonus_quests = 0, daily_quests = 0 WHERE id=$1", s.charID) } @@ -1706,14 +1704,6 @@ func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetTinyBin(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetTinyBin) - // requested after conquest quests - doAckBufSucceed(s, pkt.AckHandle, []byte{}) -} - -func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {} - func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) { @@ -1723,45 +1713,6 @@ func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetRyoudama) - // likely guild related - // REQ: 00 04 13 53 8F 18 00 - // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E - // REQ: 00 06 13 53 8F 18 00 - // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 - // REQ: 00 05 13 53 8F 18 00 - // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00 - data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000") - doAckBufSucceed(s, pkt.AckHandle, data) -} - -func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) { - // if the game gets bad responses for this it breaks the ability to save - pkt := p.(*mhfpacket.MsgMhfGetTenrouirai) - var data []byte - var err error - if pkt.Unk0 == 1 { - data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010") - } else if pkt.Unk2 == 4 { - data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010") - } else { - data = []byte{0x00, 0x00, 0x00, 0x00} - s.logger.Info("GET_TENROUIRAI request for unknown type") - } - if err != nil { - panic(err) - } - doAckBufSucceed(s, pkt.AckHandle, data) -} - -func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfPostTenrouirai) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) -} - func handleMsgMhfGetDailyMissionMaster(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetDailyMissionPersonal(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index b623efae6..a94032332 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -37,11 +37,8 @@ func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint) - // get next midday - var t = Time_static() - year, month, day := t.Date() - midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) - if t.After(midday) { + midday := TimeMidnight().Add(12 * time.Hour) + if TimeAdjusted().After(midday) { midday = midday.Add(24 * time.Hour) } @@ -54,7 +51,7 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) { var bondBonus, bonusQuests, dailyQuests uint32 bf := byteframe.NewByteFrame() - if t.After(dailyTime) { + if midday.After(dailyTime) { addPointNetcafe(s, 5) bondBonus = 5 // Bond point bonus quests bonusQuests = 3 // HRP bonus quests? @@ -80,7 +77,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { cafeReset = TimeWeekNext() s.server.db.Exec(`UPDATE characters SET cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID) } - if Time_Current_Adjusted().After(cafeReset) { + if TimeAdjusted().After(cafeReset) { cafeReset = TimeWeekNext() s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID) s.server.db.Exec(`DELETE FROM cafe_accepted WHERE character_id=$1`, s.charID) @@ -92,7 +89,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { panic(err) } if s.FindCourse("NetCafe").ID != 0 || s.FindCourse("N").ID != 0 { - cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime + cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime } bf.WriteUint32(cafeTime) // Total cafe time bf.WriteUint16(0) @@ -142,7 +139,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) { } resp := byteframe.NewByteFrame() resp.WriteUint32(0) - resp.WriteUint32(uint32(time.Now().Unix())) + resp.WriteUint32(uint32(TimeAdjusted().Unix())) resp.WriteUint32(count) resp.WriteBytes(bf.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) @@ -165,7 +162,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { SELECT ch.cafe_time + $2 FROM characters ch WHERE ch.id = $1 - ) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart) + ) >= time_req`, s.charID, TimeAdjusted().Unix()-s.sessionStart) if err != nil { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { @@ -222,7 +219,7 @@ func addPointNetcafe(s *Session, p int) error { func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfStartBoostTime) bf := byteframe.NewByteFrame() - boostLimit := Time_Current_Adjusted().Add(100 * time.Minute) + boostLimit := TimeAdjusted().Add(120 * time.Minute) s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID) bf.WriteUint32(uint32(boostLimit.Unix())) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) @@ -255,7 +252,7 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) return } - if boostLimit.Unix() > Time_Current_Adjusted().Unix() { + if boostLimit.After(TimeAdjusted()) { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) } else { doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02}) diff --git a/server/channelserver/handlers_caravan.go b/server/channelserver/handlers_caravan.go index f000d1623..86cf73249 100644 --- a/server/channelserver/handlers_caravan.go +++ b/server/channelserver/handlers_caravan.go @@ -1,9 +1,36 @@ package channelserver import ( + "encoding/hex" "erupe-ce/network/mhfpacket" ) +func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetRyoudama) + // likely guild related + // REQ: 00 04 13 53 8F 18 00 + // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E + // REQ: 00 06 13 53 8F 18 00 + // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 + // REQ: 00 05 13 53 8F 18 00 + // RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00 + data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000") + doAckBufSucceed(s, pkt.AckHandle, data) +} + +func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {} + +func handleMsgMhfGetTinyBin(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetTinyBin) + // requested after conquest quests + doAckBufSucceed(s, pkt.AckHandle, []byte{}) +} + +func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostTinyBin) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_diva.go b/server/channelserver/handlers_diva.go index 48e6dcecf..0daabe70c 100644 --- a/server/channelserver/handlers_diva.go +++ b/server/channelserver/handlers_diva.go @@ -15,7 +15,7 @@ func cleanupDiva(s *Session) { func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 { timestamps := make([]uint32, 6) - midnight := Time_Current_Midnight() + midnight := TimeMidnight() if debug && start <= 3 { midnight := uint32(midnight.Unix()) switch start { @@ -43,7 +43,7 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 { } return timestamps } - if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 { + if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 { cleanupDiva(s) // Generate a new diva defense, starting midnight tomorrow start = uint32(midnight.Add(24 * time.Hour).Unix()) diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index 6de9fd416..41d6259f1 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -61,7 +61,7 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule) 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()) + rows, _ := s.server.db.Queryx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1 OR start_time=$2`, TimeMidnight(), TimeMidnight().Add(24*time.Hour)) for rows.Next() { var feature activeFeature rows.StructScan(&feature) @@ -71,19 +71,19 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) { if len(features) < 2 { if len(features) == 0 { feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons) - feature.StartTime = Time_Current_Midnight().Add(-24 * time.Hour) + feature.StartTime = TimeMidnight() 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() + feature.StartTime = TimeMidnight().Add(24 * time.Hour) features = append(features, feature) s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures) } bf := byteframe.NewByteFrame() bf.WriteUint8(2) - bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Add(-5 * time.Minute).Unix())) for _, feature := range features { bf.WriteUint32(uint32(feature.StartTime.Unix())) bf.WriteUint32(feature.ActiveFeatures) @@ -119,129 +119,97 @@ func generateFeatureWeapons(count int) activeFeature { } type loginBoost struct { - WeekReq, WeekCount uint8 - Available bool - Expiration uint32 + WeekReq uint8 `db:"week_req"` + WeekCount uint8 + Active bool + Expiration time.Time `db:"expiration"` + Reset time.Time `db:"reset"` } func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetKeepLoginBoostStatus) - var loginBoostStatus []loginBoost - insert := false - boostState, err := s.server.db.Query("SELECT week_req, week_count, available, end_time FROM login_boost_state WHERE char_id=$1 ORDER BY week_req ASC", s.charID) + bf := byteframe.NewByteFrame() + + var loginBoosts []loginBoost + rows, err := s.server.db.Queryx("SELECT week_req, expiration, reset FROM login_boost WHERE char_id=$1 ORDER BY week_req", s.charID) if err != nil { - panic(err) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 35)) + return } - for boostState.Next() { - var boost loginBoost - err = boostState.Scan(&boost.WeekReq, &boost.WeekCount, &boost.Available, &boost.Expiration) - if err != nil { - panic(err) + for rows.Next() { + var temp loginBoost + rows.StructScan(&temp) + loginBoosts = append(loginBoosts, temp) + } + if len(loginBoosts) == 0 { + temp := TimeWeekStart() + loginBoosts = []loginBoost{ + {WeekReq: 1, Expiration: temp}, + {WeekReq: 2, Expiration: temp}, + {WeekReq: 3, Expiration: temp}, + {WeekReq: 4, Expiration: temp}, + {WeekReq: 5, Expiration: temp}, } - loginBoostStatus = append(loginBoostStatus, boost) - } - if len(loginBoostStatus) == 0 { - // create default Entries (should only been week 1 with ) - insert = true - loginBoostStatus = []loginBoost{ - { - WeekReq: 1, // weeks needed - WeekCount: 0, // weeks passed - Available: true, // available - Expiration: 0, //uint32(t.Add(120 * time.Minute).Unix()), // uncomment to enable permanently - }, - { - WeekReq: 2, - WeekCount: 0, - Available: true, - Expiration: 0, - }, - { - WeekReq: 3, - WeekCount: 0, - Available: true, - Expiration: 0, - }, - { - WeekReq: 4, - WeekCount: 0, - Available: true, - Expiration: 0, - }, - { - WeekReq: 5, - WeekCount: 0, - Available: true, - Expiration: 0, - }, + for _, boost := range loginBoosts { + s.server.db.Exec(`INSERT INTO login_boost VALUES ($1, $2, $3, $4)`, s.charID, boost.WeekReq, boost.Expiration, time.Time{}) } } - resp := byteframe.NewByteFrame() - CurrentWeek := Time_Current_Week_uint8() - for d := range loginBoostStatus { - if CurrentWeek == 1 && loginBoostStatus[d].WeekCount <= 5 { - loginBoostStatus[d].WeekCount = 0 + + for _, boost := range loginBoosts { + // Reset if next week + if !boost.Reset.IsZero() && boost.Reset.Before(TimeAdjusted()) { + boost.Expiration = TimeWeekStart() + boost.Reset = time.Time{} + s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, boost.Expiration, boost.Reset, s.charID, boost.WeekReq) } - if loginBoostStatus[d].WeekReq == CurrentWeek || loginBoostStatus[d].WeekCount != 0 { - loginBoostStatus[d].WeekCount = CurrentWeek + + boost.WeekCount = uint8((TimeAdjusted().Unix()-boost.Expiration.Unix())/604800 + 1) + + if boost.WeekCount >= boost.WeekReq { + boost.Active = true + boost.WeekCount = boost.WeekReq } - if !loginBoostStatus[d].Available && loginBoostStatus[d].WeekCount >= loginBoostStatus[d].WeekReq && uint32(time.Now().In(time.FixedZone("UTC+1", 1*60*60)).Unix()) >= loginBoostStatus[d].Expiration { - loginBoostStatus[d].Expiration = 1 + + // Show reset timer on expired boosts + if boost.Reset.After(TimeAdjusted()) { + boost.Active = true + boost.WeekCount = 0 } - if !insert { - _, err := s.server.db.Exec(`UPDATE login_boost_state SET week_count=$1, end_time=$2 WHERE char_id=$3 AND week_req=$4`, loginBoostStatus[d].WeekCount, loginBoostStatus[d].Expiration, s.charID, loginBoostStatus[d].WeekReq) - if err != nil { - panic(err) - } + + bf.WriteUint8(boost.WeekReq) + bf.WriteBool(boost.Active) + bf.WriteUint8(boost.WeekCount) + if !boost.Reset.IsZero() { + bf.WriteUint32(uint32(boost.Expiration.Unix())) + } else { + bf.WriteUint32(0) } } - for _, v := range loginBoostStatus { - if insert { - _, err := s.server.db.Exec(`INSERT INTO login_boost_state (char_id, week_req, week_count, available, end_time) VALUES ($1,$2,$3,$4,$5)`, s.charID, v.WeekReq, v.WeekCount, v.Available, v.Expiration) - if err != nil { - panic(err) - } - } - resp.WriteUint8(v.WeekReq) - resp.WriteUint8(v.WeekCount) - resp.WriteBool(v.Available) - resp.WriteUint32(v.Expiration) - } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) { - // Directly interacts with MhfGetKeepLoginBoostStatus - // TODO: make these states persistent on a per character basis pkt := p.(*mhfpacket.MsgMhfUseKeepLoginBoost) - var t = time.Now().In(time.FixedZone("UTC+1", 1*60*60)) - resp := byteframe.NewByteFrame() - resp.WriteUint8(0) - - // response is end timestamp based on input + var expiration time.Time + bf := byteframe.NewByteFrame() + bf.WriteUint8(0) switch pkt.BoostWeekUsed { case 1: - t = t.Add(120 * time.Minute) - resp.WriteUint32(uint32(t.Unix())) - case 2: - t = t.Add(240 * time.Minute) - resp.WriteUint32(uint32(t.Unix())) + fallthrough case 3: - t = t.Add(120 * time.Minute) - resp.WriteUint32(uint32(t.Unix())) + expiration = TimeAdjusted().Add(120 * time.Minute) case 4: - t = t.Add(180 * time.Minute) - resp.WriteUint32(uint32(t.Unix())) + expiration = TimeAdjusted().Add(180 * time.Minute) + case 2: + fallthrough case 5: - t = t.Add(240 * time.Minute) - resp.WriteUint32(uint32(t.Unix())) + expiration = TimeAdjusted().Add(240 * time.Minute) } - _, err := s.server.db.Exec(`UPDATE login_boost_state SET available='false', end_time=$1 WHERE char_id=$2 AND week_req=$3`, uint32(t.Unix()), s.charID, pkt.BoostWeekUsed) - if err != nil { - panic(err) - } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf.WriteUint32(uint32(expiration.Unix())) + s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, expiration, TimeWeekNext(), s.charID, pkt.BoostWeekUsed) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 9f925f3dc..2659aae1d 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -12,22 +12,25 @@ import ( func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveMezfesData) + s.server.db.Exec(`UPDATE characters SET mezfes=$1 WHERE id=$2`, pkt.RawDataPayload, s.charID) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadMezfesData) - - resp := byteframe.NewByteFrame() - resp.WriteUint32(0) // Unk - - resp.WriteUint8(2) // Count of the next 2 uint32s - resp.WriteUint32(0) - resp.WriteUint32(0) - - resp.WriteUint32(0) // Unk - - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + var data []byte + s.server.db.QueryRow(`SELECT mezfes FROM characters WHERE id=$1`, s.charID).Scan(&data) + bf := byteframe.NewByteFrame() + if len(data) > 0 { + bf.WriteBytes(data) + } else { + bf.WriteUint32(0) + bf.WriteUint8(2) + bf.WriteUint32(0) + bf.WriteUint32(0) + bf.WriteUint32(0) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { @@ -38,7 +41,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { // Unk // Start? // End? - midnight := Time_Current_Midnight() + midnight := TimeMidnight() switch state { case 1: bf.WriteUint32(uint32(midnight.Unix())) @@ -57,13 +60,13 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(uint32(midnight.Add(7 * 24 * time.Hour).Unix())) default: bf.WriteBytes(make([]byte, 16)) - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time + bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time bf.WriteUint8(3) bf.WriteBytes(make([]byte, 4)) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) return } - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time + bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time bf.WriteUint8(3) ps.Uint8(bf, "", false) bf.WriteUint16(0) // numEvents @@ -98,7 +101,7 @@ func cleanupFesta(s *Session) { func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { timestamps := make([]uint32, 5) - midnight := Time_Current_Midnight() + midnight := TimeMidnight() if debug && start <= 3 { midnight := uint32(midnight.Unix()) switch start { @@ -123,7 +126,7 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 { } return timestamps } - if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 { + if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 { cleanupFesta(s) // Generate a new festa, starting midnight tomorrow start = uint32(midnight.Add(24 * time.Hour).Unix()) @@ -167,7 +170,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { timestamps = generateFestaTimestamps(s, start, false) } - if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) { + if timestamps[0] > uint32(TimeAdjusted().Unix()) { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } @@ -180,7 +183,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { for _, timestamp := range timestamps { bf.WriteUint32(timestamp) } - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Unix())) bf.WriteUint8(4) ps.Uint8(bf, "", false) bf.WriteUint32(0) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index feb56857f..dccc47005 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1732,57 +1732,59 @@ func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) { } type GuildMeal struct { - ID uint32 `db:"id"` - MealID uint32 `db:"meal_id"` - Level uint32 `db:"level"` - Expires uint32 `db:"expires"` + ID uint32 `db:"id"` + MealID uint32 `db:"meal_id"` + Level uint32 `db:"level"` + CreatedAt time.Time `db:"created_at"` } func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadGuildCooking) - guild, _ := GetGuildInfoByCharacterId(s, s.charID) - data, err := s.server.db.Queryx("SELECT id, meal_id, level, expires FROM guild_meals WHERE guild_id = $1", guild.ID) + data, err := s.server.db.Queryx("SELECT id, meal_id, level, created_at FROM guild_meals WHERE guild_id = $1", guild.ID) if err != nil { s.logger.Error("Failed to get guild meals from db", zap.Error(err)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2)) + return } - temp := byteframe.NewByteFrame() - count := 0 + var meals []GuildMeal + var temp GuildMeal for data.Next() { - mealData := &GuildMeal{} - err = data.StructScan(&mealData) + err = data.StructScan(&temp) if err != nil { continue } - if mealData.Expires > uint32(Time_Current_Adjusted().Add(-60*time.Minute).Unix()) { - count++ - temp.WriteUint32(mealData.ID) - temp.WriteUint32(mealData.MealID) - temp.WriteUint32(mealData.Level) - temp.WriteUint32(mealData.Expires) + if temp.CreatedAt.Add(60 * time.Minute).After(TimeAdjusted()) { + meals = append(meals, temp) } } bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(count)) - bf.WriteBytes(temp.Data()) + bf.WriteUint16(uint16(len(meals))) + for _, meal := range meals { + bf.WriteUint32(meal.ID) + bf.WriteUint32(meal.MealID) + bf.WriteUint32(meal.Level) + bf.WriteUint32(uint32(meal.CreatedAt.Unix())) + } doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking) guild, _ := GetGuildInfoByCharacterId(s, s.charID) + currentTime := TimeAdjusted() if pkt.OverwriteID != 0 { - _, err := s.server.db.Exec("DELETE FROM guild_meals WHERE id = $1", pkt.OverwriteID) - if err != nil { - s.logger.Error("Failed to delete meal in db", zap.Error(err)) - } + s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, currentTime, pkt.OverwriteID) + } else { + s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, currentTime).Scan(&pkt.OverwriteID) } - _, err := s.server.db.Exec("INSERT INTO guild_meals (guild_id, meal_id, level, expires) VALUES ($1, $2, $3, $4)", guild.ID, pkt.MealID, pkt.Success, Time_Current_Adjusted().Add(30*time.Minute).Unix()) - if err != nil { - s.logger.Error("Failed to register meal in db", zap.Error(err)) - } - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x00}) + bf := byteframe.NewByteFrame() + bf.WriteUint16(1) + bf.WriteUint32(pkt.OverwriteID) + bf.WriteUint32(uint32(pkt.MealID)) + bf.WriteUint32(uint32(pkt.Success)) + bf.WriteUint32(uint32(currentTime.Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) { @@ -1819,59 +1821,51 @@ func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) { } type MessageBoardPost struct { - Type uint32 `db:"post_type"` - StampID uint32 `db:"stamp_id"` - Title string `db:"title"` - Body string `db:"body"` - AuthorID uint32 `db:"author_id"` - Timestamp uint64 `db:"created_at"` - LikedBy string `db:"liked_by"` + ID uint32 `db:"id"` + StampID uint32 `db:"stamp_id"` + Title string `db:"title"` + Body string `db:"body"` + AuthorID uint32 `db:"author_id"` + Timestamp time.Time `db:"created_at"` + LikedBy string `db:"liked_by"` } func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMessageBoard) guild, _ := GetGuildInfoByCharacterId(s, s.charID) - - msgs, err := s.server.db.Queryx("SELECT post_type, stamp_id, title, body, author_id, (EXTRACT(epoch FROM created_at)::int) as created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType)) + if pkt.BoardType == 1 { + pkt.MaxPosts = 4 + } + msgs, err := s.server.db.Queryx("SELECT id, stamp_id, title, body, author_id, created_at, liked_by FROM guild_posts WHERE guild_id = $1 AND post_type = $2 ORDER BY created_at DESC", guild.ID, int(pkt.BoardType)) if err != nil { s.logger.Error("Failed to get guild messages from db", zap.Error(err)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - + s.server.db.Exec("UPDATE characters SET guild_post_checked = now() WHERE id = $1", s.charID) bf := byteframe.NewByteFrame() - noMsgs := true - postCount := 0 + var postCount uint32 for msgs.Next() { - noMsgs = false - postCount++ postData := &MessageBoardPost{} - msgs.StructScan(&postData) + err = msgs.StructScan(&postData) if err != nil { continue } - bf.WriteUint32(postData.Type) + postCount++ + bf.WriteUint32(postData.ID) bf.WriteUint32(postData.AuthorID) - bf.WriteUint64(postData.Timestamp) - likedBySlice := strings.Split(postData.LikedBy, ",") - if likedBySlice[0] == "" { - bf.WriteUint32(0) - } else { - bf.WriteUint32(uint32(len(likedBySlice))) - } + bf.WriteUint32(0) + bf.WriteUint32(uint32(postData.Timestamp.Unix())) + bf.WriteUint32(uint32(stringsupport.CSVLength(postData.LikedBy))) bf.WriteBool(stringsupport.CSVContains(postData.LikedBy, int(s.charID))) bf.WriteUint32(postData.StampID) ps.Uint32(bf, postData.Title, true) ps.Uint32(bf, postData.Body, true) } - if noMsgs { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) - } else { - data := byteframe.NewByteFrame() - data.WriteUint32(uint32(postCount)) - data.WriteBytes(bf.Data()) - doAckBufSucceed(s, pkt.AckHandle, data.Data()) - } + data := byteframe.NewByteFrame() + data.WriteUint32(postCount) + data.WriteBytes(bf.Data()) + doAckBufSucceed(s, pkt.AckHandle, data.Data()) } func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { @@ -1886,98 +1880,58 @@ func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - var titleConv, bodyConv string switch pkt.MessageOp { case 0: // Create message postType := bf.ReadUint32() // 0 = message, 1 = news - stampId := bf.ReadUint32() + stampID := bf.ReadUint32() titleLength := bf.ReadUint32() bodyLength := bf.ReadUint32() - title := bf.ReadBytes(uint(titleLength)) - body := bf.ReadBytes(uint(bodyLength)) - titleConv = stringsupport.SJISToUTF8(title) - bodyConv = stringsupport.SJISToUTF8(body) - _, err := s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, int(stampId), int(postType), titleConv, bodyConv) - if err != nil { - s.logger.Error("Failed to add new guild message to db", zap.Error(err)) - } - /* TODO: if there are too many messages, purge excess - _, err = s.server.db.Exec("") - if err != nil { - s.logger.Fatal("Failed to remove excess guild messages from db", zap.Error(err)) - } - */ + title := stringsupport.SJISToUTF8(bf.ReadBytes(uint(titleLength))) + body := stringsupport.SJISToUTF8(bf.ReadBytes(uint(bodyLength))) + s.server.db.Exec("INSERT INTO guild_posts (guild_id, author_id, stamp_id, post_type, title, body) VALUES ($1, $2, $3, $4, $5, $6)", guild.ID, s.charID, stampID, postType, title, body) + // TODO: if there are too many messages, purge excess case 1: // Delete message - postType := bf.ReadUint32() - timestamp := bf.ReadUint64() - _, err := s.server.db.Exec("DELETE FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID) - if err != nil { - s.logger.Error("Failed to delete guild message from db", zap.Error(err)) - } + postID := bf.ReadUint32() + s.server.db.Exec("DELETE FROM guild_posts WHERE id = $1", postID) case 2: // Update message - postType := bf.ReadUint32() - timestamp := bf.ReadUint64() + postID := bf.ReadUint32() + bf.ReadBytes(8) titleLength := bf.ReadUint32() bodyLength := bf.ReadUint32() - title := bf.ReadBytes(uint(titleLength)) - body := bf.ReadBytes(uint(bodyLength)) - titleConv = stringsupport.SJISToUTF8(title) - bodyConv = stringsupport.SJISToUTF8(body) - _, err := s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE post_type = $3 AND (EXTRACT(epoch FROM created_at)::int) = $4 AND guild_id = $5", titleConv, bodyConv, int(postType), int(timestamp), guild.ID) - if err != nil { - s.logger.Error("Failed to update guild message in db", zap.Error(err)) - } + title := stringsupport.SJISToUTF8(bf.ReadBytes(uint(titleLength))) + body := stringsupport.SJISToUTF8(bf.ReadBytes(uint(bodyLength))) + s.server.db.Exec("UPDATE guild_posts SET title = $1, body = $2 WHERE id = $3", title, body, postID) case 3: // Update stamp - postType := bf.ReadUint32() - timestamp := bf.ReadUint64() - stampId := bf.ReadUint32() - _, err := s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", int(stampId), int(postType), int(timestamp), guild.ID) - if err != nil { - s.logger.Error("Failed to update guild message stamp in db", zap.Error(err)) - } + postID := bf.ReadUint32() + bf.ReadBytes(8) + stampID := bf.ReadUint32() + s.server.db.Exec("UPDATE guild_posts SET stamp_id = $1 WHERE id = $2", stampID, postID) case 4: // Like message - postType := bf.ReadUint32() - timestamp := bf.ReadUint64() + postID := bf.ReadUint32() + bf.ReadBytes(8) likeState := bf.ReadBool() var likedBy string - err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE post_type = $1 AND (EXTRACT(epoch FROM created_at)::int) = $2 AND guild_id = $3", int(postType), int(timestamp), guild.ID).Scan(&likedBy) + err := s.server.db.QueryRow("SELECT liked_by FROM guild_posts WHERE id = $1", postID).Scan(&likedBy) if err != nil { s.logger.Error("Failed to get guild message like data from db", zap.Error(err)) } else { if likeState { likedBy = stringsupport.CSVAdd(likedBy, int(s.charID)) - _, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID) - if err != nil { - s.logger.Error("Failed to like guild message in db", zap.Error(err)) - } + s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE id = $2", likedBy, postID) } else { likedBy = stringsupport.CSVRemove(likedBy, int(s.charID)) - _, err := s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE post_type = $2 AND (EXTRACT(epoch FROM created_at)::int) = $3 AND guild_id = $4", likedBy, int(postType), int(timestamp), guild.ID) - if err != nil { - s.logger.Error("Failed to unlike guild message in db", zap.Error(err)) - } + s.server.db.Exec("UPDATE guild_posts SET liked_by = $1 WHERE id = $2", likedBy, postID) } } case 5: // Check for new messages - var timeChecked int + var timeChecked time.Time var newPosts int - err := s.server.db.QueryRow("SELECT (EXTRACT(epoch FROM guild_post_checked)::int) FROM characters WHERE id = $1", s.charID).Scan(&timeChecked) - if err != nil { - s.logger.Error("Failed to get last guild post check timestamp from db", zap.Error(err)) - } else { - _, err = s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID) - if err != nil { - s.logger.Error("Failed to update guild post check timestamp in db", zap.Error(err)) - } else { - err = s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2 AND author_id != $3", guild.ID, timeChecked, s.charID).Scan(&newPosts) - if err != nil { - s.logger.Error("Failed to check for new guild posts in db", zap.Error(err)) - } else { - if newPosts > 0 { - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) - return - } - } + err := s.server.db.QueryRow("SELECT guild_post_checked FROM characters WHERE id = $1", s.charID).Scan(&timeChecked) + if err == nil { + s.server.db.QueryRow("SELECT COUNT(*) FROM guild_posts WHERE guild_id = $1 AND (EXTRACT(epoch FROM created_at)::int) > $2", guild.ID, timeChecked.Unix()).Scan(&newPosts) + if newPosts > 0 { + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01}) + return } } } diff --git a/server/channelserver/handlers_guild_adventure.go b/server/channelserver/handlers_guild_adventure.go index 304b5721f..8e953bf24 100644 --- a/server/channelserver/handlers_guild_adventure.go +++ b/server/channelserver/handlers_guild_adventure.go @@ -52,7 +52,7 @@ func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventure) guild, _ := GetGuildInfoByCharacterId(s, s.charID) - _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(6*time.Hour).Unix()) + _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, TimeAdjusted().Unix(), TimeAdjusted().Add(6*time.Hour).Unix()) if err != nil { s.logger.Error("Failed to register guild adventure", zap.Error(err)) } @@ -87,7 +87,7 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildAdventureDiva(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventureDiva) guild, _ := GetGuildInfoByCharacterId(s, s.charID) - _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(1*time.Hour).Unix()) + _, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, TimeAdjusted().Unix(), TimeAdjusted().Add(1*time.Hour).Unix()) if err != nil { s.logger.Error("Failed to register guild adventure", zap.Error(err)) } diff --git a/server/channelserver/handlers_guild_tresure.go b/server/channelserver/handlers_guild_tresure.go index c5e2fa0dc..3d0918a84 100644 --- a/server/channelserver/handlers_guild_tresure.go +++ b/server/channelserver/handlers_guild_tresure.go @@ -27,7 +27,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { } bf := byteframe.NewByteFrame() hunts := 0 - rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, Time_Current_Adjusted().Unix()) + rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, TimeAdjusted().Unix()) for rows.Next() { hunt := &TreasureHunt{} err = rows.StructScan(&hunt) @@ -101,7 +101,7 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { } } _, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6, $7)", - guild.ID, s.charID, destination, level, Time_Current_Adjusted().Unix(), huntData.Data(), catsUsed) + guild.ID, s.charID, destination, level, TimeAdjusted().Unix(), huntData.Data(), catsUsed) if err != nil { panic(err) } @@ -124,7 +124,10 @@ func treasureHuntUnregister(s *Session) { } var huntID int var hunters string - rows, _ := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) + rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID) + if err != nil { + return + } for rows.Next() { rows.Scan(&huntID, &hunters) hunters = stringsupport.CSVRemove(hunters, int(s.charID)) diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index c52802c73..e44d9f1f2 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -43,9 +43,9 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) { Unk uint32 Timestamp uint32 }{ - {0, uint32(Time_Current_Midnight().Add(-12 * time.Hour).Unix())}, - {0, uint32(Time_Current_Midnight().Add(12 * time.Hour).Unix())}, - {0, uint32(Time_Current_Midnight().Add(36 * time.Hour).Unix())}, + {0, uint32(TimeMidnight().Add(-12 * time.Hour).Unix())}, + {0, uint32(TimeMidnight().Add(12 * time.Hour).Unix())}, + {0, uint32(TimeMidnight().Add(36 * time.Hour).Unix())}, } bf.WriteUint8(uint8(len(legendDispatch))) for _, dispatch := range legendDispatch { @@ -164,8 +164,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(pactID) bf.WriteUint32(cid) bf.WriteBool(false) // ? - bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix())) - bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix())) bf.WriteBytes(stringsupport.PaddedString(name, 18, true)) } else { bf.WriteUint8(0) @@ -180,8 +180,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { rows.Scan(&name, &cid, &pactID) temp.WriteUint32(pactID) temp.WriteUint32(cid) - temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix())) - temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix())) + temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix())) + temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix())) temp.WriteBytes(stringsupport.PaddedString(name, 18, true)) } bf.WriteUint8(loans) @@ -346,7 +346,7 @@ func getGuildAirouList(s *Session) []CatDefinition { FROM guild_hunts gh INNER JOIN characters c ON gh.host_id = c.id - WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, Time_Current_Adjusted().Unix()) + WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, TimeAdjusted().Unix()) if err != nil { s.logger.Warn("Failed to get recently used airous", zap.Error(err)) } diff --git a/server/channelserver/handlers_reward.go b/server/channelserver/handlers_reward.go index b4a1abc6d..73234c2ae 100644 --- a/server/channelserver/handlers_reward.go +++ b/server/channelserver/handlers_reward.go @@ -43,7 +43,3 @@ func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) { } func handleMsgMhfAcceptReadReward(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_shop_gacha.go b/server/channelserver/handlers_shop_gacha.go index 350e31acd..2b07b3168 100644 --- a/server/channelserver/handlers_shop_gacha.go +++ b/server/channelserver/handlers_shop_gacha.go @@ -2,14 +2,10 @@ package channelserver import ( "encoding/hex" - ps "erupe-ce/common/pascalstring" - "time" - "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" - "github.com/lib/pq" - "github.com/sachaos/lottery" - "go.uber.org/zap" + "math/rand" ) type ShopItem struct { @@ -28,16 +24,36 @@ type ShopItem struct { } type Gacha struct { - ID uint32 `db:"id"` - MinGR uint32 `db:"min_gr"` - MinHR uint32 `db:"min_hr"` - Name string `db:"name"` - Link1 string `db:"link1"` - Link2 string `db:"link2"` - Link3 string `db:"link3"` - Icon uint16 `db:"icon"` - Type uint16 `db:"type"` - Hide bool `db:"hide"` + ID uint32 `db:"id"` + MinGR uint32 `db:"min_gr"` + MinHR uint32 `db:"min_hr"` + Name string `db:"name"` + URLBanner string `db:"url_banner"` + URLFeature string `db:"url_feature"` + URLThumbnail string `db:"url_thumbnail"` + Wide bool `db:"wide"` + Recommended bool `db:"recommended"` + GachaType uint8 `db:"gacha_type"` + Hidden bool `db:"hidden"` +} + +type GachaEntry struct { + EntryType uint8 `db:"entry_type"` + ID uint32 `db:"id"` + ItemType uint8 `db:"item_type"` + ItemNumber uint16 `db:"item_number"` + ItemQuantity uint16 `db:"item_quantity"` + Weight float64 `db:"weight"` + Rarity uint8 `db:"rarity"` + Rolls uint8 `db:"rolls"` + FrontierPoints uint16 `db:"frontier_points"` + DailyLimit uint8 `db:"daily_limit"` +} + +type GachaItem struct { + ItemType uint8 `db:"item_type"` + ItemID uint16 `db:"item_id"` + Quantity uint16 `db:"quantity"` } func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { @@ -55,7 +71,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { switch pkt.ShopType { case 1: // Running gachas var count uint16 - shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, link1, link2, link3, icon, type, hide FROM gacha_shop") + shopEntries, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop") if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return @@ -74,12 +90,18 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { resp.WriteUint32(gacha.MinHR) resp.WriteUint32(0) // only 0 in known packet ps.Uint8(resp, gacha.Name, true) - ps.Uint8(resp, gacha.Link1, false) - ps.Uint8(resp, gacha.Link2, false) - resp.WriteBool(gacha.Hide) - ps.Uint8(resp, gacha.Link3, false) - resp.WriteUint16(gacha.Icon) - resp.WriteUint16(gacha.Type) + ps.Uint8(resp, gacha.URLBanner, false) + ps.Uint8(resp, gacha.URLFeature, false) + resp.WriteBool(gacha.Wide) + ps.Uint8(resp, gacha.URLThumbnail, false) + resp.WriteUint8(0) // Unk + if gacha.Recommended { + resp.WriteUint8(2) + } else { + resp.WriteUint8(0) + } + resp.WriteUint8(gacha.GachaType) + resp.WriteBool(gacha.Hidden) count++ } resp.Seek(0, 0) @@ -87,47 +109,62 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { resp.WriteUint16(count) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) case 2: // Actual gacha - shopEntries, err := s.server.db.Query("SELECT entryType, itemhash, currType, currNumber, currQuant, percentage, rarityIcon, rollsCount, itemCount, dailyLimit, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1", pkt.ShopID) + bf := byteframe.NewByteFrame() + bf.WriteUint32(pkt.ShopID) + var gachaType int + s.server.db.QueryRow(`SELECT gacha_type FROM gacha_shop WHERE id = $1`, pkt.ShopID).Scan(&gachaType) + entries, err := s.server.db.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID) if err != nil { doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) return } - var entryType, currType, rarityIcon, rollsCount, itemCount, dailyLimit uint8 - var currQuant, currNumber, percentage uint16 - var itemhash uint32 - var itemType, itemId, quantity pq.Int64Array - var count uint16 - resp := byteframe.NewByteFrame() - resp.WriteUint32(pkt.ShopID) - resp.WriteUint16(0) // total defs - for shopEntries.Next() { - err = shopEntries.Scan(&entryType, &itemhash, &currType, &currNumber, &currQuant, &percentage, &rarityIcon, &rollsCount, &itemCount, &dailyLimit, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity)) + var divisor float64 + s.server.db.QueryRow(`SELECT COALESCE(SUM(weight) / 100000.0, 0) AS chance FROM gacha_entries WHERE gacha_id = $1`, pkt.ShopID).Scan(&divisor) + var entryCount uint16 + bf.WriteUint16(0) + gachaEntry := GachaEntry{} + gachaItem := GachaItem{} + for entries.Next() { + entryCount++ + entries.StructScan(&gachaEntry) + bf.WriteUint8(gachaEntry.EntryType) + bf.WriteUint32(gachaEntry.ID) + bf.WriteUint8(gachaEntry.ItemType) + bf.WriteUint16(0) + bf.WriteUint16(gachaEntry.ItemNumber) + bf.WriteUint16(gachaEntry.ItemQuantity) + if gachaType >= 4 { // If box + bf.WriteUint16(1) + } else { + bf.WriteUint16(uint16(gachaEntry.Weight / divisor)) + } + bf.WriteUint8(gachaEntry.Rarity) + bf.WriteUint8(gachaEntry.Rolls) + + var itemCount uint8 + temp := byteframe.NewByteFrame() + items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, gachaEntry.ID) if err != nil { - panic(err) + bf.WriteUint8(0) + } else { + for items.Next() { + itemCount++ + items.StructScan(&gachaItem) + temp.WriteUint16(uint16(gachaItem.ItemType)) + temp.WriteUint16(gachaItem.ItemID) + temp.WriteUint16(gachaItem.Quantity) + } + bf.WriteUint8(itemCount) } - resp.WriteUint8(entryType) - resp.WriteUint32(itemhash) - resp.WriteUint8(currType) - resp.WriteUint16(0) // unk, always 0 in existing packets - resp.WriteUint16(currNumber) // it's either item ID or quantity for gacha coins - resp.WriteUint16(currQuant) // only for item ID - resp.WriteUint16(percentage) - resp.WriteUint8(rarityIcon) - resp.WriteUint8(rollsCount) - resp.WriteUint8(itemCount) - resp.WriteUint16(0) // unk, always 0 in existing packets - resp.WriteUint8(dailyLimit) - resp.WriteUint8(0) // unk, always 0 in existing packets - for i := 0; i < int(itemCount); i++ { - resp.WriteUint16(uint16(itemType[i])) // unk, always 0 in existing packets - resp.WriteUint16(uint16(itemId[i])) // unk, always 0 in existing packets - resp.WriteUint16(uint16(quantity[i])) // unk, always 0 in existing packets - } - count++ + + bf.WriteUint16(gachaEntry.FrontierPoints) + bf.WriteUint8(gachaEntry.DailyLimit) + bf.WriteUint8(0) + bf.WriteBytes(temp.Data()) } - resp.Seek(4, 0) - resp.WriteUint16(count) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + bf.Seek(4, 0) + bf.WriteUint16(entryCount) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) case 4: // N Points, 0-6 doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) case 5: // GCP->Item, 0-6 @@ -206,124 +243,363 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory) bf := byteframe.NewByteFrame() - bf.WriteUint8(0) + bf.WriteUint8(1) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGachaPoint) var fp, gp, gt uint32 - s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_prem, 0), COALESCE(gacha_trial,0) FROM characters WHERE id=$1", s.charID).Scan(&fp, &gp, >) + s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_premium, 0), COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.charID).Scan(&fp, &gp, >) resp := byteframe.NewByteFrame() - resp.WriteUint32(gp) // Real Gacha Points? - resp.WriteUint32(gt) // Trial Gacha Point? - resp.WriteUint32(fp) // Frontier Points? + resp.WriteUint32(gp) + resp.WriteUint32(gt) + resp.WriteUint32(fp) doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -type gachaItem struct { - itemhash uint32 - percentage uint16 - rarityIcon byte - itemCount byte - itemType pq.Int64Array - itemId pq.Int64Array - quantity pq.Int64Array +func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUseGachaPoint) + if pkt.TrialCoins > 0 { + s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.TrialCoins, s.charID) + } + if pkt.PremiumCoins > 0 { + s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.PremiumCoins, s.charID) + } + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } -func (i gachaItem) Weight() int { - return int(i.percentage) +func spendGachaCoin(s *Session, quantity uint16) { + var gt uint16 + s.server.db.QueryRow(`SELECT COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(>) + if quantity <= gt { + s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID) + } else { + s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID) + } +} + +func transactGacha(s *Session, gachaID uint32, rollID uint8) (error, int) { + var itemType uint8 + var itemNumber uint16 + var rolls int + err := s.server.db.QueryRowx(`SELECT item_type, item_number, rolls FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, gachaID, rollID).Scan(&itemType, &itemNumber, &rolls) + if err != nil { + return err, 0 + } + switch itemType { + /* + valid types that need manual savedata manipulation: + - Ryoudan Points + - Bond Points + - Image Change Points + valid types that work (no additional code needed): + - Tore Points + - Festa Points + */ + case 17: + _ = addPointNetcafe(s, int(itemNumber)*-1) + case 19: + fallthrough + case 20: + spendGachaCoin(s, itemNumber) + case 21: + s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", itemNumber, s.charID) + } + return nil, rolls +} + +func getGuaranteedItems(s *Session, gachaID uint32, rollID uint8) []GachaItem { + var rewards []GachaItem + var reward GachaItem + items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = (SELECT id FROM gacha_entries WHERE entry_type = $1 AND gacha_id = $2)`, rollID, gachaID) + if err == nil { + for items.Next() { + items.StructScan(&reward) + rewards = append(rewards, reward) + } + } + return rewards +} + +func addGachaItem(s *Session, items []GachaItem) { + var data []byte + s.server.db.QueryRow(`SELECT gacha_items FROM characters WHERE id = $1`, s.charID).Scan(&data) + if len(data) > 0 { + numItems := int(data[0]) + data = data[1:] + oldItem := byteframe.NewByteFrameFromBytes(data) + for i := 0; i < numItems; i++ { + items = append(items, GachaItem{ + ItemType: oldItem.ReadUint8(), + ItemID: oldItem.ReadUint16(), + Quantity: oldItem.ReadUint16(), + }) + } + } + newItem := byteframe.NewByteFrame() + newItem.WriteUint8(uint8(len(items))) + for i := range items { + newItem.WriteUint8(items[i].ItemType) + newItem.WriteUint16(items[i].ItemID) + newItem.WriteUint16(items[i].Quantity) + } + s.server.db.Exec(`UPDATE characters SET gacha_items = $1 WHERE id = $2`, newItem.Data(), s.charID) +} + +func getRandomEntries(entries []GachaEntry, rolls int, isBox bool) ([]GachaEntry, error) { + var chosen []GachaEntry + var totalWeight float64 + for i := range entries { + totalWeight += entries[i].Weight + } + for { + if !isBox { + result := rand.Float64() * totalWeight + for _, entry := range entries { + result -= entry.Weight + if result < 0 { + chosen = append(chosen, entry) + break + } + } + } else { + result := rand.Intn(len(entries)) + chosen = append(chosen, entries[result]) + entries[result] = entries[len(entries)-1] + entries = entries[:len(entries)-1] + } + if rolls == len(chosen) { + break + } + } + return chosen, nil +} + +func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReceiveGachaItem) + var data []byte + err := s.server.db.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.charID, []byte{0x00}).Scan(&data) + if err != nil { + data = []byte{0x00} + } + + // I think there are still some edge cases where rewards can be nulled via overflow + if data[0] > 36 || len(data) > 181 { + resp := byteframe.NewByteFrame() + resp.WriteUint8(36) + resp.WriteBytes(data[1:181]) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + } else { + doAckBufSucceed(s, pkt.AckHandle, data) + } + + if !pkt.Freeze { + if data[0] > 36 || len(data) > 181 { + update := byteframe.NewByteFrame() + update.WriteUint8(uint8(len(data[181:]) / 5)) + update.WriteBytes(data[181:]) + s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", update.Data(), s.charID) + } else { + s.server.db.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.charID) + } + } } func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfPlayNormalGacha) - // needs to query db for input gacha and return a result or number of results - // uint8 number of results - // uint8 item type - // uint16 item id - // uint16 quantity - - var currType, rarityIcon, rollsCount, itemCount byte - var currQuant, currNumber, percentage uint16 - var itemhash uint32 - var itemType, itemId, quantity pq.Int64Array - var items []lottery.Weighter - // get info for updating data and calculating costs - err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount) + bf := byteframe.NewByteFrame() + var gachaEntries []GachaEntry + var entry GachaEntry + var rewards []GachaItem + var reward GachaItem + err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType) if err != nil { - panic(err) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return } - // get existing items in storage if any - var data []byte - _ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data) - if len(data) == 0 { - data = []byte{0x00} - } - // get gacha items and iterate through them for gacha roll - shopEntries, err := s.server.db.Query("SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=100", pkt.GachaHash) + temp := byteframe.NewByteFrame() + entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) if err != nil { - panic(err) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return } - for shopEntries.Next() { - err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity)) + for entries.Next() { + entries.StructScan(&entry) + gachaEntries = append(gachaEntries, entry) + } + rewardEntries, err := getRandomEntries(gachaEntries, rolls, false) + for i := range rewardEntries { + items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) if err != nil { - panic(err) + continue } - items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity}) - } - // execute rolls, build response and update database - results := byte(0) - resp := byteframe.NewByteFrame() - dbUpdate := byteframe.NewByteFrame() - resp.WriteUint8(0) // results go here later - l := lottery.NewDefaultLottery() - for x := 0; x < int(rollsCount); x++ { - ind := l.Draw(items) - results += items[ind].(*gachaItem).itemCount - for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ { - // items in storage don't get rarity - dbUpdate.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y])) - dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y])) - dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y])) - data = append(data, dbUpdate.Data()...) - dbUpdate.Seek(0, 0) - // response needs all item info and the rarity - resp.WriteBytes(dbUpdate.Data()) - resp.WriteUint8(items[ind].(*gachaItem).rarityIcon) + for items.Next() { + items.StructScan(&reward) + rewards = append(rewards, reward) + temp.WriteUint8(reward.ItemType) + temp.WriteUint16(reward.ItemID) + temp.WriteUint16(reward.Quantity) + temp.WriteUint8(entry.Rarity) } } - resp.Seek(0, 0) - resp.WriteUint8(results) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - - // add claimables to DB - data[0] = data[0] + results - _, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID) - if err != nil { - s.logger.Fatal("Failed to update minidata in db", zap.Error(err)) - } - // deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards - if currType == 19 { - _, err = s.server.db.Exec("UPDATE characters SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end WHERE id=$2", currNumber, s.charID) - } - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } + bf.WriteUint8(uint8(len(rewards))) + bf.WriteBytes(temp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + addGachaItem(s, rewards) } -func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) { - // should write to database when that's set up - pkt := p.(*mhfpacket.MsgMhfUseGachaPoint) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPlayStepupGacha) + bf := byteframe.NewByteFrame() + var gachaEntries []GachaEntry + var entry GachaEntry + var rewards []GachaItem + var reward GachaItem + err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+(SELECT frontier_points FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.GachaID, pkt.RollType, s.charID) + s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID) + s.server.db.Exec(`INSERT INTO gacha_stepup (gacha_id, step, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, pkt.RollType+1, s.charID) + temp := byteframe.NewByteFrame() + guaranteedItems := getGuaranteedItems(s, pkt.GachaID, pkt.RollType) + for _, item := range guaranteedItems { + temp.WriteUint8(item.ItemType) + temp.WriteUint16(item.ItemID) + temp.WriteUint16(item.Quantity) + temp.WriteUint8(0) + } + entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + for entries.Next() { + entries.StructScan(&entry) + gachaEntries = append(gachaEntries, entry) + } + rewardEntries, err := getRandomEntries(gachaEntries, rolls, false) + for i := range rewardEntries { + items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + if err != nil { + continue + } + for items.Next() { + items.StructScan(&reward) + rewards = append(rewards, reward) + temp.WriteUint8(reward.ItemType) + temp.WriteUint16(reward.ItemID) + temp.WriteUint16(reward.Quantity) + temp.WriteUint8(entry.Rarity) + } + } + bf.WriteUint8(uint8(len(rewards) + len(guaranteedItems))) + bf.WriteUint8(uint8(len(rewards))) + bf.WriteBytes(temp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + addGachaItem(s, rewards) + addGachaItem(s, guaranteedItems) +} + +func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetStepupStatus) + // TODO: Reset daily (noon) + var step uint8 + s.server.db.QueryRow(`SELECT step FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID).Scan(&step) + var stepCheck int + s.server.db.QueryRow(`SELECT COUNT(1) FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, pkt.GachaID, step).Scan(&stepCheck) + if stepCheck == 0 { + s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID) + step = 0 + } + bf := byteframe.NewByteFrame() + bf.WriteUint8(step) + bf.WriteUint32(uint32(TimeAdjusted().Unix())) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoxGachaInfo) + entries, err := s.server.db.Queryx(`SELECT entry_id FROM gacha_box WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + var entryIDs []uint32 + for entries.Next() { + var entryID uint32 + entries.Scan(&entryID) + entryIDs = append(entryIDs, entryID) + } + bf := byteframe.NewByteFrame() + bf.WriteUint8(uint8(len(entryIDs))) + for i := range entryIDs { + bf.WriteUint32(entryIDs[i]) + bf.WriteBool(true) + } + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) +} + +func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPlayBoxGacha) + bf := byteframe.NewByteFrame() + var gachaEntries []GachaEntry + var entry GachaEntry + var rewards []GachaItem + var reward GachaItem + err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + temp := byteframe.NewByteFrame() + entries, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID) + if err != nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1)) + return + } + for entries.Next() { + entries.StructScan(&entry) + gachaEntries = append(gachaEntries, entry) + } + rewardEntries, err := getRandomEntries(gachaEntries, rolls, true) + for i := range rewardEntries { + items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID) + if err != nil { + continue + } + s.server.db.Exec(`INSERT INTO gacha_box (gacha_id, entry_id, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, rewardEntries[i].ID, s.charID) + for items.Next() { + items.StructScan(&reward) + rewards = append(rewards, reward) + temp.WriteUint8(reward.ItemType) + temp.WriteUint16(reward.ItemID) + temp.WriteUint16(reward.Quantity) + temp.WriteUint8(0) + } + } + bf.WriteUint8(uint8(len(rewards))) + bf.WriteBytes(temp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) + addGachaItem(s, rewards) +} + +func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfResetBoxGachaInfo) + s.server.db.Exec("DELETE FROM gacha_box WHERE gacha_id = $1 AND character_id = $2", pkt.GachaID, s.charID) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } func handleMsgMhfExchangeFpoint2Item(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfExchangeFpoint2Item) var balance uint32 var itemValue, quantity int - _ = s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue) + s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue) cost := (int(pkt.Quantity) * quantity) * itemValue - s.server.db.QueryRow("UPDATE characters SET frontier_points=frontier_points::int - $1 WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance) + s.server.db.QueryRow("UPDATE users u SET frontier_points=frontier_points::int - $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance) bf := byteframe.NewByteFrame() bf.WriteUint32(balance) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) @@ -335,7 +611,7 @@ func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) { var itemValue, quantity int s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue) cost := (int(pkt.Quantity) / quantity) * itemValue - s.server.db.QueryRow("UPDATE characters SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE id=$2 RETURNING frontier_points", cost, s.charID).Scan(&balance) + s.server.db.QueryRow("UPDATE users u SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance) bf := byteframe.NewByteFrame() bf.WriteUint32(balance) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) @@ -394,291 +670,6 @@ func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } -func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfPlayStepupGacha) - results := byte(0) - stepResults := byte(0) - resp := byteframe.NewByteFrame() - rollFrame := byteframe.NewByteFrame() - stepFrame := byteframe.NewByteFrame() - stepData := []byte{} - var currType, rarityIcon, rollsCount, itemCount byte - var currQuant, currNumber, percentage uint16 - var itemhash uint32 - var itemType, itemId, quantity pq.Int64Array - var items []lottery.Weighter - // get info for updating data and calculating costs - err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity)) - if err != nil { - panic(err) - } - // get existing items in storage if any - var data []byte - _ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data) - if len(data) == 0 { - data = []byte{0x00} - } - // roll definition includes items with step up gachas that are appended last - for x := 0; x < int(itemCount); x++ { - stepFrame.WriteUint8(uint8(itemType[x])) - stepFrame.WriteUint16(uint16(itemId[x])) - stepFrame.WriteUint16(uint16(quantity[x])) - stepData = append(stepData, stepFrame.Data()...) - stepFrame.WriteUint8(0) // rarity still defined - stepResults++ - } - // get gacha items and iterate through them for gacha roll - shopEntries, err := s.server.db.Query("SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity FROM gacha_shop_items WHERE shophash=$1 AND entryType=100", pkt.GachaHash) - if err != nil { - panic(err) - } - for shopEntries.Next() { - err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity)) - if err != nil { - panic(err) - } - items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity}) - } - // execute rolls, build response and update database - resp.WriteUint16(0) // results count goes here later - l := lottery.NewDefaultLottery() - for x := 0; x < int(rollsCount); x++ { - ind := l.Draw(items) - results += items[ind].(*gachaItem).itemCount - for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ { - // items in storage don't get rarity - rollFrame.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y])) - rollFrame.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y])) - rollFrame.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y])) - data = append(data, rollFrame.Data()...) - rollFrame.Seek(0, 0) - // response needs all item info and the rarity - resp.WriteBytes(rollFrame.Data()) - resp.WriteUint8(items[ind].(*gachaItem).rarityIcon) - } - } - resp.WriteBytes(stepFrame.Data()) - resp.Seek(0, 0) - resp.WriteUint8(results + stepResults) - resp.WriteUint8(results) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - - // add claimables to DB - data = append(data, stepData...) - data[0] = data[0] + results + stepResults - _, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID) - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } - // deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards - // reduce real if trial don't cover cost - if currType == 19 { - _, err = s.server.db.Exec(`UPDATE characters - SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, - gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end - WHERE id=$2`, currNumber, s.charID) - } - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } - // update step progression - _, err = s.server.db.Exec("UPDATE stepup_state SET step_progression = $1 WHERE char_id = $2", pkt.RollType+1, s.charID) - if err != nil { - s.logger.Fatal("Failed to update step_progression in db", zap.Error(err)) - } - -} - -func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfReceiveGachaItem) - // persistent for claimable items on cat - var data []byte - err := s.server.db.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.charID, []byte{0x00}).Scan(&data) - if err != nil { - panic("Failed to get gacha_items") - } - // limit of 36 items are returned - if data[0] > 36 { - outData := make([]byte, 181) - copy(outData, data[0:181]) - outData[0] = byte(36) - saveData := append(data[:1], data[181:]...) - saveData[0] = saveData[0] - 36 - doAckBufSucceed(s, pkt.AckHandle, outData) - if pkt.Unk0 != 0x2401 { - _, err := s.server.db.Exec("UPDATE characters SET gacha_items = $2 WHERE id = $1", s.charID, saveData) - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } - } - } else { - doAckBufSucceed(s, pkt.AckHandle, data) - if pkt.Unk0 != 0x2401 { - _, err := s.server.db.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.charID) - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } - } - } -} - -func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetStepupStatus) - // get the reset time from db - var step_progression int - var step_time time.Time - err := s.server.db.QueryRow(`SELECT COALESCE(step_progression, 0), COALESCE(step_time, $1) FROM stepup_state WHERE char_id = $2 AND shophash = $3`, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), s.charID, pkt.GachaHash).Scan(&step_progression, &step_time) - if err != nil { - s.logger.Fatal("Failed to Select coalesce in db", zap.Error(err)) - } - - // calculate next midday - var t = time.Now().In(time.FixedZone("UTC+9", 9*60*60)) - year, month, day := t.Date() - midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location()) - if t.After(midday) { - midday = midday.Add(24 * time.Hour) - } - // after midday or not set - if t.After(step_time) { - step_progression = 0 - } - _, err = s.server.db.Exec(`INSERT INTO stepup_state (shophash, step_progression, step_time, char_id) - VALUES ($1,$2,$3,$4) ON CONFLICT (shophash, char_id) - DO UPDATE SET step_progression=$2, step_time=$3 - WHERE EXCLUDED.char_id=$4 AND EXCLUDED.shophash=$1`, pkt.GachaHash, step_progression, midday, s.charID) - if err != nil { - s.logger.Fatal("Failed to update platedata savedata in db", zap.Error(err)) - } - resp := byteframe.NewByteFrame() - resp.WriteUint8(uint8(step_progression)) - resp.WriteUint32(uint32(time.Now().In(time.FixedZone("UTC+9", 9*60*60)).Unix())) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) -} - func handleMsgMhfPlayFreeGacha(s *Session, p mhfpacket.MHFPacket) { // not sure this is used anywhere, free gachas use the MSG_MHF_PLAY_NORMAL_GACHA method in captures } - -func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetBoxGachaInfo) - count := 0 - var used_itemhash pq.Int64Array - // pull array of used values - // single sized respone with 0x00 is a valid with no items present - _ = s.server.db.QueryRow("SELECT used_itemhash FROM lucky_box_state WHERE shophash=$1 AND char_id=$2", pkt.GachaHash, s.charID).Scan((*pq.Int64Array)(&used_itemhash)) - resp := byteframe.NewByteFrame() - resp.WriteUint8(0) - for ind := range used_itemhash { - resp.WriteUint32(uint32(used_itemhash[ind])) - resp.WriteUint8(1) - count++ - } - resp.Seek(0, 0) - resp.WriteUint8(uint8(count)) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) -} - -func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfPlayBoxGacha) - // needs to query db for input gacha and return a result or number of results - // uint8 number of results - // uint8 item type - // uint16 item id - // uint16 quantity - - var currType, rarityIcon, rollsCount, itemCount byte - var currQuant, currNumber, percentage uint16 - var itemhash uint32 - var itemType, itemId, quantity, usedItemHash pq.Int64Array - var items []lottery.Weighter - // get info for updating data and calculating costs - err := s.server.db.QueryRow("SELECT currType, currNumber, currQuant, rollsCount FROM gacha_shop_items WHERE shophash=$1 AND entryType=$2", pkt.GachaHash, pkt.RollType).Scan(&currType, &currNumber, &currQuant, &rollsCount) - if err != nil { - panic(err) - } - // get existing items in storage if any - var data []byte - _ = s.server.db.QueryRow("SELECT gacha_items FROM characters WHERE id = $1", s.charID).Scan(&data) - if len(data) == 0 { - data = []byte{0x00} - } - // get gacha items and iterate through them for gacha roll - shopEntries, err := s.server.db.Query(`SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity - FROM gacha_shop_items - WHERE shophash=$1 AND entryType=100 - EXCEPT ALL SELECT itemhash, percentage, rarityIcon, itemCount, itemType, itemId, quantity - FROM gacha_shop_items gsi JOIN lucky_box_state lbs ON gsi.itemhash = ANY(lbs.used_itemhash) - WHERE lbs.char_id=$2`, pkt.GachaHash, s.charID) - if err != nil { - panic(err) - } - for shopEntries.Next() { - err = shopEntries.Scan(&itemhash, &percentage, &rarityIcon, &itemCount, (*pq.Int64Array)(&itemType), (*pq.Int64Array)(&itemId), (*pq.Int64Array)(&quantity)) - if err != nil { - panic(err) - } - items = append(items, &gachaItem{itemhash: itemhash, percentage: percentage, rarityIcon: rarityIcon, itemCount: itemCount, itemType: itemType, itemId: itemId, quantity: quantity}) - } - // execute rolls, build response and update database - results := byte(0) - resp := byteframe.NewByteFrame() - dbUpdate := byteframe.NewByteFrame() - resp.WriteUint8(0) // results go here later - l := lottery.NewDefaultLottery() - for x := 0; x < int(rollsCount); x++ { - ind := l.Draw(items) - results += items[ind].(*gachaItem).itemCount - for y := 0; y < int(items[ind].(*gachaItem).itemCount); y++ { - // items in storage don't get rarity - dbUpdate.WriteUint8(uint8(items[ind].(*gachaItem).itemType[y])) - dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).itemId[y])) - dbUpdate.WriteUint16(uint16(items[ind].(*gachaItem).quantity[y])) - data = append(data, dbUpdate.Data()...) - dbUpdate.Seek(0, 0) - // response needs all item info and the rarity - resp.WriteBytes(dbUpdate.Data()) - resp.WriteUint8(items[ind].(*gachaItem).rarityIcon) - - usedItemHash = append(usedItemHash, int64(items[ind].(*gachaItem).itemhash)) - } - // remove rolled - items = append(items[:ind], items[ind+1:]...) - } - resp.Seek(0, 0) - resp.WriteUint8(results) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) - - // add claimables to DB - data[0] = data[0] + results - _, err = s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", data, s.charID) - if err != nil { - s.logger.Fatal("Failed to update gacha_items in db", zap.Error(err)) - } - // update lucky_box_state - _, err = s.server.db.Exec(`INSERT INTO lucky_box_state (char_id, shophash, used_itemhash) - VALUES ($1,$2,$3) ON CONFLICT (char_id, shophash) - DO UPDATE SET used_itemhash = COALESCE(lucky_box_state.used_itemhash::int[] || $3::int[], $3::int[]) - WHERE EXCLUDED.char_id=$1 AND EXCLUDED.shophash=$2`, s.charID, pkt.GachaHash, usedItemHash) - if err != nil { - s.logger.Fatal("Failed to update lucky box state in db", zap.Error(err)) - } - // deduct gacha coins if relevant, items are handled fine by the standard savedata packet immediately afterwards - if currType == 19 { - _, err = s.server.db.Exec(`UPDATE characters - SET gacha_trial = CASE WHEN (gacha_trial > $1) then gacha_trial - $1 else gacha_trial end, gacha_prem = CASE WHEN NOT (gacha_trial > $1) then gacha_prem - $1 else gacha_prem end - WHERE id=$2`, currNumber, s.charID) - } - if err != nil { - s.logger.Fatal("Failed to update gacha_trial in db", zap.Error(err)) - } -} - -func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfResetBoxGachaInfo) - _, err := s.server.db.Exec("DELETE FROM lucky_box_state WHERE shophash=$1 AND char_id=$2", pkt.GachaHash, s.charID) - if err != nil { - panic(err) - } - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) -} diff --git a/server/channelserver/handlers_tournament.go b/server/channelserver/handlers_tournament.go index 11b8645c3..84c2c8e8f 100644 --- a/server/channelserver/handlers_tournament.go +++ b/server/channelserver/handlers_tournament.go @@ -13,7 +13,7 @@ func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) { switch pkt.Unk0 { case 0: - bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Unix())) bf.WriteUint32(0) // Tied to schedule ID? case 1: diff --git a/server/channelserver/handlers_tower.go b/server/channelserver/handlers_tower.go index 0bafffaa2..0a2675812 100644 --- a/server/channelserver/handlers_tower.go +++ b/server/channelserver/handlers_tower.go @@ -58,6 +58,42 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } +func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) { + // if the game gets bad responses for this it breaks the ability to save + pkt := p.(*mhfpacket.MsgMhfGetTenrouirai) + var data []byte + var err error + if pkt.Unk0 == 1 { + data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010") + } else if pkt.Unk2 == 4 { + data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010") + } else { + data = []byte{0x00, 0x00, 0x00, 0x00} + s.logger.Info("GET_TENROUIRAI request for unknown type") + } + if err != nil { + panic(err) + } + doAckBufSucceed(s, pkt.AckHandle, data) +} + +func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostTenrouirai) + doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} + +func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {} + +func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + +func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPresentBox) + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) +} + func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGemInfo) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 2ebc6d080..5f3a46572 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -73,7 +73,7 @@ func NewSession(server *Server, conn net.Conn) *Session { cryptConn: network.NewCryptConn(conn), sendPackets: make(chan packet, 20), clientContext: &clientctx.ClientContext{}, // Unused - sessionStart: Time_Current_Adjusted().Unix(), + sessionStart: TimeAdjusted().Unix(), stageMoveStack: stringstack.New(), } return s diff --git a/server/channelserver/sys_time.go b/server/channelserver/sys_time.go new file mode 100644 index 000000000..396ae4cf5 --- /dev/null +++ b/server/channelserver/sys_time.go @@ -0,0 +1,25 @@ +package channelserver + +import ( + "time" +) + +func TimeAdjusted() time.Time { + baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60)) + return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location()) +} + +func TimeMidnight() time.Time { + baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60)) + return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location()) +} + +func TimeWeekStart() time.Time { + midnight := TimeMidnight() + offset := (int(midnight.Weekday()) - 1) * -24 + return midnight.Add(time.Hour * time.Duration(offset)) +} + +func TimeWeekNext() time.Time { + return TimeWeekStart().Add(time.Hour * 24 * 7) +} diff --git a/server/channelserver/sys_timefix.go b/server/channelserver/sys_timefix.go deleted file mode 100644 index 4cde6a319..000000000 --- a/server/channelserver/sys_timefix.go +++ /dev/null @@ -1,58 +0,0 @@ -package channelserver - -import ( - "fmt" - "time" -) - -var ( - Offset = 9 - YearAdjust = -7 - MonthAdjust = 0 - DayAdjust = 0 -) - -var ( - TimeStatic = time.Time{} -) - -func Time_Current() time.Time { - baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)) - return baseTime -} - -func Time_Current_Adjusted() time.Time { - baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust) - return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location()) -} - -func Time_Current_Midnight() time.Time { - baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust) - return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location()) -} - -func TimeWeekStart() time.Time { - midnight := Time_Current_Midnight() - offset := (int(midnight.Weekday()) - 1) * -24 - return midnight.Add(time.Hour * time.Duration(offset)) -} - -func TimeWeekNext() time.Time { - return TimeWeekStart().Add(time.Hour * 24 * 7) -} - -func Time_Current_Week_uint8() uint8 { - baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust) - - _, thisWeek := baseTime.ISOWeek() - _, beginningOfTheMonth := time.Date(baseTime.Year(), baseTime.Month(), 1, 0, 0, 0, 0, baseTime.Location()).ISOWeek() - - return uint8(1 + thisWeek - beginningOfTheMonth) -} - -func Time_static() time.Time { - if TimeStatic.IsZero() { - TimeStatic = Time_Current_Adjusted() - } - return TimeStatic -} diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go index 706aac8b3..7639c33da 100644 --- a/server/entranceserver/entrance_server.go +++ b/server/entranceserver/entrance_server.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net" + "strings" "sync" "erupe-ce/config" @@ -112,7 +113,11 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) { s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt))) - data := makeSv2Resp(s.erupeConfig, s) + local := false + if strings.Split(conn.RemoteAddr().String(), ":")[0] == "127.0.0.1" { + local = true + } + data := makeSv2Resp(s.erupeConfig, s, local) if len(pkt) > 5 { data = append(data, makeUsrResp(pkt, s)...) } diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 0df241260..d2004d938 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -17,7 +17,7 @@ var season uint8 // Server Channels var currentplayers uint16 -func encodeServerInfo(config *config.Config, s *Server) []byte { +func encodeServerInfo(config *config.Config, s *Server, local bool) []byte { serverInfos := config.Entrance.Entries bf := byteframe.NewByteFrame() @@ -30,7 +30,11 @@ func encodeServerInfo(config *config.Config, s *Server) []byte { if si.IP == "" { si.IP = config.Host } - bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) + if local { + bf.WriteUint32(0x0100007F) // 127.0.0.1 + } else { + bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) + } bf.WriteUint16(16 + uint16(serverIdx)) bf.WriteUint16(0x0000) bf.WriteUint16(uint16(len(si.Channels))) @@ -62,7 +66,7 @@ func encodeServerInfo(config *config.Config, s *Server) []byte { bf.WriteUint16(0x3039) } } - bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Unix())) + bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix())) bf.WriteUint32(0x0000003C) return bf.Data() } @@ -85,9 +89,9 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt return bf.Data() } -func makeSv2Resp(config *config.Config, s *Server) []byte { +func makeSv2Resp(config *config.Config, s *Server, local bool) []byte { serverInfos := config.Entrance.Entries - rawServerData := encodeServerInfo(config, s) + rawServerData := encodeServerInfo(config, s, local) bf := byteframe.NewByteFrame() bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00)) return bf.Data() diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 9335de0c0..d9fdbbba5 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -8,6 +8,7 @@ import ( "erupe-ce/server/channelserver" "fmt" "math/rand" + "strings" "time" "go.uber.org/zap" @@ -51,7 +52,11 @@ func (s *Session) makeSignInResp(uid int) []byte { ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false) } } - ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false) + if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" { + ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false) + } else { + ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false) + } lastPlayed := uint32(0) for _, char := range chars { @@ -122,12 +127,7 @@ func (s *Session) makeSignInResp(uid int) []byte { 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(uint32(returnExpiry.Unix())) bf.WriteUint32(0x00000000) bf.WriteUint32(0x0A5197DF) // unk id @@ -135,9 +135,9 @@ func (s *Session) makeSignInResp(uid int) []byte { alt := s.server.erupeConfig.DevModeOptions.MezFesAlt if mezfes { // Start time - bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) + bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) // End time - bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix())) + bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) bf.WriteUint8(2) // Unk bf.WriteUint32(20) // Single tickets bf.WriteUint32(10) // Group tickets