chunithm: Initial implementation code drop
No cleaner development history than this exists. It's difficult to methodically design something when you don't yet know what it is you're designing.
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
-- SQLite's ALTER TABLE statement is almost useless, and who knows how much of
|
||||
-- this stuff is present in older versions. So while ideally we'd mark a lot
|
||||
-- of these columns NOT NULL that might come back to bite us as we extend
|
||||
-- support downward from Chunithm 4 to Chunithm 1.
|
||||
|
||||
create table "cm_user_data" (
|
||||
"id" integer primary key not null,
|
||||
-- Could in theory do a shared primary key with aime_player, since this
|
||||
-- is going to be a 1:1 relation for the foreseeable future, but again
|
||||
-- the SQLite strait-jacket forces us to design defensively.
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"access_code" text,
|
||||
"user_name" text,
|
||||
"is_web_join" text,
|
||||
"web_limit_date" text,
|
||||
"level" integer,
|
||||
"reincarnation_num" integer,
|
||||
"exp" integer,
|
||||
"point" integer,
|
||||
"total_point" integer,
|
||||
"play_count" integer,
|
||||
"multi_play_count" integer,
|
||||
"multi_win_count" integer,
|
||||
"request_res_count" integer,
|
||||
"accept_res_count" integer,
|
||||
"success_res_count" integer,
|
||||
"player_rating" integer,
|
||||
"highest_rating" integer,
|
||||
"nameplate_id" integer,
|
||||
"frame_id" integer,
|
||||
"character_id" integer,
|
||||
"trophy_id" integer,
|
||||
"played_tutorial_bit" integer,
|
||||
"first_tutorial_cancel_num" integer,
|
||||
"master_tutorial_cancel_num" integer,
|
||||
"total_repertoire_count" integer,
|
||||
"total_map_num" integer,
|
||||
"total_hi_score" integer,
|
||||
"total_basic_high_score" integer,
|
||||
"total_advanced_high_score" integer,
|
||||
"total_expert_high_score" integer,
|
||||
"total_master_high_score" integer,
|
||||
"event_watched_date" text,
|
||||
"friend_count" integer,
|
||||
"is_maimai" text,
|
||||
"first_game_id" text,
|
||||
"first_rom_version" text,
|
||||
"first_data_version" text,
|
||||
"first_play_date" text,
|
||||
"last_game_id" text,
|
||||
"last_rom_version" text,
|
||||
"last_data_version" text,
|
||||
"last_play_date" text,
|
||||
"last_place_id" integer,
|
||||
"last_place_name" text,
|
||||
"last_region_id" text,
|
||||
"last_region_name" text,
|
||||
"last_all_net_id" text,
|
||||
"last_client_id" text
|
||||
);
|
||||
|
||||
create table "cm_user_activity" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"kind" integer not null,
|
||||
-- This is just called "id" on the wire but, well, we already have an "id"
|
||||
-- column up there ^
|
||||
"activity_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_activity_uq" unique (
|
||||
"profile_id",
|
||||
"kind",
|
||||
"activity_id"
|
||||
)
|
||||
);
|
||||
|
||||
create table "cm_user_data_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"compatible_cm_version" text not null,
|
||||
"medal" integer not null,
|
||||
"map_icon_id" integer not null,
|
||||
"voice_id" integer not null,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null,
|
||||
"ext_str1" text not null,
|
||||
"ext_str2" text not null,
|
||||
"ext_str3" text not null,
|
||||
"ext_str4" text not null,
|
||||
"ext_str5" text not null,
|
||||
"ext_long1" integer not null,
|
||||
"ext_long2" integer not null,
|
||||
"ext_long3" integer not null,
|
||||
"ext_long4" integer not null,
|
||||
"ext_long5" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_character" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"character_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"level" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"friendship_exp" integer not null,
|
||||
"is_valid" integer not null,
|
||||
"is_new_mark" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
constraint "cm_user_character_uq" unique ("profile_id", "character_id")
|
||||
);
|
||||
|
||||
create table "cm_user_game_option" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"bg_info" integer not null,
|
||||
"field_color" integer not null,
|
||||
"guide_sound" integer not null,
|
||||
"sound_effect" integer not null,
|
||||
"guide_line" integer not null,
|
||||
"speed" integer not null,
|
||||
"option_set" integer not null,
|
||||
"matching" integer not null,
|
||||
"judge_pos" integer not null,
|
||||
"rating" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"headphone" integer not null,
|
||||
"player_level" integer not null,
|
||||
"success_tap" integer not null,
|
||||
"success_ex_tap" integer not null,
|
||||
"success_slide_hold" integer not null,
|
||||
"success_air" integer not null,
|
||||
"success_flick" integer not null,
|
||||
"success_skill" integer not null,
|
||||
"success_tap_timbre" integer not null,
|
||||
"privacy" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_game_option_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_item" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"item_kind" integer not null,
|
||||
"item_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_item_uq" unique ("profile_id", "item_kind", "item_id")
|
||||
);
|
||||
|
||||
create table "cm_user_map" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"map_id" integer not null,
|
||||
"position" integer not null,
|
||||
"is_clear" text not null,
|
||||
"area_id" integer not null,
|
||||
"route_number" integer not null,
|
||||
"event_id" integer not null,
|
||||
"rate" integer not null,
|
||||
"status_count" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_map_uq" unique ("profile_id", "map_id")
|
||||
);
|
||||
|
||||
create table "cm_user_music" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"res_request_count" integer not null,
|
||||
"res_accept_count" integer not null,
|
||||
"res_success_count" integer not null,
|
||||
"miss_count" integer not null,
|
||||
"max_combo_count" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"full_chain" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"score_rank" integer not null,
|
||||
"is_lock" text not null,
|
||||
constraint "cm_user_music_uq" unique ("profile_id", "music_id", "level")
|
||||
);
|
||||
|
||||
create table "cm_user_playlog" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"order_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"place_id" integer not null,
|
||||
"play_date" text not null,
|
||||
"user_play_date" text not null,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"custom_id" integer not null,
|
||||
"played_user_id1" integer not null,
|
||||
"played_user_id2" integer not null,
|
||||
"played_user_id3" integer not null,
|
||||
"played_user_name1" text not null,
|
||||
"played_user_name2" text not null,
|
||||
"played_user_name3" text not null,
|
||||
"played_music_level1" integer not null,
|
||||
"played_music_level2" integer not null,
|
||||
"played_music_level3" integer not null,
|
||||
"played_custom1" integer not null,
|
||||
"played_custom2" integer not null,
|
||||
"played_custom3" integer not null,
|
||||
"track" integer not null,
|
||||
"score" integer not null,
|
||||
"rank" integer not null,
|
||||
"max_combo" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"rate_tap" integer not null,
|
||||
"rate_hold" integer not null,
|
||||
"rate_slide" integer not null,
|
||||
"rate_air" integer not null,
|
||||
"rate_flick" integer not null,
|
||||
"judge_guilty" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_critical" integer not null,
|
||||
"event_id" integer not null,
|
||||
"player_rating" integer not null,
|
||||
"is_new_record" text not null,
|
||||
"is_full_combo" text not null,
|
||||
"full_chain_kind" integer not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_continue" text not null,
|
||||
"is_free_to_play" text not null,
|
||||
"character_id" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"play_kind" integer not null,
|
||||
"is_clear" text not null,
|
||||
"skill_level" integer not null,
|
||||
"skill_effect" integer not null,
|
||||
"place_name" text not null,
|
||||
"is_maimai" text not null
|
||||
);
|
||||
@@ -0,0 +1,304 @@
|
||||
-- SQLite's ALTER TABLE statement is almost useless, and who knows how much of
|
||||
-- this stuff is present in older versions. So while ideally we'd mark a lot
|
||||
-- of these columns NOT NULL that might come back to bite us as we extend
|
||||
-- support downward from Chunithm 4 to Chunithm 1.
|
||||
|
||||
create table "cm_user_data" (
|
||||
"id" integer primary key not null,
|
||||
-- Could in theory do a shared primary key with aime_player, since this
|
||||
-- is going to be a 1:1 relation for the foreseeable future, but again
|
||||
-- the SQLite strait-jacket forces us to design defensively.
|
||||
"player_id" integer not null
|
||||
references "aime_player"("id")
|
||||
on delete cascade,
|
||||
"access_code" text,
|
||||
"user_name" text,
|
||||
"is_web_join" text,
|
||||
"web_limit_date" text,
|
||||
"level" integer,
|
||||
"reincarnation_num" integer,
|
||||
"exp" integer,
|
||||
"point" integer,
|
||||
"total_point" integer,
|
||||
"play_count" integer,
|
||||
"multi_play_count" integer,
|
||||
"multi_win_count" integer,
|
||||
"request_res_count" integer,
|
||||
"accept_res_count" integer,
|
||||
"success_res_count" integer,
|
||||
"player_rating" integer,
|
||||
"highest_rating" integer,
|
||||
"nameplate_id" integer,
|
||||
"frame_id" integer,
|
||||
"character_id" integer,
|
||||
"trophy_id" integer,
|
||||
"played_tutorial_bit" integer,
|
||||
"first_tutorial_cancel_num" integer,
|
||||
"master_tutorial_cancel_num" integer,
|
||||
"total_repertoire_count" integer,
|
||||
"total_map_num" integer,
|
||||
"total_hi_score" integer,
|
||||
"total_basic_high_score" integer,
|
||||
"total_advanced_high_score" integer,
|
||||
"total_expert_high_score" integer,
|
||||
"total_master_high_score" integer,
|
||||
"event_watched_date" text,
|
||||
"friend_count" integer,
|
||||
"is_maimai" text,
|
||||
"first_game_id" text,
|
||||
"first_rom_version" text,
|
||||
"first_data_version" text,
|
||||
"first_play_date" text,
|
||||
"last_game_id" text,
|
||||
"last_rom_version" text,
|
||||
"last_data_version" text,
|
||||
"last_play_date" text,
|
||||
"last_place_id" integer,
|
||||
"last_place_name" text,
|
||||
"last_region_id" text,
|
||||
"last_region_name" text,
|
||||
"last_all_net_id" text,
|
||||
"last_client_id" text
|
||||
);
|
||||
|
||||
create table "cm_user_activity" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"kind" integer not null,
|
||||
-- This is just called "id" on the wire but, well, we already have an "id"
|
||||
-- column up there ^
|
||||
"activity_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_activity_uq" unique (
|
||||
"profile_id",
|
||||
"kind",
|
||||
"activity_id"
|
||||
)
|
||||
);
|
||||
|
||||
create table "cm_user_data_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"compatible_cm_version" text not null,
|
||||
"medal" integer not null,
|
||||
"map_icon_id" integer not null,
|
||||
"voice_id" integer not null,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null,
|
||||
"ext_str1" text not null,
|
||||
"ext_str2" text not null,
|
||||
"ext_str3" text not null,
|
||||
"ext_str4" text not null,
|
||||
"ext_str5" text not null,
|
||||
"ext_long1" integer not null,
|
||||
"ext_long2" integer not null,
|
||||
"ext_long3" integer not null,
|
||||
"ext_long4" integer not null,
|
||||
"ext_long5" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_character" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"character_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"level" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"friendship_exp" integer not null,
|
||||
"is_valid" integer not null,
|
||||
"is_new_mark" integer not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
constraint "cm_user_character_uq" unique ("profile_id", "character_id")
|
||||
);
|
||||
|
||||
create table "cm_user_game_option" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"bg_info" integer not null,
|
||||
"field_color" integer not null,
|
||||
"guide_sound" integer not null,
|
||||
"sound_effect" integer not null,
|
||||
"guide_line" integer not null,
|
||||
"speed" integer not null,
|
||||
"option_set" integer not null,
|
||||
"matching" integer not null,
|
||||
"judge_pos" integer not null,
|
||||
"rating" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"headphone" integer not null,
|
||||
"player_level" integer not null,
|
||||
"success_tap" integer not null,
|
||||
"success_ex_tap" integer not null,
|
||||
"success_slide_hold" integer not null,
|
||||
"success_air" integer not null,
|
||||
"success_flick" integer not null,
|
||||
"success_skill" integer not null,
|
||||
"success_tap_timbre" integer not null,
|
||||
"privacy" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_game_option_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"ext1" integer not null,
|
||||
"ext2" integer not null,
|
||||
"ext3" integer not null,
|
||||
"ext4" integer not null,
|
||||
"ext5" integer not null,
|
||||
"ext6" integer not null,
|
||||
"ext7" integer not null,
|
||||
"ext8" integer not null,
|
||||
"ext9" integer not null,
|
||||
"ext10" integer not null,
|
||||
"ext11" integer not null,
|
||||
"ext12" integer not null,
|
||||
"ext13" integer not null,
|
||||
"ext14" integer not null,
|
||||
"ext15" integer not null,
|
||||
"ext16" integer not null,
|
||||
"ext17" integer not null,
|
||||
"ext18" integer not null,
|
||||
"ext19" integer not null,
|
||||
"ext20" integer not null
|
||||
);
|
||||
|
||||
create table "cm_user_item" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"item_kind" integer not null,
|
||||
"item_id" integer not null,
|
||||
"stock" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_item_uq" unique ("profile_id", "item_kind", "item_id")
|
||||
);
|
||||
|
||||
create table "cm_user_map" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"map_id" integer not null,
|
||||
"position" integer not null,
|
||||
"is_clear" text not null,
|
||||
"area_id" integer not null,
|
||||
"route_number" integer not null,
|
||||
"event_id" integer not null,
|
||||
"rate" integer not null,
|
||||
"status_count" integer not null,
|
||||
"is_valid" text not null,
|
||||
constraint "cm_user_map_uq" unique ("profile_id", "map_id")
|
||||
);
|
||||
|
||||
create table "cm_user_music" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"res_request_count" integer not null,
|
||||
"res_accept_count" integer not null,
|
||||
"res_success_count" integer not null,
|
||||
"miss_count" integer not null,
|
||||
"max_combo_count" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"full_chain" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"score_rank" integer not null,
|
||||
"is_lock" text not null,
|
||||
constraint "cm_user_music_uq" unique ("profile_id", "music_id", "level")
|
||||
);
|
||||
|
||||
create table "cm_user_playlog" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"order_id" integer not null,
|
||||
"sort_number" integer not null,
|
||||
"place_id" integer not null,
|
||||
"play_date" text not null,
|
||||
"user_play_date" text not null,
|
||||
"music_id" integer not null,
|
||||
"level" integer not null,
|
||||
"custom_id" integer not null,
|
||||
"played_user_id1" integer not null,
|
||||
"played_user_id2" integer not null,
|
||||
"played_user_id3" integer not null,
|
||||
"played_user_name1" text not null,
|
||||
"played_user_name2" text not null,
|
||||
"played_user_name3" text not null,
|
||||
"played_music_level1" integer not null,
|
||||
"played_music_level2" integer not null,
|
||||
"played_music_level3" integer not null,
|
||||
"played_custom1" integer not null,
|
||||
"played_custom2" integer not null,
|
||||
"played_custom3" integer not null,
|
||||
"track" integer not null,
|
||||
"score" integer not null,
|
||||
"rank" integer not null,
|
||||
"max_combo" integer not null,
|
||||
"max_chain" integer not null,
|
||||
"rate_tap" integer not null,
|
||||
"rate_hold" integer not null,
|
||||
"rate_slide" integer not null,
|
||||
"rate_air" integer not null,
|
||||
"rate_flick" integer not null,
|
||||
"judge_guilty" integer not null,
|
||||
"judge_attack" integer not null,
|
||||
"judge_justice" integer not null,
|
||||
"judge_critical" integer not null,
|
||||
"event_id" integer not null,
|
||||
"player_rating" integer not null,
|
||||
"is_new_record" text not null,
|
||||
"is_full_combo" text not null,
|
||||
"full_chain_kind" integer not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_continue" text not null,
|
||||
"is_free_to_play" text not null,
|
||||
"character_id" integer not null,
|
||||
"skill_id" integer not null,
|
||||
"play_kind" integer not null,
|
||||
"is_clear" text not null,
|
||||
"skill_level" integer not null,
|
||||
"skill_effect" integer not null,
|
||||
"place_name" text not null,
|
||||
"is_maimai" text not null
|
||||
);
|
||||
@@ -9,6 +9,7 @@ const scripts = [
|
||||
// Order matters! Later scripts reference schema instantiated earlier.
|
||||
"meta.sql",
|
||||
"aime.sql",
|
||||
"cm.sql",
|
||||
"idz.sql",
|
||||
];
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameCharge(req: Request, res: Response) {
|
||||
res.json({
|
||||
length: 0,
|
||||
gameChargeList: [
|
||||
/*
|
||||
{
|
||||
orderId: 1,
|
||||
chargeId: 1,
|
||||
price: 1,
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
salePrice:
|
||||
saleStartDate: 'STRINGIDK',
|
||||
saleEndDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameEvent(req: Request, res: Response) {
|
||||
res.json({
|
||||
type: 1,
|
||||
length: 0,
|
||||
gameEventList: [
|
||||
/*
|
||||
{
|
||||
type: 1,
|
||||
id: 1102, // data/A000/event/event00001102
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameIdlist(req: Request, res: Response) {
|
||||
const { type } = req.body;
|
||||
|
||||
res.json({
|
||||
type,
|
||||
length: 0,
|
||||
gameIdlistList: [],
|
||||
});
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameMessage(req: Request, res: Response) {
|
||||
res.json({
|
||||
type: 1,
|
||||
length: 0,
|
||||
gameMessageList: [
|
||||
/* {
|
||||
type: 2,
|
||||
id: 1,
|
||||
message: "true",
|
||||
startDate: "0",
|
||||
endDate: "0"
|
||||
},*/
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameRanking(req: Request, res: Response) {
|
||||
const { type } = req.body;
|
||||
|
||||
res.json({
|
||||
type,
|
||||
gameRankingList: [
|
||||
/*
|
||||
// QWORD fields maybe?
|
||||
{
|
||||
id: 1,
|
||||
point: 1,
|
||||
}
|
||||
*/
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameSale(req: Request, res: Response) {
|
||||
const { type } = req.body;
|
||||
|
||||
res.json({
|
||||
type,
|
||||
length: 0,
|
||||
gameSaleList: [
|
||||
/*
|
||||
{
|
||||
orderId: 1234,
|
||||
type,
|
||||
id: 4321,
|
||||
rate: 5678,
|
||||
startDate: 'STRINGIDK',
|
||||
endDate: 'STRINGIDK',
|
||||
},
|
||||
*/
|
||||
],
|
||||
});
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Request, Response } from "express";
|
||||
|
||||
export default function getGameSetting(req: Request, res: Response) {
|
||||
res.json({
|
||||
gameSetting: {
|
||||
dataVersion: 1,
|
||||
isMaintenance: false,
|
||||
requestInterval: 10,
|
||||
rebootStartTime: 0,
|
||||
rebootEndTime: 0,
|
||||
isBackgroundDistribute: false,
|
||||
maxCountCharacter: 999,
|
||||
maxCountItem: 999,
|
||||
maxCountMusic: 999,
|
||||
},
|
||||
isDumpUpload: false,
|
||||
isAou: false,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Make the names explicit so that we don't get the order mixed up
|
||||
interface Params {
|
||||
maxCount: number;
|
||||
nextIndex: number;
|
||||
}
|
||||
|
||||
export function paginationCookie<T>(items: T[], params: Params) {
|
||||
if (items.length < params.maxCount) {
|
||||
return "-1";
|
||||
} else {
|
||||
return (params.nextIndex + params.maxCount).toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GameLoginRequest } from "../request/gameLogin";
|
||||
import { GameLoginResponse } from "../response/gameLogin";
|
||||
|
||||
export default async function gameLogin(
|
||||
rep: Repositories,
|
||||
req: GameLoginRequest
|
||||
): Promise<GameLoginResponse> {
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GameLogoutRequest } from "../request/gameLogout";
|
||||
import { GameLogoutResponse } from "../response/gameLogout";
|
||||
|
||||
export default async function gameLogout(
|
||||
rep: Repositories,
|
||||
req: GameLogoutRequest
|
||||
): Promise<GameLogoutResponse> {
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameChargeRequest } from "../request/getGameCharge";
|
||||
import { GetGameChargeResponse } from "../response/getGameCharge";
|
||||
|
||||
export default async function getGameCharge(
|
||||
rep: Repositories,
|
||||
req: GetGameChargeRequest
|
||||
): Promise<GetGameChargeResponse> {
|
||||
return {
|
||||
length: "0",
|
||||
gameChargeList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameEventRequest } from "../request/getGameEvent";
|
||||
import { GetGameEventResponse } from "../response/getGameEvent";
|
||||
|
||||
export default async function getGameEvent(
|
||||
rep: Repositories,
|
||||
req: GetGameEventRequest
|
||||
): Promise<GetGameEventResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameEventList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameIdlistRequest } from "../request/getGameIdlist";
|
||||
import { GetGameIdlistResponse } from "../response/getGameIdlist";
|
||||
|
||||
export default async function getGameIdlist(
|
||||
rep: Repositories,
|
||||
req: GetGameIdlistRequest
|
||||
): Promise<GetGameIdlistResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameIdlistList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameMessageRequest } from "../request/getGameMessage";
|
||||
import { GetGameMessageResponse } from "../response/getGameMessage";
|
||||
|
||||
export default async function getGameMessage(
|
||||
rep: Repositories,
|
||||
req: GetGameMessageRequest
|
||||
): Promise<GetGameMessageResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameMessageList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameRankingRequest } from "../request/getGameRanking";
|
||||
import { GetGameRankingResponse } from "../response/getGameRanking";
|
||||
|
||||
export default async function getGameRanking(
|
||||
rep: Repositories,
|
||||
req: GetGameRankingRequest
|
||||
): Promise<GetGameRankingResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
gameRankingList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameSaleRequest } from "../request/getGameSale";
|
||||
import { GetGameSaleResponse } from "../response/getGameSale";
|
||||
|
||||
export default async function getGameSale(
|
||||
rep: Repositories,
|
||||
req: GetGameSaleRequest
|
||||
): Promise<GetGameSaleResponse> {
|
||||
return {
|
||||
type: req.type,
|
||||
length: "0",
|
||||
gameSaleList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetGameSettingRequest } from "../request/getGameSetting";
|
||||
import { GetGameSettingResponse } from "../response/getGameSetting";
|
||||
|
||||
export default async function getGameSetting(
|
||||
rep: Repositories,
|
||||
req: GetGameSettingRequest
|
||||
): Promise<GetGameSettingResponse> {
|
||||
return {
|
||||
gameSetting: {
|
||||
dataVersion: "1",
|
||||
isMaintenance: "false",
|
||||
requestInterval: "10",
|
||||
rebootStartTime: "0",
|
||||
rebootEndTime: "0",
|
||||
isBackgroundDistribute: "false",
|
||||
maxCountCharacter: "999",
|
||||
maxCountItem: "999",
|
||||
maxCountMusic: "999",
|
||||
},
|
||||
isDumpUpload: "false",
|
||||
isAou: "false",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserActivity } from "../proto/userActivity";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserActivityRequest } from "../request/getUserActivity";
|
||||
import { GetUserActivityResponse } from "../response/getUserActivity";
|
||||
|
||||
export default async function getUserActivity(
|
||||
rep: Repositories,
|
||||
req: GetUserActivityRequest
|
||||
): Promise<GetUserActivityResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const kind = parseInt(req.kind);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userActivity().load(profileId, kind);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
kind: kind.toString(),
|
||||
userActivityList: items.map(writeUserActivity),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { paginationCookie } from "./_util";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserCharacter } from "../proto/userCharacter";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserCharacterRequest } from "../request/getUserCharacter";
|
||||
import { GetUserCharacterResponse } from "../response/getUserCharacter";
|
||||
|
||||
export default async function getUserCharacter(
|
||||
rep: Repositories,
|
||||
req: GetUserCharacterRequest
|
||||
): Promise<GetUserCharacterResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep
|
||||
.userCharacter()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||
userCharacterList: items.map(writeUserCharacter),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserChargeRequest } from "../request/getUserCharge";
|
||||
import { GetUserChargeResponse } from "../response/getUserCharge";
|
||||
|
||||
export default async function getUserCharge(
|
||||
rep: Repositories,
|
||||
req: GetUserChargeRequest
|
||||
): Promise<GetUserChargeResponse> {
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userChargeList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserCourseRequest } from "../request/getUserCourse";
|
||||
import { GetUserCourseResponse } from "../response/getUserCourse";
|
||||
|
||||
export default async function getUserCourse(
|
||||
rep: Repositories,
|
||||
req: GetUserCourseRequest
|
||||
): Promise<GetUserCourseResponse> {
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
nextIndex: "-1",
|
||||
userCourseList: [
|
||||
// This will get saved at some point I think
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserData } from "../proto/userData";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDataRequest } from "../request/getUserData";
|
||||
import { GetUserDataResponse } from "../response/getUserData";
|
||||
|
||||
export default async function getUserData(
|
||||
rep: Repositories,
|
||||
req: GetUserDataRequest
|
||||
): Promise<GetUserDataResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userData = await rep.userData().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userData: writeUserData(userData),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserDataEx } from "../proto/userDataEx";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDataExRequest } from "../request/getUserDataEx";
|
||||
import { GetUserDataExResponse } from "../response/getUserDataEx";
|
||||
|
||||
export default async function getUserDataEx(
|
||||
rep: Repositories,
|
||||
req: GetUserDataExRequest
|
||||
): Promise<GetUserDataExResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userDataEx = await rep.userDataEx().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userDataEx: writeUserDataEx(userDataEx),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDuelRequest } from "../request/getUserDuel";
|
||||
import { GetUserDuelResponse } from "../response/getUserDuel";
|
||||
|
||||
export default async function getUserDuel(
|
||||
rep: Repositories,
|
||||
req: GetUserDuelRequest
|
||||
): Promise<GetUserDuelResponse> {
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userDuelList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserItem } from "../proto/userItem";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserItemRequest } from "../request/getUserItem";
|
||||
import { GetUserItemResponse } from "../response/getUserItem";
|
||||
import { UserItemItem } from "../model/userItem";
|
||||
|
||||
// I'm not sure why they went to all this trouble just to avoid passing an
|
||||
// explicit `itemKind` parameter in the request...
|
||||
|
||||
const itemKindMul = 10000000000n;
|
||||
|
||||
export default async function getUserItem(
|
||||
rep: Repositories,
|
||||
req: GetUserItemRequest
|
||||
): Promise<GetUserItemResponse> {
|
||||
// Split the "nextIndex" parameter
|
||||
|
||||
const compoundIn = BigInt(req.nextIndex);
|
||||
const itemKindN = compoundIn / itemKindMul;
|
||||
const nextIndexN = compoundIn % itemKindMul;
|
||||
|
||||
// Now unpack the entire request
|
||||
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const itemKind = parseInt(itemKindN as any);
|
||||
const nextIndex = parseInt(nextIndexN as any);
|
||||
|
||||
// Hit DB
|
||||
// This gets called for not-yet-created profiles for some reason (probably
|
||||
// so that the server can force some bonus items into a new profile's
|
||||
// inventory)? So we need to gracefully handle that condition.
|
||||
|
||||
// (maybe some sort of anchor row is created on first GameLogin...)
|
||||
|
||||
const profileId = await rep.userData().tryLookup(aimeId);
|
||||
let items: UserItemItem[];
|
||||
|
||||
if (profileId !== undefined) {
|
||||
items = await rep
|
||||
.userItem()
|
||||
.load(profileId, itemKind, { limit: maxCount, offset: nextIndex });
|
||||
} else {
|
||||
items = [];
|
||||
}
|
||||
|
||||
// Pack the next pagination cookie into bigint compound form
|
||||
|
||||
const compoundOut =
|
||||
itemKindN * itemKindMul + BigInt(maxCount + items.length);
|
||||
|
||||
// Done:
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
nextIndex: items.length < maxCount ? "-1" : compoundOut.toString(),
|
||||
itemKind: itemKind.toString(),
|
||||
userItemList: items.map(writeUserItem),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserMap } from "../proto/userMap";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserMapRequest } from "../request/getUserMap";
|
||||
import { GetUserMapResponse } from "../response/getUserMap";
|
||||
|
||||
export default async function getUserMap(
|
||||
rep: Repositories,
|
||||
req: GetUserMapRequest
|
||||
): Promise<GetUserMapResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userMap().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: items.length.toString(),
|
||||
userMapList: items.map(writeUserMap),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { writeUserMusicDetail, UserMusicJson } from "../proto/userMusic";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserMusicRequest } from "../request/getUserMusic";
|
||||
import { GetUserMusicResponse } from "../response/getUserMusic";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { UserMusicDetailItem } from "../model/userMusic";
|
||||
import { paginationCookie } from "./_util";
|
||||
|
||||
export default async function getUserMusic(
|
||||
rep: Repositories,
|
||||
req: GetUserMusicRequest
|
||||
): Promise<GetUserMusicResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const flat = await rep
|
||||
.userMusic()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
const groupIdx = new Map<number, UserMusicDetailItem[]>();
|
||||
|
||||
for (const item of flat) {
|
||||
const { musicId } = item;
|
||||
const existing = groupIdx.get(musicId);
|
||||
|
||||
if (existing !== undefined) {
|
||||
existing.push(item);
|
||||
} else {
|
||||
groupIdx.set(musicId, [item]);
|
||||
}
|
||||
}
|
||||
|
||||
const userMusicList = new Array<UserMusicJson>();
|
||||
|
||||
for (const group of groupIdx.values()) {
|
||||
userMusicList.push({
|
||||
length: group.length.toString(),
|
||||
userMusicDetailList: group.map(writeUserMusicDetail),
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: userMusicList.length.toString(),
|
||||
nextIndex: paginationCookie(flat, { maxCount, nextIndex }),
|
||||
userMusicList,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserGameOption } from "../proto/userGameOption";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserOptionRequest } from "../request/getUserOption";
|
||||
import { GetUserOptionResponse } from "../response/getUserOption";
|
||||
|
||||
export default async function getUserOption(
|
||||
rep: Repositories,
|
||||
req: GetUserOptionRequest
|
||||
): Promise<GetUserOptionResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userGameOption = await rep.userGameOption().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userGameOption: writeUserGameOption(userGameOption),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserGameOptionEx } from "../proto/userGameOptionEx";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserOptionExRequest } from "../request/getUserOptionEx";
|
||||
import { GetUserOptionExResponse } from "../response/getUserOptionEx";
|
||||
|
||||
export default async function getUserOptionEx(
|
||||
rep: Repositories,
|
||||
req: GetUserOptionExRequest
|
||||
): Promise<GetUserOptionExResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const userGameOptionEx = await rep.userGameOptionEx().load(profileId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
userGameOptionEx: writeUserGameOptionEx(userGameOptionEx),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { readAimeId, writeDate } from "../proto/base";
|
||||
import { writeUserCharacter } from "../proto/userCharacter";
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserPreviewRequest } from "../request/getUserPreview";
|
||||
import { GetUserPreviewResponse } from "../response/getUserPreview";
|
||||
|
||||
export default async function getUserPreview(
|
||||
rep: Repositories,
|
||||
req: GetUserPreviewRequest
|
||||
): Promise<GetUserPreviewResponse | null> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const profileId = await rep.userData().tryLookup(aimeId);
|
||||
|
||||
// Return an empty JSON object if the player lacks a profile; this will
|
||||
// start the new profile registration flow in the client.
|
||||
|
||||
if (profileId === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userData = await rep.userData().load(profileId);
|
||||
const userCharacter = await rep
|
||||
.userCharacter()
|
||||
.loadOne(profileId, userData.characterId);
|
||||
const userGameOption = await rep.userGameOption().load(profileId);
|
||||
|
||||
return {
|
||||
userId: "1",
|
||||
|
||||
// Current login (i.e. profile lock) status
|
||||
|
||||
isLogin: "false",
|
||||
lastLoginDate: "1970-01-01 09:00:00",
|
||||
|
||||
// UserData:
|
||||
|
||||
userName: userData.userName,
|
||||
reincarnationNum: userData.reincarnationNum.toString(),
|
||||
level: userData.level.toString(),
|
||||
exp: userData.exp.toString(),
|
||||
playerRating: userData.playerRating.toString(),
|
||||
lastGameId: userData.lastGameId,
|
||||
lastRomVersion: userData.lastRomVersion,
|
||||
lastDataVersion: userData.lastDataVersion,
|
||||
lastPlayDate: writeDate(userData.lastPlayDate),
|
||||
trophyId: userData.trophyId.toString(),
|
||||
|
||||
// Currently selected UserCharacter:
|
||||
|
||||
userCharacter: writeUserCharacter(userCharacter),
|
||||
|
||||
// UserGameOption:
|
||||
|
||||
playerLevel: userGameOption.playerLevel.toString(),
|
||||
rating: userGameOption.rating.toString(),
|
||||
headphone: userGameOption.headphone.toString(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserRecentRatingRequest } from "../request/getUserRecentRating";
|
||||
import { GetUserRecentRatingResponse } from "../response/getUserRecentRating";
|
||||
|
||||
export default async function getUserRecentRating(
|
||||
rep: Repositories,
|
||||
req: GetUserRecentRatingRequest
|
||||
): Promise<GetUserRecentRatingResponse> {
|
||||
// I don't really know what subset of data to return here.
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userRecentRatingList: [],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserRegionRequest } from "../request/getUserRegion";
|
||||
import { GetUserRegionResponse } from "../response/getUserRegion";
|
||||
|
||||
export default async function getUserRegion(
|
||||
rep: Repositories,
|
||||
req: GetUserRegionRequest
|
||||
): Promise<GetUserRegionResponse> {
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userRegionList: [
|
||||
// Doesn't get saved
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import compression from "compression";
|
||||
import express, { NextFunction, Request, Response } from "express";
|
||||
import logger from "debug";
|
||||
|
||||
import gameLogin from "./gameLogin";
|
||||
import gameLogout from "./gameLogout";
|
||||
import getGameCharge from "./getGameCharge";
|
||||
import getGameEvent from "./getGameEvent";
|
||||
import getGameIdlist from "./getGameIdlist";
|
||||
import getGameMessage from "./getGameMessage";
|
||||
import getGameRanking from "./getGameRanking";
|
||||
import getGameSale from "./getGameSale";
|
||||
import getGameSetting from "./getGameSetting";
|
||||
import getUserActivity from "./getUserActivity";
|
||||
import getUserCharacter from "./getUserCharacter";
|
||||
import getUserCharge from "./getUserCharge";
|
||||
import getUserCourse from "./getUserCourse";
|
||||
import getUserData from "./getUserData";
|
||||
import getUserDataEx from "./getUserDataEx";
|
||||
import getUserDuel from "./getUserDuel";
|
||||
import getUserItem from "./getUserItem";
|
||||
import getUserMap from "./getUserMap";
|
||||
import getUserMusic from "./getUserMusic";
|
||||
import getUserOption from "./getUserOption";
|
||||
import getUserOptionEx from "./getUserOptionEx";
|
||||
import getUserPreview from "./getUserPreview";
|
||||
import getUserRecentRating from "./getUserRecentRating";
|
||||
import getUserRegion from "./getUserRegion";
|
||||
import upsertClientBookkeeping from "./upsertClientBookkeeping";
|
||||
import upsertClientDevelop from "./upsertClientDevelop";
|
||||
import upsertClientError from "./upsertClientError";
|
||||
import upsertClientSetting from "./upsertClientSetting";
|
||||
import upsertClientTestmode from "./upsertClientTestmode";
|
||||
import upsertUserAll from "./upsertUserAll";
|
||||
import createSqlWrapper from "../sql";
|
||||
import { DataSource } from "../../sql";
|
||||
|
||||
const debug = logger("app:chuni:io");
|
||||
|
||||
// Thankfully we can use standard middleware for JSON I/O. We have to use a
|
||||
// zlib middleware as well because compression is non-optional: the client will
|
||||
// attempt to inflate whatever response we send to it whether there's a
|
||||
// Transfer-Encoding header or not.
|
||||
|
||||
function quirks(req: Request, res: Response, next: NextFunction) {
|
||||
// Chunithm client does not follow the HTTP spec: user agents must indicate
|
||||
// that they are willing to accept a particular Transfer-Encoding for that
|
||||
// T-E to be used (unless the T-E in question is "chunked" but w/e), and this
|
||||
// indication is advisory anyway, the server is permitted to ignore it.
|
||||
//
|
||||
// Force this header into the request, since it is not present normally.
|
||||
|
||||
// ... unless, that is, the client is actually somebody debugging with curl
|
||||
// and not the real game client, in which case this forced deflate can be
|
||||
// annoying. Pass an "X-Debug" request header to suppress forced compression.
|
||||
|
||||
if (req.headers["x-debug"] === undefined) {
|
||||
req.headers["accept-encoding"] = "deflate";
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
function trace(req: Request, res: Response, next: NextFunction) {
|
||||
debug("\n--- Chunithm %s ---\n", req.url);
|
||||
debug("Request: %j", req.body);
|
||||
|
||||
const prevJson = res.json;
|
||||
|
||||
res.json = function(obj) {
|
||||
debug("Response: %j", obj);
|
||||
|
||||
res.json = prevJson;
|
||||
res.json.apply(this, arguments);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
export default function chunithm(db: DataSource) {
|
||||
const wrapper = createSqlWrapper(db);
|
||||
|
||||
wrapper.rpc("/GameLoginApi", gameLogin);
|
||||
wrapper.rpc("/GameLogoutApi", gameLogout);
|
||||
wrapper.rpc("/GetGameChargeApi", getGameCharge);
|
||||
wrapper.rpc("/GetGameEventApi", getGameEvent);
|
||||
wrapper.rpc("/GetGameIdlistApi", getGameIdlist);
|
||||
wrapper.rpc("/GetGameMessageApi", getGameMessage);
|
||||
wrapper.rpc("/GetGameRankingApi", getGameRanking);
|
||||
wrapper.rpc("/GetGameSaleApi", getGameSale);
|
||||
wrapper.rpc("/GetGameSettingApi", getGameSetting);
|
||||
wrapper.rpc("/GetUserActivityApi", getUserActivity);
|
||||
wrapper.rpc("/GetUserCharacterApi", getUserCharacter);
|
||||
wrapper.rpc("/GetUserChargeApi", getUserCharge);
|
||||
wrapper.rpc("/GetUserCourseAPi", getUserCourse);
|
||||
wrapper.rpc("/GetUserDataApi", getUserData);
|
||||
wrapper.rpc("/GetUserDataExApi", getUserDataEx);
|
||||
wrapper.rpc("/GetUserDuelApi", getUserDuel);
|
||||
wrapper.rpc("/GetUserItemApi", getUserItem);
|
||||
wrapper.rpc("/GetUserMapApi", getUserMap);
|
||||
wrapper.rpc("/GetUserMusicApi", getUserMusic);
|
||||
wrapper.rpc("/GetUserOptionApi", getUserOption);
|
||||
wrapper.rpc("/GetUserOptionExApi", getUserOptionEx);
|
||||
wrapper.rpc("/GetUserPreviewApi", getUserPreview);
|
||||
wrapper.rpc("/GetUserRecentRatingApi", getUserRecentRating);
|
||||
wrapper.rpc("/GetUserRegionApi", getUserRegion);
|
||||
wrapper.rpc("/UpsertClientBookkeepingApi", upsertClientBookkeeping);
|
||||
wrapper.rpc("/UpsertClientDevelopApi", upsertClientDevelop);
|
||||
wrapper.rpc("/UpsertClientErrorApi", upsertClientError);
|
||||
wrapper.rpc("/UpsertClientSettingApi", upsertClientSetting);
|
||||
wrapper.rpc("/UpsertClientTestmodeApi", upsertClientTestmode);
|
||||
wrapper.rpc("/UpsertUserAllApi", upsertUserAll);
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(quirks);
|
||||
app.use(compression({ threshold: 0 }));
|
||||
app.use(express.json());
|
||||
app.use(trace);
|
||||
|
||||
app.use("/ChuniServlet", wrapper);
|
||||
|
||||
return app;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientBookkeepingRequest } from "../request/upsertClientBookkeeping";
|
||||
import { UpsertClientBookkeepingResponse } from "../response/upsertClientBookkeeping";
|
||||
|
||||
export default async function upsertClientBookkeeping(
|
||||
rep: Repositories,
|
||||
req: UpsertClientBookkeepingRequest
|
||||
): Promise<UpsertClientBookkeepingResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientDevelopRequest } from "../request/upsertClientDevelop";
|
||||
import { UpsertClientDevelopResponse } from "../response/upsertClientDevelop";
|
||||
|
||||
export default async function upsertClientDevelop(
|
||||
rep: Repositories,
|
||||
req: UpsertClientDevelopRequest
|
||||
): Promise<UpsertClientDevelopResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientErrorRequest } from "../request/upsertClientError";
|
||||
import { UpsertClientErrorResponse } from "../response/upsertClientError";
|
||||
|
||||
export default async function upsertClientError(
|
||||
rep: Repositories,
|
||||
req: UpsertClientErrorRequest
|
||||
): Promise<UpsertClientErrorResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientSettingRequest } from "../request/upsertClientSetting";
|
||||
import { UpsertClientSettingResponse } from "../response/upsertClientSetting";
|
||||
|
||||
export default async function upsertClientSetting(
|
||||
rep: Repositories,
|
||||
req: UpsertClientSettingRequest
|
||||
): Promise<UpsertClientSettingResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { UpsertClientTestmodeRequest } from "../request/upsertClientTestmode";
|
||||
import { UpsertClientTestmodeResponse } from "../response/upsertClientTestmode";
|
||||
|
||||
export default async function upsertClientTestmode(
|
||||
rep: Repositories,
|
||||
req: UpsertClientTestmodeRequest
|
||||
): Promise<UpsertClientTestmodeResponse> {
|
||||
return {
|
||||
returnCode: "1",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { readUserCharacter } from "../proto/userCharacter";
|
||||
import { readUserData } from "../proto/userData";
|
||||
import { readUserGameOption } from "../proto/userGameOption";
|
||||
import { UpsertUserAllRequest } from "../request/upsertUserAll";
|
||||
import { UpsertUserAllResponse } from "../response/upsertUserAll";
|
||||
import { readUserGameOptionEx } from "../proto/userGameOptionEx";
|
||||
import { readUserMap } from "../proto/userMap";
|
||||
import { readUserItem } from "../proto/userItem";
|
||||
import { readUserMusicDetail } from "../proto/userMusic";
|
||||
import { readUserActivity } from "../proto/userActivity";
|
||||
import { readUserDataEx } from "../proto/userDataEx";
|
||||
import { readUserPlaylog } from "../proto/userPlaylog";
|
||||
|
||||
// It shouldn't need to be said really, but seeing as this message (A) requires
|
||||
// fairly lengthy processing and (B) is only sent right at the end of a credit,
|
||||
// you should probably copy the JSON from the server's debug log and then
|
||||
// validate this implementation using curl if you're planning to make any
|
||||
// modifications to how things get saved.
|
||||
|
||||
export default async function upsertUserAll(
|
||||
rep: Repositories,
|
||||
req: UpsertUserAllRequest
|
||||
): Promise<UpsertUserAllResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const payload = req.upsertUserAll;
|
||||
const profile = readUserData(req.upsertUserAll.userData[0]);
|
||||
|
||||
const profileId = await rep.userData().save(aimeId, profile);
|
||||
|
||||
// Empty lists don't get sent at all. Not sure which are potentially empty
|
||||
// so let's just assume the answer is "all of them". Hence the use of || []
|
||||
// everywhere.
|
||||
|
||||
for (const item of payload.userGameOption || []) {
|
||||
await rep.userGameOption().save(profileId, readUserGameOption(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userGameOptionEx || []) {
|
||||
await rep.userGameOptionEx().save(profileId, readUserGameOptionEx(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userMapList || []) {
|
||||
await rep.userMap().save(profileId, readUserMap(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userCharacterList || []) {
|
||||
await rep.userCharacter().save(profileId, readUserCharacter(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userItemList || []) {
|
||||
await rep.userItem().save(profileId, readUserItem(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userMusicDetailList || []) {
|
||||
await rep.userMusic().save(profileId, readUserMusicDetail(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userActivityList || []) {
|
||||
await rep.userActivity().save(profileId, readUserActivity(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userPlaylogList || []) {
|
||||
await rep.userPlaylog().save(profileId, readUserPlaylog(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDataEx || []) {
|
||||
await rep.userDataEx().save(profileId, readUserDataEx(item));
|
||||
}
|
||||
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
+2
-75
@@ -1,75 +1,2 @@
|
||||
import compression from "compression";
|
||||
import express from "express";
|
||||
import logger from "debug";
|
||||
|
||||
import getGameCharge from "./getGameCharge";
|
||||
import getGameEvent from "./getGameEvent";
|
||||
import getGameIdlist from "./getGameIdlist";
|
||||
import getGameMessage from "./getGameMessage";
|
||||
import getGameRanking from "./getGameRanking";
|
||||
import getGameSale from "./getGameSale";
|
||||
import getGameSetting from "./getGameSetting";
|
||||
import upsertClientBookkeeping from "./upsertClientBookkeeping";
|
||||
import upsertClientDevelop from "./upsertClientDevelop";
|
||||
import upsertClientError from "./upsertClientError";
|
||||
import upsertClientSetting from "./upsertClientSetting";
|
||||
import upsertClientTestmode from "./upsertClientTestmode";
|
||||
|
||||
const debug = logger("app:chuni:io");
|
||||
const app = express();
|
||||
|
||||
// Thankfully we can use standard middleware for JSON I/O. We have to use a
|
||||
// zlib middleware as well because compression is non-optional: the client will
|
||||
// attempt to inflate whatever response we send to it whether there's a
|
||||
// Transfer-Encoding header or not.
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
// Chunithm client does not follow the HTTP spec: user agents must indicate
|
||||
// that they are willing to accept a particular Transfer-Encoding for that
|
||||
// T-E to be used (unless the T-E in question is "chunked" but w/e), and this
|
||||
// indication is advisory anyway, the server is permitted to ignore it.
|
||||
//
|
||||
// Force this header into the request, since it is not present normally.
|
||||
|
||||
req.headers["accept-encoding"] = "deflate";
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
app.use(compression({ threshold: 0 }));
|
||||
app.use(express.json());
|
||||
|
||||
// Trace requests and responses
|
||||
|
||||
app.use(function(req, resp, next) {
|
||||
debug("\n--- Chunithm %s ---\n", req.url);
|
||||
debug("Request: %j", req.body);
|
||||
|
||||
const prevJson = resp.json;
|
||||
|
||||
resp.json = function(obj) {
|
||||
debug("Response: %j", obj);
|
||||
|
||||
resp.json = prevJson;
|
||||
resp.json.apply(this, arguments);
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
app.post("/ChuniServlet/GetGameChargeApi", getGameCharge);
|
||||
app.post("/ChuniServlet/GetGameEventApi", getGameEvent);
|
||||
app.post("/ChuniServlet/GetGameIdlistApi", getGameIdlist);
|
||||
app.post("/ChuniServlet/GetGameMessageApi", getGameMessage);
|
||||
app.post("/ChuniServlet/GetGameRankingApi", getGameRanking);
|
||||
app.post("/ChuniServlet/GetGameSaleApi", getGameSale);
|
||||
app.post("/ChuniServlet/GetGameSettingApi", getGameSetting);
|
||||
app.post("/ChuniServlet/UpsertClientBookkeepingApi", upsertClientBookkeeping);
|
||||
app.post("/ChuniServlet/UpsertClientDevelopApi", upsertClientDevelop);
|
||||
app.post("/ChuniServlet/UpsertClientErrorApi", upsertClientError);
|
||||
app.post("/ChuniServlet/UpsertClientSettingApi", upsertClientSetting);
|
||||
app.post("/ChuniServlet/UpsertClientTestmodeApi", upsertClientTestmode);
|
||||
|
||||
export default app;
|
||||
import chunithm from "./handler";
|
||||
export default chunithm;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export interface GameChargeItem {
|
||||
orderId: number;
|
||||
chargeId: number;
|
||||
price: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
salePrice: number;
|
||||
saleStartDate: Date;
|
||||
saleEndDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface GameEventItem {
|
||||
type: number;
|
||||
id: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GameMessageItem {
|
||||
type: number;
|
||||
id: number;
|
||||
message: string;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GameRankingItem {
|
||||
id: bigint;
|
||||
point: bigint;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface GameSaleItem {
|
||||
orderId: number;
|
||||
type: number;
|
||||
id: number;
|
||||
rate: number;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface UserActivityItem {
|
||||
kind: number;
|
||||
activityId: number; // This is just called "id" in the JSON!
|
||||
sortNumber: number;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface UserCharacterItem {
|
||||
characterId: number;
|
||||
playCount: number;
|
||||
level: number;
|
||||
skillId: number;
|
||||
friendshipExp: number;
|
||||
isValid: boolean;
|
||||
isNewMark: boolean;
|
||||
param1: number;
|
||||
param2: number;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
export interface UserDataItem {
|
||||
/** NFC LUID */
|
||||
accessCode: string;
|
||||
|
||||
/** Uses wide latin chars */
|
||||
userName: string;
|
||||
|
||||
isWebJoin: boolean;
|
||||
webLimitDate: Date;
|
||||
level: number;
|
||||
reincarnationNum: number;
|
||||
exp: number;
|
||||
point: number;
|
||||
totalPoint: bigint;
|
||||
playCount: number;
|
||||
multiPlayCount: number;
|
||||
multiWinCount: number;
|
||||
requestResCount: number;
|
||||
acceptResCount: number;
|
||||
successResCount: number;
|
||||
playerRating: number;
|
||||
highestRating: number;
|
||||
nameplateId: number;
|
||||
frameId: number;
|
||||
characterId: number;
|
||||
trophyId: number;
|
||||
playedTutorialBit: number;
|
||||
firstTutorialCancelNum: number;
|
||||
masterTutorialCancelNum: number;
|
||||
totalRepertoireCount: number;
|
||||
totalMapNum: number;
|
||||
totalHiScore: bigint;
|
||||
totalBasicHighScore: bigint;
|
||||
totalAdvancedHighScore: bigint;
|
||||
totalExpertHighScore: bigint;
|
||||
totalMasterHighScore: bigint;
|
||||
eventWatchedDate: Date;
|
||||
friendCount: number;
|
||||
isMaimai: boolean;
|
||||
firstGameId: string;
|
||||
firstRomVersion: string;
|
||||
firstDataVersion: string;
|
||||
firstPlayDate: Date;
|
||||
lastGameId: string;
|
||||
lastRomVersion: string;
|
||||
lastDataVersion: string;
|
||||
lastPlayDate: Date;
|
||||
|
||||
/** ALLNet place ID */
|
||||
lastPlaceId: number;
|
||||
|
||||
/** ALLNet place name */
|
||||
lastPlaceName: string;
|
||||
|
||||
/** ALLNet "region0" */
|
||||
lastRegionId: string;
|
||||
|
||||
/** ALLNet "region_name0" */
|
||||
lastRegionName: string;
|
||||
|
||||
/** ALLNet "allnet_id" string */
|
||||
lastAllNetId: string;
|
||||
|
||||
/** Keychip ID */
|
||||
lastClientId: string;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
export interface UserDataExItem {
|
||||
compatibleCmVersion: string;
|
||||
medal: number;
|
||||
mapIconId: number;
|
||||
voiceId: number;
|
||||
ext1: number;
|
||||
ext2: number;
|
||||
ext3: number;
|
||||
ext4: number;
|
||||
ext5: number;
|
||||
ext6: number;
|
||||
ext7: number;
|
||||
ext8: number;
|
||||
ext9: number;
|
||||
ext10: number;
|
||||
ext11: number;
|
||||
ext12: number;
|
||||
ext13: number;
|
||||
ext14: number;
|
||||
ext15: number;
|
||||
ext16: number;
|
||||
ext17: number;
|
||||
ext18: number;
|
||||
ext19: number;
|
||||
ext20: number;
|
||||
extStr1: string;
|
||||
extStr2: string;
|
||||
extStr3: string;
|
||||
extStr4: string;
|
||||
extStr5: string;
|
||||
extLong1: bigint;
|
||||
extLong2: bigint;
|
||||
extLong3: bigint;
|
||||
extLong4: bigint;
|
||||
extLong5: bigint;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
export interface UserGameOptionItem {
|
||||
bgInfo: number;
|
||||
fieldColor: number;
|
||||
guideSound: number;
|
||||
soundEffect: number;
|
||||
guideLine: number;
|
||||
speed: number;
|
||||
optionSet: number;
|
||||
matching: number;
|
||||
judgePos: number;
|
||||
rating: number;
|
||||
judgeJustice: number;
|
||||
judgeAttack: number;
|
||||
headphone: number;
|
||||
playerLevel: number;
|
||||
successTap: number;
|
||||
successExTap: number;
|
||||
successSlideHold: number;
|
||||
successAir: number;
|
||||
successFlick: number;
|
||||
successSkill: number;
|
||||
successTapTimbre: number;
|
||||
privacy: number;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export interface UserGameOptionExItem {
|
||||
ext1: number;
|
||||
ext2: number;
|
||||
ext3: number;
|
||||
ext4: number;
|
||||
ext5: number;
|
||||
ext6: number;
|
||||
ext7: number;
|
||||
ext8: number;
|
||||
ext9: number;
|
||||
ext10: number;
|
||||
ext11: number;
|
||||
ext12: number;
|
||||
ext13: number;
|
||||
ext14: number;
|
||||
ext15: number;
|
||||
ext16: number;
|
||||
ext17: number;
|
||||
ext18: number;
|
||||
ext19: number;
|
||||
ext20: number;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface UserItemItem {
|
||||
itemKind: number;
|
||||
itemId: number;
|
||||
stock: number;
|
||||
isValid: boolean;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface UserMapItem {
|
||||
mapId: number;
|
||||
position: number;
|
||||
isClear: boolean;
|
||||
areaId: number;
|
||||
routeNumber: number;
|
||||
eventId: number;
|
||||
rate: number;
|
||||
statusCount: number;
|
||||
isValid: boolean;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export interface UserMusicDetailItem {
|
||||
musicId: number;
|
||||
level: number;
|
||||
playCount: number;
|
||||
scoreMax: number;
|
||||
resRequestCount: number;
|
||||
resAcceptCount: number;
|
||||
resSuccessCount: number;
|
||||
missCount: number;
|
||||
maxComboCount: number;
|
||||
isFullCombo: boolean;
|
||||
isAllJustice: boolean;
|
||||
isSuccess: boolean;
|
||||
fullChain: number;
|
||||
maxChain: number;
|
||||
scoreRank: number;
|
||||
isLock: boolean;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
export interface UserPlaylogItem {
|
||||
orderId: number;
|
||||
sortNumber: number;
|
||||
placeId: number;
|
||||
playDate: Date;
|
||||
userPlayDate: Date;
|
||||
musicId: number;
|
||||
level: number;
|
||||
customId: number;
|
||||
playedUserId1: number;
|
||||
playedUserId2: number;
|
||||
playedUserId3: number;
|
||||
playedUserName1: string;
|
||||
playedUserName2: string;
|
||||
playedUserName3: string;
|
||||
playedMusicLevel1: number;
|
||||
playedMusicLevel2: number;
|
||||
playedMusicLevel3: number;
|
||||
playedCustom1: number;
|
||||
playedCustom2: number;
|
||||
playedCustom3: number;
|
||||
track: number;
|
||||
score: number;
|
||||
rank: number;
|
||||
maxCombo: number;
|
||||
maxChain: number;
|
||||
rateTap: number;
|
||||
rateHold: number;
|
||||
rateSlide: number;
|
||||
rateAir: number;
|
||||
rateFlick: number;
|
||||
judgeGuilty: number;
|
||||
judgeAttack: number;
|
||||
judgeJustice: number;
|
||||
judgeCritical: number;
|
||||
eventId: number;
|
||||
playerRating: number;
|
||||
isNewRecord: boolean;
|
||||
isFullCombo: boolean;
|
||||
fullChainKind: number;
|
||||
isAllJustice: boolean;
|
||||
isContinue: boolean;
|
||||
isFreeToPlay: boolean;
|
||||
characterId: number;
|
||||
skillId: number;
|
||||
playKind: number;
|
||||
isClear: boolean;
|
||||
skillLevel: number;
|
||||
skillEffect: number;
|
||||
placeName: string;
|
||||
isMaimai: boolean;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface UserRecentRatingItem {
|
||||
musicId: number;
|
||||
difficultId: number;
|
||||
romVersionCode: number;
|
||||
score: number;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface UserRegionItem {
|
||||
regionId: number;
|
||||
playCount: number;
|
||||
created: Date;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import format from "date-fns/format";
|
||||
|
||||
import { AimeId } from "../../model";
|
||||
|
||||
type ModelObject<T> = {
|
||||
[K in keyof T]: string | number | bigint | boolean | Date;
|
||||
};
|
||||
|
||||
export type Crush<T extends ModelObject<T>> = {
|
||||
[K in keyof T]: string;
|
||||
};
|
||||
|
||||
export function readAimeId(str: string): AimeId {
|
||||
return parseInt(str) as AimeId;
|
||||
}
|
||||
|
||||
export function readBoolean(str: string): boolean {
|
||||
// The compiler will catch typos of "readBoolean". It will not catch typos
|
||||
// of "true". Which sounds like a few hours of debugging I'd prefer to
|
||||
// proactively avoid.
|
||||
return str === "true";
|
||||
}
|
||||
|
||||
export function readDate(str: string): Date {
|
||||
// If we ever need to change this then we'll be able to do it from one place
|
||||
return new Date(str);
|
||||
}
|
||||
|
||||
export function readWtf8(src: string): string {
|
||||
// If you read this aloud it is pronounced "Double UTF-8".
|
||||
// Which actually kind of accurately describes what's going on here.
|
||||
|
||||
const buf = Buffer.alloc(src.length);
|
||||
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
buf.writeUInt8(src.codePointAt(i)!, i);
|
||||
}
|
||||
|
||||
return buf.toString("utf-8");
|
||||
}
|
||||
|
||||
export function writeDate(date: Date): string {
|
||||
return format(date, "YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
export function writeObject<T extends ModelObject<T>>(obj: T): Crush<T> {
|
||||
const result = {} as Crush<T>;
|
||||
|
||||
for (const k in obj) {
|
||||
const v = obj[k];
|
||||
|
||||
if (v instanceof Date) {
|
||||
result[k] = writeDate(v);
|
||||
} else {
|
||||
result[k] = v.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { GameChargeItem } from "../model/gameCharge";
|
||||
|
||||
export type GameChargeJson = Crush<GameChargeItem>;
|
||||
|
||||
export function writeGameCharge(obj: GameChargeItem): GameChargeJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { GameEventItem } from "../model/gameEvent";
|
||||
|
||||
export type GameEventJson = Crush<GameEventItem>;
|
||||
|
||||
export function writeGameEvent(obj: GameEventItem): GameEventJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { GameMessageItem } from "../model/gameMessage";
|
||||
|
||||
export type GameMessageJson = Crush<GameMessageItem>;
|
||||
|
||||
export function writeGameMessage(obj: GameMessageItem): GameMessageJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { GameRankingItem } from "../model/gameRanking";
|
||||
|
||||
export type GameRankingJson = Crush<GameRankingItem>;
|
||||
|
||||
export function writeGameRanking(obj: GameRankingItem): GameRankingJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { GameSaleItem } from "../model/gameSale";
|
||||
|
||||
export type GameSaleJson = Crush<GameSaleItem>;
|
||||
|
||||
export function writeGameSale(obj: GameSaleItem): GameSaleJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { UserActivityItem } from "../model/userActivity";
|
||||
|
||||
export interface UserActivityJson {
|
||||
// Not quite the same as our internal representation! Internally we refer to
|
||||
// the "id" field as "activityId", because the SQL table that stores these
|
||||
// values already has an "id" column that holds a synthetic primary key.
|
||||
|
||||
kind: string;
|
||||
id: string;
|
||||
sortNumber: string;
|
||||
param1: string;
|
||||
param2: string;
|
||||
param3: string;
|
||||
param4: string;
|
||||
}
|
||||
|
||||
export function readUserActivity(json: UserActivityJson): UserActivityItem {
|
||||
return {
|
||||
kind: parseInt(json.kind),
|
||||
activityId: parseInt(json.id), // <-- Look closely!
|
||||
sortNumber: parseInt(json.sortNumber),
|
||||
param1: parseInt(json.param1),
|
||||
param2: parseInt(json.param2),
|
||||
param3: parseInt(json.param3),
|
||||
param4: parseInt(json.param4),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserActivity(obj: UserActivityItem): UserActivityJson {
|
||||
return {
|
||||
kind: obj.kind.toString(),
|
||||
id: obj.activityId.toString(), // <-- Look closely!
|
||||
sortNumber: obj.sortNumber.toString(),
|
||||
param1: obj.param1.toString(),
|
||||
param2: obj.param2.toString(),
|
||||
param3: obj.param3.toString(),
|
||||
param4: obj.param4.toString(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Crush, readBoolean, writeObject } from "./base";
|
||||
import { UserCharacterItem } from "../model/userCharacter";
|
||||
|
||||
export type UserCharacterJson = Crush<UserCharacterItem>;
|
||||
|
||||
export function readUserCharacter(json: UserCharacterJson): UserCharacterItem {
|
||||
return {
|
||||
characterId: parseInt(json.characterId),
|
||||
playCount: parseInt(json.playCount),
|
||||
level: parseInt(json.level),
|
||||
skillId: parseInt(json.skillId),
|
||||
friendshipExp: parseInt(json.friendshipExp),
|
||||
isValid: readBoolean(json.isValid),
|
||||
isNewMark: readBoolean(json.isNewMark),
|
||||
param1: parseInt(json.param1),
|
||||
param2: parseInt(json.param2),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserCharacter(obj: UserCharacterItem): UserCharacterJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Crush, readBoolean, readDate, readWtf8, writeObject } from "./base";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
|
||||
export type UserDataJson = Crush<UserDataItem>;
|
||||
|
||||
export function readUserData(json: UserDataJson): UserDataItem {
|
||||
return {
|
||||
accessCode: json.accessCode,
|
||||
userName: readWtf8(json.userName), // <--
|
||||
isWebJoin: readBoolean(json.isWebJoin),
|
||||
webLimitDate: readDate(json.webLimitDate),
|
||||
level: parseInt(json.level),
|
||||
reincarnationNum: parseInt(json.reincarnationNum),
|
||||
exp: parseInt(json.exp),
|
||||
point: parseInt(json.point),
|
||||
totalPoint: BigInt(json.totalPoint),
|
||||
playCount: parseInt(json.playCount),
|
||||
multiPlayCount: parseInt(json.multiPlayCount),
|
||||
multiWinCount: parseInt(json.multiWinCount),
|
||||
requestResCount: parseInt(json.requestResCount),
|
||||
acceptResCount: parseInt(json.acceptResCount),
|
||||
successResCount: parseInt(json.successResCount),
|
||||
playerRating: parseInt(json.playerRating),
|
||||
highestRating: parseInt(json.highestRating),
|
||||
nameplateId: parseInt(json.nameplateId),
|
||||
frameId: parseInt(json.frameId),
|
||||
characterId: parseInt(json.characterId),
|
||||
trophyId: parseInt(json.trophyId),
|
||||
playedTutorialBit: parseInt(json.playedTutorialBit),
|
||||
firstTutorialCancelNum: parseInt(json.firstTutorialCancelNum),
|
||||
masterTutorialCancelNum: parseInt(json.masterTutorialCancelNum),
|
||||
totalRepertoireCount: parseInt(json.totalRepertoireCount),
|
||||
totalMapNum: parseInt(json.totalMapNum),
|
||||
totalHiScore: BigInt(json.totalHiScore),
|
||||
totalBasicHighScore: BigInt(json.totalBasicHighScore),
|
||||
totalAdvancedHighScore: BigInt(json.totalAdvancedHighScore),
|
||||
totalExpertHighScore: BigInt(json.totalExpertHighScore),
|
||||
totalMasterHighScore: BigInt(json.totalMasterHighScore),
|
||||
eventWatchedDate: readDate(json.eventWatchedDate),
|
||||
friendCount: parseInt(json.friendCount),
|
||||
isMaimai: readBoolean(json.isMaimai),
|
||||
firstGameId: json.firstGameId,
|
||||
firstRomVersion: json.firstRomVersion,
|
||||
firstDataVersion: json.firstDataVersion,
|
||||
firstPlayDate: readDate(json.firstPlayDate),
|
||||
lastGameId: json.lastGameId,
|
||||
lastRomVersion: json.lastRomVersion,
|
||||
lastDataVersion: json.lastDataVersion,
|
||||
lastPlayDate: readDate(json.lastPlayDate),
|
||||
lastPlaceId: parseInt(json.lastPlaceId),
|
||||
lastPlaceName: json.lastPlaceName,
|
||||
lastRegionId: json.lastRegionId,
|
||||
lastRegionName: json.lastRegionName,
|
||||
lastAllNetId: json.lastAllNetId,
|
||||
lastClientId: json.lastClientId,
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserData(obj: UserDataItem): UserDataJson {
|
||||
// Note: no need for a writeWtf8() for the user name.
|
||||
|
||||
// Seems like something went very horribly wrong with their use of the
|
||||
// boost::property_page JSON codec, but C++ and especially Boost is a hot
|
||||
// mess at the best of times, so the Chunithm dev team has my sympathies
|
||||
// nonetheless.
|
||||
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { UserDataExItem } from "../model/userDataEx";
|
||||
|
||||
export type UserDataExJson = Crush<UserDataExItem>;
|
||||
|
||||
export function readUserDataEx(json: UserDataExJson): UserDataExItem {
|
||||
return {
|
||||
compatibleCmVersion: json.compatibleCmVersion,
|
||||
medal: parseInt(json.medal),
|
||||
mapIconId: parseInt(json.mapIconId),
|
||||
voiceId: parseInt(json.voiceId),
|
||||
ext1: parseInt(json.ext1),
|
||||
ext2: parseInt(json.ext2),
|
||||
ext3: parseInt(json.ext3),
|
||||
ext4: parseInt(json.ext4),
|
||||
ext5: parseInt(json.ext5),
|
||||
ext6: parseInt(json.ext6),
|
||||
ext7: parseInt(json.ext7),
|
||||
ext8: parseInt(json.ext8),
|
||||
ext9: parseInt(json.ext9),
|
||||
ext10: parseInt(json.ext10),
|
||||
ext11: parseInt(json.ext11),
|
||||
ext12: parseInt(json.ext12),
|
||||
ext13: parseInt(json.ext13),
|
||||
ext14: parseInt(json.ext14),
|
||||
ext15: parseInt(json.ext15),
|
||||
ext16: parseInt(json.ext16),
|
||||
ext17: parseInt(json.ext17),
|
||||
ext18: parseInt(json.ext18),
|
||||
ext19: parseInt(json.ext19),
|
||||
ext20: parseInt(json.ext20),
|
||||
extStr1: json.extStr1,
|
||||
extStr2: json.extStr2,
|
||||
extStr3: json.extStr3,
|
||||
extStr4: json.extStr4,
|
||||
extStr5: json.extStr5,
|
||||
extLong1: BigInt(json.extLong1),
|
||||
extLong2: BigInt(json.extLong2),
|
||||
extLong3: BigInt(json.extLong3),
|
||||
extLong4: BigInt(json.extLong4),
|
||||
extLong5: BigInt(json.extLong5),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserDataEx(obj: UserDataExItem): UserDataExJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { UserGameOptionItem } from "../model/userGameOption";
|
||||
|
||||
export type UserGameOptionJson = Crush<UserGameOptionItem>;
|
||||
|
||||
export function readUserGameOption(
|
||||
json: UserGameOptionJson
|
||||
): UserGameOptionItem {
|
||||
return {
|
||||
bgInfo: parseInt(json.bgInfo),
|
||||
fieldColor: parseInt(json.fieldColor),
|
||||
guideSound: parseInt(json.guideSound),
|
||||
soundEffect: parseInt(json.soundEffect),
|
||||
guideLine: parseInt(json.guideLine),
|
||||
speed: parseInt(json.speed),
|
||||
optionSet: parseInt(json.optionSet),
|
||||
matching: parseInt(json.matching),
|
||||
judgePos: parseInt(json.judgePos),
|
||||
rating: parseInt(json.rating),
|
||||
judgeJustice: parseInt(json.judgeJustice),
|
||||
judgeAttack: parseInt(json.judgeAttack),
|
||||
headphone: parseInt(json.headphone),
|
||||
playerLevel: parseInt(json.playerLevel),
|
||||
successTap: parseInt(json.successTap),
|
||||
successExTap: parseInt(json.successExTap),
|
||||
successSlideHold: parseInt(json.successSlideHold),
|
||||
successAir: parseInt(json.successAir),
|
||||
successFlick: parseInt(json.successFlick),
|
||||
successSkill: parseInt(json.successSkill),
|
||||
successTapTimbre: parseInt(json.successTapTimbre),
|
||||
privacy: parseInt(json.privacy),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserGameOption(
|
||||
obj: UserGameOptionItem
|
||||
): UserGameOptionJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { UserGameOptionExItem } from "../model/userGameOptionEx";
|
||||
|
||||
export type UserGameOptionExJson = Crush<UserGameOptionExItem>;
|
||||
|
||||
export function readUserGameOptionEx(
|
||||
json: UserGameOptionExJson
|
||||
): UserGameOptionExItem {
|
||||
return {
|
||||
ext1: parseInt(json.ext1),
|
||||
ext2: parseInt(json.ext2),
|
||||
ext3: parseInt(json.ext3),
|
||||
ext4: parseInt(json.ext4),
|
||||
ext5: parseInt(json.ext5),
|
||||
ext6: parseInt(json.ext6),
|
||||
ext7: parseInt(json.ext7),
|
||||
ext8: parseInt(json.ext8),
|
||||
ext9: parseInt(json.ext9),
|
||||
ext10: parseInt(json.ext10),
|
||||
ext11: parseInt(json.ext11),
|
||||
ext12: parseInt(json.ext12),
|
||||
ext13: parseInt(json.ext13),
|
||||
ext14: parseInt(json.ext14),
|
||||
ext15: parseInt(json.ext15),
|
||||
ext16: parseInt(json.ext16),
|
||||
ext17: parseInt(json.ext17),
|
||||
ext18: parseInt(json.ext18),
|
||||
ext19: parseInt(json.ext19),
|
||||
ext20: parseInt(json.ext20),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserGameOptionEx(
|
||||
obj: UserGameOptionExItem
|
||||
): UserGameOptionExJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Crush, readBoolean, writeObject } from "./base";
|
||||
import { UserItemItem } from "../model/userItem";
|
||||
|
||||
export type UserItemJson = Crush<UserItemItem>;
|
||||
|
||||
export function readUserItem(json: UserItemJson): UserItemItem {
|
||||
return {
|
||||
itemKind: parseInt(json.itemKind),
|
||||
itemId: parseInt(json.itemId),
|
||||
stock: parseInt(json.stock),
|
||||
isValid: readBoolean(json.isValid),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserItem(obj: UserItemItem): UserItemJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Crush, readBoolean, writeObject } from "./base";
|
||||
import { UserMapItem } from "../model/userMap";
|
||||
|
||||
export type UserMapJson = Crush<UserMapItem>;
|
||||
|
||||
export function readUserMap(json: UserMapJson): UserMapItem {
|
||||
return {
|
||||
mapId: parseInt(json.mapId),
|
||||
position: parseInt(json.position),
|
||||
isClear: readBoolean(json.isClear),
|
||||
areaId: parseInt(json.areaId),
|
||||
routeNumber: parseInt(json.routeNumber),
|
||||
eventId: parseInt(json.eventId),
|
||||
rate: parseInt(json.rate),
|
||||
statusCount: parseInt(json.statusCount),
|
||||
isValid: readBoolean(json.isValid),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserMap(obj: UserMapItem): UserMapJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { Crush, readBoolean, writeObject } from "./base";
|
||||
import { UserMusicDetailItem } from "../model/userMusic";
|
||||
|
||||
/**
|
||||
* Chart high score object. Saved as a flat list when the client saves new
|
||||
* scores, loaded in groups when a player's high scores are loaded.
|
||||
*/
|
||||
export type UserMusicDetailJson = Crush<UserMusicDetailItem>;
|
||||
|
||||
/**
|
||||
* Grouping of `UserMusicDetailItem` objects, used when scores are loaded.
|
||||
* Scores are grouped by `musicId`.
|
||||
*/
|
||||
export interface UserMusicJson {
|
||||
/** Integer, number of sub-objects in this object */
|
||||
length: string;
|
||||
|
||||
/** Scores for this music ID */
|
||||
userMusicDetailList: UserMusicDetailJson[];
|
||||
}
|
||||
|
||||
export function readUserMusicDetail(
|
||||
json: UserMusicDetailJson
|
||||
): UserMusicDetailItem {
|
||||
return {
|
||||
musicId: parseInt(json.musicId),
|
||||
level: parseInt(json.level),
|
||||
playCount: parseInt(json.playCount),
|
||||
scoreMax: parseInt(json.scoreMax),
|
||||
resRequestCount: parseInt(json.resRequestCount),
|
||||
resAcceptCount: parseInt(json.resAcceptCount),
|
||||
resSuccessCount: parseInt(json.resSuccessCount),
|
||||
missCount: parseInt(json.missCount),
|
||||
maxComboCount: parseInt(json.maxComboCount),
|
||||
isFullCombo: readBoolean(json.isFullCombo),
|
||||
isAllJustice: readBoolean(json.isAllJustice),
|
||||
isSuccess: readBoolean(json.isSuccess),
|
||||
fullChain: parseInt(json.fullChain),
|
||||
maxChain: parseInt(json.maxChain),
|
||||
scoreRank: parseInt(json.scoreRank),
|
||||
isLock: readBoolean(json.isLock),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserMusicDetail(
|
||||
obj: UserMusicDetailItem
|
||||
): UserMusicDetailJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { Crush, readBoolean, readDate } from "./base";
|
||||
import { UserPlaylogItem } from "../model/userPlaylog";
|
||||
|
||||
export type UserPlaylogJson = Crush<UserPlaylogItem>;
|
||||
|
||||
export function readUserPlaylog(json: UserPlaylogJson): UserPlaylogItem {
|
||||
return {
|
||||
orderId: parseInt(json.orderId),
|
||||
sortNumber: parseInt(json.sortNumber),
|
||||
placeId: parseInt(json.placeId),
|
||||
playDate: readDate(json.playDate),
|
||||
userPlayDate: readDate(json.userPlayDate),
|
||||
musicId: parseInt(json.musicId),
|
||||
level: parseInt(json.level),
|
||||
customId: parseInt(json.customId),
|
||||
playedUserId1: parseInt(json.playedUserId1),
|
||||
playedUserId2: parseInt(json.playedUserId2),
|
||||
playedUserId3: parseInt(json.playedUserId3),
|
||||
playedUserName1: json.playedUserName1,
|
||||
playedUserName2: json.playedUserName2,
|
||||
playedUserName3: json.playedUserName3,
|
||||
playedMusicLevel1: parseInt(json.playedMusicLevel1),
|
||||
playedMusicLevel2: parseInt(json.playedMusicLevel2),
|
||||
playedMusicLevel3: parseInt(json.playedMusicLevel3),
|
||||
playedCustom1: parseInt(json.playedCustom1),
|
||||
playedCustom2: parseInt(json.playedCustom2),
|
||||
playedCustom3: parseInt(json.playedCustom3),
|
||||
track: parseInt(json.track),
|
||||
score: parseInt(json.score),
|
||||
rank: parseInt(json.rank),
|
||||
maxCombo: parseInt(json.maxCombo),
|
||||
maxChain: parseInt(json.maxChain),
|
||||
rateTap: parseInt(json.rateTap),
|
||||
rateHold: parseInt(json.rateHold),
|
||||
rateSlide: parseInt(json.rateSlide),
|
||||
rateAir: parseInt(json.rateAir),
|
||||
rateFlick: parseInt(json.rateFlick),
|
||||
judgeGuilty: parseInt(json.judgeGuilty),
|
||||
judgeAttack: parseInt(json.judgeAttack),
|
||||
judgeJustice: parseInt(json.judgeJustice),
|
||||
judgeCritical: parseInt(json.judgeCritical),
|
||||
eventId: parseInt(json.eventId),
|
||||
playerRating: parseInt(json.playerRating),
|
||||
isNewRecord: readBoolean(json.isNewRecord),
|
||||
isFullCombo: readBoolean(json.isFullCombo),
|
||||
fullChainKind: parseInt(json.fullChainKind),
|
||||
isAllJustice: readBoolean(json.isAllJustice),
|
||||
isContinue: readBoolean(json.isContinue),
|
||||
isFreeToPlay: readBoolean(json.isFreeToPlay),
|
||||
characterId: parseInt(json.characterId),
|
||||
skillId: parseInt(json.skillId),
|
||||
playKind: parseInt(json.playKind),
|
||||
isClear: readBoolean(json.isClear),
|
||||
skillLevel: parseInt(json.skillLevel),
|
||||
skillEffect: parseInt(json.skillEffect),
|
||||
placeName: json.placeName,
|
||||
isMaimai: readBoolean(json.isMaimai),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { UserRecentRatingItem } from "../model/userRecentRating";
|
||||
|
||||
export type UserRecentRatingJson = Crush<UserRecentRatingItem>;
|
||||
|
||||
export function readUserRecentRating(
|
||||
json: UserRecentRatingJson
|
||||
): UserRecentRatingItem {
|
||||
return {
|
||||
musicId: parseInt(json.musicId),
|
||||
difficultId: parseInt(json.difficultId),
|
||||
romVersionCode: parseInt(json.romVersionCode),
|
||||
score: parseInt(json.score),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserRecentRating(
|
||||
obj: UserRecentRatingItem
|
||||
): UserRecentRatingJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Crush, readDate, writeObject } from "./base";
|
||||
import { UserRegionItem } from "../model/userRegion";
|
||||
|
||||
export type UserRegionJson = Crush<UserRegionItem>;
|
||||
|
||||
export function readUserRegion(json: UserRegionJson): UserRegionItem {
|
||||
return {
|
||||
regionId: parseInt(json.regionId),
|
||||
playCount: parseInt(json.playCount),
|
||||
created: readDate(json.created),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserRegion(obj: UserRegionItem): UserRegionJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface Page {
|
||||
offset: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface Repository<T> {
|
||||
save(profileId: Id<UserDataItem>, obj: T): Promise<void>;
|
||||
}
|
||||
|
||||
export interface Repository1<T> extends Repository<T> {
|
||||
load(profileId: Id<UserDataItem>): Promise<T>;
|
||||
}
|
||||
|
||||
export interface RepositoryN<T> extends Repository<T> {
|
||||
load(profileId: Id<UserDataItem>, page?: Page): Promise<T[]>;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
export { Page } from "./_defs";
|
||||
|
||||
import { UserActivityRepository } from "./userActivity";
|
||||
import { UserCharacterRepository } from "./userCharacter";
|
||||
import { UserDataRepository } from "./userData";
|
||||
import { UserDataExRepository } from "./userDataEx";
|
||||
import { UserGameOptionRepository } from "./userGameOption";
|
||||
import { UserGameOptionExRepository } from "./userGameOptionEx";
|
||||
import { UserItemRepository } from "./userItem";
|
||||
import { UserMapRepository } from "./userMap";
|
||||
import { UserMusicRepository } from "./userMusic";
|
||||
import { UserPlaylogRepository } from "./userPlaylog";
|
||||
|
||||
export interface Repositories {
|
||||
userActivity(): UserActivityRepository;
|
||||
|
||||
userCharacter(): UserCharacterRepository;
|
||||
|
||||
userData(): UserDataRepository;
|
||||
|
||||
userDataEx(): UserDataExRepository;
|
||||
|
||||
userGameOption(): UserGameOptionRepository;
|
||||
|
||||
userGameOptionEx(): UserGameOptionExRepository;
|
||||
|
||||
userItem(): UserItemRepository;
|
||||
|
||||
userMap(): UserMapRepository;
|
||||
|
||||
userMusic(): UserMusicRepository;
|
||||
|
||||
userPlaylog(): UserPlaylogRepository;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { UserActivityItem } from "../model/userActivity";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface UserActivityRepository {
|
||||
load(profileId: Id<UserDataItem>, kind: number): Promise<UserActivityItem[]>;
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserActivityItem): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { RepositoryN } from "./_defs";
|
||||
import { UserCharacterItem } from "../model/userCharacter";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface UserCharacterRepository
|
||||
extends RepositoryN<UserCharacterItem> {
|
||||
loadOne(
|
||||
profileId: Id<UserDataItem>,
|
||||
characterId: number
|
||||
): Promise<UserCharacterItem>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { AimeId, Id } from "../../model";
|
||||
|
||||
export interface UserDataRepository {
|
||||
load(profileId: Id<UserDataItem>): Promise<UserDataItem>;
|
||||
|
||||
lookup(aimeId: AimeId): Promise<Id<UserDataItem>>;
|
||||
|
||||
save(aimeId: AimeId, obj: UserDataItem): Promise<Id<UserDataItem>>;
|
||||
|
||||
tryLookup(aimeId: AimeId): Promise<Id<UserDataItem> | undefined>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Repository1 } from "./_defs";
|
||||
import { UserDataExItem } from "../model/userDataEx";
|
||||
|
||||
export type UserDataExRepository = Repository1<UserDataExItem>;
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Repository1 } from "./_defs";
|
||||
import { UserGameOptionItem } from "../model/userGameOption";
|
||||
|
||||
export type UserGameOptionRepository = Repository1<UserGameOptionItem>;
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Repository1 } from "./_defs";
|
||||
import { UserGameOptionExItem } from "../model/userGameOptionEx";
|
||||
|
||||
export type UserGameOptionExRepository = Repository1<UserGameOptionExItem>;
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Page } from "./_defs";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserItemItem } from "../model/userItem";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface UserItemRepository {
|
||||
load(
|
||||
profileId: Id<UserDataItem>,
|
||||
itemKind: number,
|
||||
page: Page
|
||||
): Promise<UserItemItem[]>;
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserItemItem): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { RepositoryN } from "./_defs";
|
||||
import { UserMapItem } from "../model/userMap";
|
||||
|
||||
export type UserMapRepository = RepositoryN<UserMapItem>;
|
||||
@@ -0,0 +1,4 @@
|
||||
import { RepositoryN } from "./_defs";
|
||||
import { UserMusicDetailItem } from "../model/userMusic";
|
||||
|
||||
export type UserMusicRepository = RepositoryN<UserMusicDetailItem>;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserPlaylogItem } from "../model/userPlaylog";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface UserPlaylogRepository {
|
||||
// This seems to only be used internally by SEGA for analytics, but the data
|
||||
// is nice to have.
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserPlaylogItem): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GameLoginRequest {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GameLogoutRequest {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetGameChargeRequest {
|
||||
/** Boolean */
|
||||
isAllSale: "string";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetGameEventRequest {
|
||||
// FIXME
|
||||
type: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetGameIdlistRequest {
|
||||
/** Integer */
|
||||
type: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GetGameMessageRequest {
|
||||
/** Integer */
|
||||
type: string;
|
||||
|
||||
/** Boolean */
|
||||
isAllMessage: string;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface GetGameRankingRequest {
|
||||
/** Long */
|
||||
type: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GetGameSaleRequest {
|
||||
/** Integer */
|
||||
type: string;
|
||||
|
||||
/** Boolean */
|
||||
isAllSale: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GetGameSettingRequest {
|
||||
/** Base-10 ALLNet location ID */
|
||||
placeId: string;
|
||||
|
||||
/** Keychip ID */
|
||||
clientId: string;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface GetUserActivityRequest {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
|
||||
/** Integer, filter on `UserActivityItem.kind`. */
|
||||
kind: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user