The server for Open Course World

dyna: remove dumpView and use 3 optional fetcher methods instead

mm2srv 077f91d6 93dc5426

+191 -161
+28 -22
db/dyna/dyna.go
··· 8 8 "smm2_gameserver/config" 9 9 "smm2_gameserver/db/dump" 10 10 "smm2_gameserver/db/view" 11 - "smm2_gameserver/db/view/dump_view" 12 11 "smm2_gameserver/nex/connection/user" 13 12 "smm2_gameserver/nex/datastore" 14 13 "smm2_gameserver/nex/id" ··· 32 31 var USER_DATA_ID_START int64 = util.READ_ONLY_USER_PID 33 32 34 33 type DB struct { 35 - Config *config.Config 36 - Conn *pgxpool.Pool 37 - EndlessTypes map[int]EndlessTypeHandler 38 - sillyImportLock map[datastore.Pid]bool 34 + Config *config.Config 35 + Conn *pgxpool.Pool 36 + EndlessTypes map[int]EndlessTypeHandler 37 + sillyImportLock map[datastore.Pid]bool 38 + DumpGetWorldMapByIds func(state *user.State, ids []string) ([]datastore.WorldMapInfo, error) 39 + DumpGetCourseInfo func(state *user.State, dataId uint64) (datastore.CourseInfo, error) 40 + DumpGetCourseInfoAtOffset func(offset int64) (datastore.CourseInfo, error) 39 41 } 40 42 41 43 func New(config *config.Config) (*DB, error) { ··· 141 143 return false 142 144 } 143 145 144 - func (s *DB) PostPlayResult(data_id int64, pid datastore.Pid, tries int, run_time int, dumpView *dump_view.DumpView) error { 146 + func (s *DB) PostPlayResult(data_id int64, pid datastore.Pid, tries int, run_time int) error { 145 147 ownCourse := s.isOwnCourse(data_id, pid) 146 148 147 149 result := PlayResult{} ··· 210 212 return nil 211 213 } 212 214 213 - func (s *DB) PostRankingInfo(data_id int64, pid datastore.Pid, like_info uint8, dumpView *dump_view.DumpView) error { 215 + func (s *DB) PostRankingInfo(data_id int64, pid datastore.Pid, like_info uint8) error { 214 216 if s.isOwnCourse(data_id, pid) { 215 217 return nil 216 218 } ··· 800 802 return ids, nil 801 803 } 802 804 803 - func (s *DB) GetCourseInfosInterleave(current_pid datastore.Pid, ids []int64, dumpView *dump_view.DumpView) ([]datastore.CourseInfo, error) { 805 + func (s *DB) GetCourseInfosInterleave(current_pid datastore.Pid, ids []int64) ([]datastore.CourseInfo, error) { 804 806 // Equivelent to GetWorldMapByIdsInterleave for levels, just doesn't batch 805 807 infos := []datastore.CourseInfo{} 806 808 for _, data_id := range ids { 807 809 info, err := s.GetCourseInfo(data_id, s.Config) 808 810 if err == pgx.ErrNoRows { 809 - info, err = dumpView.GetCourseInfo(nil, uint64(data_id)) 810 - if err != nil { 811 - fmt.Println("GetCourseInfosInterleave Not Found in Dump", data_id, err) 811 + if s.DumpGetCourseInfo != nil { 812 + info, err = s.DumpGetCourseInfo(nil, uint64(data_id)) 813 + if err != nil { 814 + fmt.Println("GetCourseInfosInterleave Not Found in Dump", data_id, err) 815 + continue 816 + } 817 + } else { 812 818 continue 813 819 } 814 820 } else if err != nil { ··· 831 837 return infos, nil 832 838 } 833 839 834 - func (s *DB) SearchCourses(state *user.State, query_pid datastore.Pid, dumpView *dump_view.DumpView, param view.SearchCoursesParam) ([]datastore.CourseInfo, []uint32, bool, error) { 840 + func (s *DB) SearchCourses(state *user.State, query_pid datastore.Pid, param view.SearchCoursesParam) ([]datastore.CourseInfo, []uint32, bool, error) { 835 841 infos := []datastore.CourseInfo{} 836 842 837 843 // Only query courses in dyna (dump if they are a legacy player), pid is guaranteed to be our own ··· 850 856 return infos, []uint32{}, false, err 851 857 } 852 858 853 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 859 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 854 860 return infos, []uint32{}, len(infos) != 0, err 855 861 case "liked_by": 856 862 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM like_info WHERE pid = $1 AND liked = True ORDER BY id DESC LIMIT $2 OFFSET $3", query_pid, param.Size, param.Offset) ··· 864 870 return infos, []uint32{}, false, err 865 871 } 866 872 867 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 873 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 868 874 return infos, []uint32{}, len(infos) != 0, err 869 875 case "booed_by": // sadly the game doesn't show this anyways. but our frontend could. 870 876 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM like_info WHERE pid = $1 AND booed = True ORDER BY id DESC LIMIT $2 OFFSET $3", query_pid, param.Size, param.Offset) ··· 878 884 return infos, []uint32{}, false, err 879 885 } 880 886 881 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 887 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 882 888 return infos, []uint32{}, len(infos) != 0, err 883 889 case "played_by": 884 890 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM player_result WHERE pid = $1 ORDER BY id DESC LIMIT $2 OFFSET $3", query_pid, param.Size, param.Offset) ··· 892 898 return infos, []uint32{}, false, err 893 899 } 894 900 895 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 901 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 896 902 return infos, []uint32{}, len(infos) != 0, err 897 903 case "first_clear_by": 898 904 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM player_result WHERE pid = $1 AND is_first_clear = True ORDER BY id DESC LIMIT $2 OFFSET $3", query_pid, param.Size, param.Offset) ··· 906 912 return infos, []uint32{}, false, err 907 913 } 908 914 909 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 915 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 910 916 return infos, []uint32{}, len(infos) != 0, err 911 917 case "world_record_by": 912 918 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM player_result WHERE pid = $1 AND is_world_record = True ORDER BY id DESC LIMIT $2 OFFSET $3", query_pid, param.Size, param.Offset) ··· 920 926 return infos, []uint32{}, false, err 921 927 } 922 928 923 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 929 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 924 930 return infos, []uint32{}, len(infos) != 0, err 925 931 case "followee_posted_by": 926 932 rows, err := s.Conn.Query(context.Background(), "SELECT data_id FROM course WHERE deleted = False AND time_uploaded IS NOT NULL AND uploader IN (SELECT followed_pid FROM player_follow WHERE pid = $3) ORDER BY id DESC LIMIT $1 OFFSET $2", param.Size, param.Offset, query_pid) ··· 934 940 return infos, []uint32{}, false, err 935 941 } 936 942 937 - infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids, dumpView) 943 + infos, err = s.GetCourseInfosInterleave(datastore.Pid(state.Pid), ids) 938 944 return infos, []uint32{}, len(infos) != 0, err 939 945 case "advanced": 940 - return s.SearchCoursesAdvanced(datastore.Pid(state.Pid), dumpView, param) 946 + return s.SearchCoursesAdvanced(datastore.Pid(state.Pid), param) 941 947 } 942 948 943 949 return infos, []uint32{}, false, nil 944 950 } 945 951 946 - func (s *DB) SearchCoursesAdvanced(current_pid datastore.Pid, dumpView *dump_view.DumpView, param view.SearchCoursesParam) ([]datastore.CourseInfo, []uint32, bool, error) { 952 + func (s *DB) SearchCoursesAdvanced(current_pid datastore.Pid, param view.SearchCoursesParam) ([]datastore.CourseInfo, []uint32, bool, error) { 947 953 infos := []datastore.CourseInfo{} 948 954 949 955 sortBy := "" ··· 1076 1082 return infos, []uint32{}, false, err 1077 1083 } 1078 1084 1079 - infos, err = s.GetCourseInfosInterleave(current_pid, ids, dumpView) 1085 + infos, err = s.GetCourseInfosInterleave(current_pid, ids) 1080 1086 return infos, []uint32{}, len(infos) != 0, err 1081 1087 } 1082 1088
+2 -3
db/dyna/endless.go
··· 4 4 "context" 5 5 "fmt" 6 6 "math/rand" 7 - "smm2_gameserver/db/view/dump_view" 8 7 "smm2_gameserver/nex/datastore" 9 8 "time" 10 9 ··· 244 243 return handler.ExitEndlessCourse(current_pid, difficulty) 245 244 } 246 245 247 - func (s *DB) GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int, dump_view *dump_view.DumpView) ([]datastore.CourseInfo, error) { 246 + func (s *DB) GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int) ([]datastore.CourseInfo, error) { 248 247 handler, err := s.GetEndlessTypeHandler(current_pid, difficulty) 249 248 if err != nil { 250 249 return []datastore.CourseInfo{}, err 251 250 } 252 - return handler.GetEndlessCourses(current_pid, difficulty, num_levels, dump_view) 251 + return handler.GetEndlessCourses(current_pid, difficulty, num_levels) 253 252 }
+1 -2
db/dyna/endless_type_interface.go
··· 1 1 package dyna 2 2 3 3 import ( 4 - "smm2_gameserver/db/view/dump_view" 5 4 "smm2_gameserver/nex/datastore" 6 5 ) 7 6 ··· 12 11 ClearEndlessCourse(current_pid datastore.Pid, difficulty, new_lives, coins, points_score int) (datastore.EndlessModeRunState, error) 13 12 SkipEndlessCourse(current_pid datastore.Pid, difficulty int) error 14 13 GameOverEndless(current_pid datastore.Pid, difficulty int) error 15 - GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int, dump_view *dump_view.DumpView) ([]datastore.CourseInfo, error) 14 + GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int) ([]datastore.CourseInfo, error) 16 15 GetEndlessStartLives(current_pid datastore.Pid, difficulty uint8) int 17 16 }
+28 -78
db/dyna/endless_type_normal.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "fmt" 6 5 "math/rand" 7 - "smm2_gameserver/db/dump" 8 - "smm2_gameserver/db/view/dump_view" 9 6 "smm2_gameserver/nex/datastore" 10 7 "time" 11 8 ··· 227 224 return nil 228 225 } 229 226 230 - func (s *EndlessTypeNormal) GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int, dump_view *dump_view.DumpView) ([]datastore.CourseInfo, error) { 227 + func (s *EndlessTypeNormal) GetEndlessCourses(current_pid datastore.Pid, difficulty int, num_levels int) ([]datastore.CourseInfo, error) { 231 228 num_levels = 1 232 229 233 230 courses := []datastore.CourseInfo{} ··· 243 240 // Use our random number generator 244 241 random := rand.New(rand.NewSource(current_seed)) 245 242 246 - if s.db.Config.UseDumpForSearches { 247 - if true { 248 - // until the dump index is created use this 249 - num_levels_total := int64(26609725) 250 - 251 - tries := 0 252 - for tries = 0; tries < 100; tries++ { 253 - generated_offset := random.Int63n(num_levels_total) 254 - dumpCourseInfo := dump.CourseInfo{} 243 + if s.db.Config.UseDumpForSearches && s.db.DumpGetCourseInfoAtOffset != nil { 244 + // until the dump index is created use this 245 + num_levels_total := int64(26609725) 255 246 256 - err := dump_view.DumpConn.QueryRowx("SELECT * FROM level ORDER BY data_id LIMIT -1 OFFSET ?", generated_offset).StructScan(&dumpCourseInfo) 257 - if err != nil { 258 - return courses, err 259 - } 247 + tries := 0 248 + for tries = 0; tries < 100; tries++ { 249 + generated_offset := random.Int63n(num_levels_total) 260 250 261 - //if dumpCourseInfo.Difficulty == 0 && dumpCourseInfo.GameStyle == 3 { 262 - if dumpCourseInfo.Difficulty == int(difficulty) { 263 - info := dump.DatastoreFromCourseInfo(dumpCourseInfo) 264 - info, err = s.db.UpdateCourseInfo(info, current_pid) 265 - if err != nil { 266 - return courses, err 267 - } 268 - courses = append(courses, info) 269 - break 270 - } 251 + info, err := s.db.DumpGetCourseInfoAtOffset(generated_offset) 252 + if err != nil { 253 + return courses, err 271 254 } 272 255 273 - if len(courses) != 0 { 274 - // Generate our updated seed 275 - random = rand.New(rand.NewSource(current_seed)) 276 - for i := 0; i < tries-1; i++ { 277 - random.Int63() 278 - } 279 - 280 - // Update the current random seed 281 - _, err = s.Conn.Exec(context.Background(), `UPDATE current_endless_run SET current_seed = $3 WHERE pid = $1 AND difficulty = $2`, current_pid, difficulty, random.Int63()) 256 + //if dumpCourseInfo.Difficulty == 0 && dumpCourseInfo.GameStyle == 3 { 257 + if info.Difficulty == uint8(difficulty) { 258 + info, err = s.db.UpdateCourseInfo(info, current_pid) 282 259 if err != nil { 283 260 return courses, err 284 261 } 262 + courses = append(courses, info) 263 + break 285 264 } 265 + } 286 266 287 - return courses, nil 288 - } else { 289 - // This is simply too slow even with indices 290 - // Get number of levels per difficulty (the dump is static so this number should never change) 291 - var num_levels_total int64 292 - //err = dump_view.DumpConn.QueryRowx("SELECT MAX(ROWID) FROM level").Scan(&num_levels_total) 293 - //if err != nil { 294 - // return courses, err 295 - //} 296 - switch difficulty { 297 - case 0: 298 - num_levels_total = 8710121 299 - break 300 - case 1: 301 - num_levels_total = 11744359 302 - break 303 - case 2: 304 - num_levels_total = 4150458 305 - break 306 - case 3: 307 - num_levels_total = 2004787 308 - break 309 - default: 310 - return courses, fmt.Errorf("No difficulty exists with id %d", difficulty) 267 + if len(courses) != 0 { 268 + // Generate our updated seed 269 + random = rand.New(rand.NewSource(current_seed)) 270 + for i := 0; i < tries-1; i++ { 271 + random.Int63() 311 272 } 312 273 313 - for level := 0; level < num_levels; level++ { 314 - generated_offset := int64(random.Int63n(num_levels_total)) 315 - dumpCourseInfo := dump.CourseInfo{} 316 - 317 - // Start at randomized offset looking for difficulty 318 - // TODO prioritize high likes 319 - err := dump_view.DumpConn.QueryRowx("SELECT * FROM level WHERE difficulty = ? ORDER BY data_id LIMIT -1 OFFSET ?", difficulty, generated_offset).StructScan(&dumpCourseInfo) 320 - if err != nil { 321 - return courses, err 322 - } 323 - 324 - info := dump.DatastoreFromCourseInfo(dumpCourseInfo) 325 - info, err = s.db.UpdateCourseInfo(info, current_pid) 326 - if err != nil { 327 - return courses, err 328 - } 329 - courses = append(courses, info) 274 + // Update the current random seed 275 + _, err = s.Conn.Exec(context.Background(), `UPDATE current_endless_run SET current_seed = $3 WHERE pid = $1 AND difficulty = $2`, current_pid, difficulty, random.Int63()) 276 + if err != nil { 277 + return courses, err 330 278 } 331 279 } 280 + 281 + return courses, nil 332 282 } else { 333 283 // Only use levels already uploaded to dyna 334 284 // Get number of levels ··· 357 307 return courses, err 358 308 } 359 309 360 - courses, err = s.db.GetCourseInfosInterleave(current_pid, ids, dump_view) 310 + courses, err = s.db.GetCourseInfosInterleave(current_pid, ids) 361 311 if err != nil { 362 312 return courses, err 363 313 }
+3 -4
db/dyna/endless_type_noskip.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "smm2_gameserver/db/view/dump_view" 6 5 "smm2_gameserver/nex/datastore" 7 6 8 7 "github.com/jackc/pgx/v4/pgxpool" ··· 77 76 return s.Normal.ExitEndlessCourse(current_pid, difficulty) 78 77 } 79 78 80 - func (s *EndlessTypeNoSkip) GetEndlessCourses(current_pid datastore.Pid, difficulty int, _num_levels int, dump_view *dump_view.DumpView) ([]datastore.CourseInfo, error) { 79 + func (s *EndlessTypeNoSkip) GetEndlessCourses(current_pid datastore.Pid, difficulty int, _num_levels int) ([]datastore.CourseInfo, error) { 81 80 courses := []datastore.CourseInfo{} 82 81 83 82 row := s.Conn.QueryRow(context.Background(), "SELECT current_data_id, clears, num_levels FROM current_endless_run WHERE pid = $1 AND difficulty = $2", current_pid, difficulty) ··· 90 89 return courses, err 91 90 } 92 91 if oldDataId.Int64 == 0 { 93 - return s.Normal.GetEndlessCourses(current_pid, difficulty, num_levels, dump_view) 92 + return s.Normal.GetEndlessCourses(current_pid, difficulty, num_levels) 94 93 } 95 94 96 95 if clears != num_levels { ··· 109 108 return []datastore.CourseInfo{info}, nil 110 109 } 111 110 112 - return s.Normal.GetEndlessCourses(current_pid, difficulty, num_levels, dump_view) 111 + return s.Normal.GetEndlessCourses(current_pid, difficulty, num_levels) 113 112 }
+18 -12
db/dyna/super_world.go
··· 4 4 "context" 5 5 "fmt" 6 6 "math/rand" 7 - "smm2_gameserver/db/view/dump_view" 8 7 "smm2_gameserver/nex/datastore" 9 8 "time" 10 9 ··· 71 70 return err 72 71 } 73 72 74 - func (s *DB) GetWorldMapByIdsInterleave(current_pid datastore.Pid, ids []string, dumpView *dump_view.DumpView) ([]datastore.WorldMapInfo, error) { 73 + func (s *DB) GetWorldMapByIdsInterleave(current_pid datastore.Pid, ids []string) ([]datastore.WorldMapInfo, error) { 75 74 infos := []datastore.WorldMapInfo{} 76 75 // Transparently request dump and OCW worlds at the same time 77 76 dump_ids := []string{} 78 77 ocw_ids := []string{} 79 78 dump_indices := []int{} 80 79 ocw_indices := []int{} 80 + dump_worlds := []datastore.WorldMapInfo{} 81 + 81 82 for index, id := range ids { 82 83 if len(id) < 35 { 83 84 // OCW super world ··· 94 95 // Super world deletion complicates this function 95 96 96 97 // Request dump worlds 97 - dump_worlds, err := dumpView.GetWorldMapByIds(nil, dump_ids) 98 - if err == pgx.ErrNoRows { 98 + if s.DumpGetWorldMapByIds != nil { 99 + var err error 100 + dump_worlds, err = s.DumpGetWorldMapByIds(nil, dump_ids) 101 + if err == pgx.ErrNoRows { 102 + dump_indices = []int{} 103 + } else if err != nil { 104 + return infos, err 105 + } 106 + } else { 99 107 dump_indices = []int{} 100 - } else if err != nil { 101 - return infos, err 102 108 } 103 109 104 110 // Request OCW worlds 105 - ocw_worlds, err := s.GetWorldMapByIds(current_pid, ids, dumpView) 111 + ocw_worlds, err := s.GetWorldMapByIds(current_pid, ids) 106 112 if err == pgx.ErrNoRows { 107 113 ocw_indices = []int{} 108 114 } else if err != nil { ··· 121 127 return worlds, nil 122 128 } 123 129 124 - func (s *DB) GetWorldMapByIds(current_pid datastore.Pid, ids []string, dumpView *dump_view.DumpView) ([]datastore.WorldMapInfo, error) { 130 + func (s *DB) GetWorldMapByIds(current_pid datastore.Pid, ids []string) ([]datastore.WorldMapInfo, error) { 125 131 worlds := []datastore.WorldMapInfo{} 126 132 for _, id := range ids { 127 133 world, err := s.GetSuperWorld(id) ··· 135 141 return worlds, nil 136 142 } 137 143 138 - func (s *DB) GetWorldsPlayedBy(current_pid datastore.Pid, pids []uint64, offset, size uint32, dumpView *dump_view.DumpView) ([]datastore.WorldMapInfo, error) { 144 + func (s *DB) GetWorldsPlayedBy(current_pid datastore.Pid, pids []uint64, offset, size uint32) ([]datastore.WorldMapInfo, error) { 139 145 infos := []datastore.WorldMapInfo{} 140 146 141 147 // Return played super worlds ordered by date played, most recent first ··· 155 161 } 156 162 rows.Close() 157 163 158 - return s.GetWorldMapByIdsInterleave(current_pid, super_world_ids, dumpView) 164 + return s.GetWorldMapByIdsInterleave(current_pid, super_world_ids) 159 165 } 160 166 161 167 // Use same strategy as endless, random offset in list 162 - func (s *DB) GetWorldsRandom(current_pid datastore.Pid, size uint32, dumpView *dump_view.DumpView) ([]datastore.WorldMapInfo, error) { 168 + func (s *DB) GetWorldsRandom(current_pid datastore.Pid, size uint32) ([]datastore.WorldMapInfo, error) { 163 169 infos := []datastore.WorldMapInfo{} 164 170 165 171 // Get number of world maps in total ··· 190 196 } 191 197 rows.Close() 192 198 193 - return s.GetWorldMapByIds(current_pid, ids, dumpView) 199 + return s.GetWorldMapByIds(current_pid, ids) 194 200 } 195 201 196 202 func (s *DB) RegisterWorldMap(current_pid datastore.Pid, param datastore.RegisterWorldMapParam) error {
+101 -40
db/view/combined_view/view.go
··· 10 10 "smm2_gameserver/db/view/dump_view" 11 11 "smm2_gameserver/nex/connection/user" 12 12 "smm2_gameserver/nex/datastore" 13 + "smm2_gameserver/nex/id" 13 14 "smm2_gameserver/orm" 14 15 "smm2_gameserver/util" 16 + "smm2_gameserver/util/thumbgen" 15 17 "smm2_gameserver/util/user_token" 16 18 "strconv" 17 19 "time" ··· 48 50 49 51 if err != nil { 50 52 return nil, fmt.Errorf("could not initialize dyna: %s", err) 53 + } 54 + 55 + if s.DumpView != nil { 56 + s.DB.DumpGetWorldMapByIds = s.DumpView.GetWorldMapByIds 57 + s.DB.DumpGetCourseInfo = s.DumpView.GetCourseInfo 58 + s.DB.DumpGetCourseInfoAtOffset = s.DumpView.GetCourseInfoAtOffset 51 59 } 52 60 53 61 return s, nil ··· 105 113 return courseInfo, err 106 114 } 107 115 } else { 108 - // Obtain level from dyna, uploaded to Nintendo's servers 109 - courseInfo, err = s.DumpView.GetCourseInfo(state, dataId) 110 - if err != nil { 111 - return courseInfo, err 116 + if s.DumpView != nil { 117 + // Obtain level from dyna, uploaded to Nintendo's servers 118 + courseInfo, err = s.DumpView.GetCourseInfo(state, dataId) 119 + if err != nil { 120 + return courseInfo, err 121 + } 122 + } else { 123 + return courseInfo, fmt.Errorf("not found") 112 124 } 113 125 } 114 126 ··· 127 139 } 128 140 129 141 func (s *CombinedView) ServeLevelData(w http.ResponseWriter, r *http.Request) { 142 + 130 143 vars := mux.Vars(r) 131 144 data_id, err := strconv.Atoi(vars["data_id"]) 132 145 if err != nil { 133 - w.WriteHeader(400) 146 + w.WriteHeader(404) 134 147 return 135 148 } 136 149 ··· 139 152 encrypted, err := s.DB.GetCourseData(int64(data_id)) 140 153 if err != nil { 141 154 fmt.Println("ServeLevelData Error", data_id, err) 142 - w.WriteHeader(500) 155 + w.WriteHeader(404) 143 156 return 144 157 } 145 158 ··· 147 160 w.Write(encrypted) 148 161 } else { 149 162 // Serve from dump 150 - s.DumpView.ServeLevelData(w, r) 163 + if s.DumpView != nil { 164 + s.DumpView.ServeLevelData(w, r) 165 + } else { 166 + w.WriteHeader(404) 167 + } 151 168 } 152 169 } 153 170 ··· 176 193 vars := mux.Vars(r) 177 194 data_id, err := strconv.Atoi(vars["data_id"]) 178 195 if err != nil { 179 - w.WriteHeader(400) 196 + w.WriteHeader(404) 180 197 return 181 198 } 182 199 ··· 188 205 level_data, err = s.DB.GetCourseDataCompressed(int64(data_id)) 189 206 } else { 190 207 // Get from dump compressed 191 - level_data, err = s.DumpView.GetLevelDataByDataId(data_id) 208 + if s.DumpView != nil { 209 + level_data, err = s.DumpView.GetLevelDataByDataId(data_id) 210 + } else { 211 + err = fmt.Errorf("not found") 212 + } 192 213 } 193 214 if err != nil { 194 215 w.WriteHeader(400) ··· 222 243 thumbnail, err := s.DB.GetCourseOneScreenThumbnail(int64(data_id)) 223 244 if err != nil { 224 245 fmt.Println("ServeLevelThumbnail Error", data_id, err) 225 - w.WriteHeader(500) 246 + w.WriteHeader(404) 226 247 return 227 248 } 228 249 ··· 230 251 w.Write(thumbnail) 231 252 } else { 232 253 // Serve from dump 233 - s.DumpView.ServeLevelThumbnail(w, r) 254 + if s.DumpView != nil { 255 + s.DumpView.ServeLevelThumbnail(w, r) 256 + } else { 257 + w.WriteHeader(404) 258 + } 234 259 } 235 260 } 236 261 } 237 262 238 263 func (s *CombinedView) ServeLevelEntireThumbnail(w http.ResponseWriter, r *http.Request) { 239 - s.DumpView.ServeLevelEntireThumbnail(w, r) 264 + if s.DumpView != nil { 265 + s.DumpView.ServeLevelEntireThumbnail(w, r) 266 + } else { 267 + // generate entire thumb template image 268 + vars := mux.Vars(r) 269 + dataId, err := strconv.Atoi(vars["data_id"]) 270 + 271 + if err != nil { 272 + w.WriteHeader(404) 273 + return 274 + } 275 + 276 + code, _ := id.DataIdToCourseId(uint64(dataId), true) 277 + buf, _ := thumbgen.GenerateEntireThumbMissingJpeg(code) 278 + if buf.Len() > 0xa000 { 279 + w.WriteHeader(404) 280 + return 281 + } 282 + 283 + pad := make([]byte, 0xa000-buf.Len()) 284 + buf.Write(pad) 285 + 286 + w.WriteHeader(200) 287 + buf.WriteTo(w) 288 + } 240 289 } 241 290 242 291 func (s *CombinedView) ServeWorldThumbnail(w http.ResponseWriter, r *http.Request) { 243 - s.DumpView.ServeWorldThumbnail(w, r) 292 + if s.DumpView != nil { 293 + s.DumpView.ServeWorldThumbnail(w, r) 294 + } else { 295 + w.WriteHeader(404) 296 + } 244 297 } 245 298 246 299 func (s *CombinedView) GetEventCourseStatus(state *user.State) (datastore.EventCourseStatusInfo, error) { ··· 269 322 userInfo, err := s.DB.GetUserInfoByCode(state, code) 270 323 if err != nil { 271 324 //fmt.Println("GetUserInfoByCode", state.Pid, code, err) 272 - // try dump_view instead 273 - return s.DumpView.GetUserInfoByCode(state, code) 325 + if s.DumpView != nil { 326 + // try dump_view instead 327 + return s.DumpView.GetUserInfoByCode(state, code) 328 + } else { 329 + return userInfo, err 330 + } 274 331 } 275 332 return userInfo, nil 276 333 } ··· 310 367 if err != nil { 311 368 //fmt.Println("GetUserInfoByPid", state.Pid, pid, err) 312 369 // try dump instead 313 - info, err = s.DumpView.GetUserInfoByPid(state, pid, option) 314 - if err != nil { 370 + if s.DumpView != nil { 371 + info, err = s.DumpView.GetUserInfoByPid(state, pid, option) 372 + if err != nil { 373 + return info, err 374 + } 375 + } else { 315 376 return info, err 316 377 } 317 378 } ··· 353 414 switch param.Type { 354 415 case "posted_by": 355 416 if util.OurPids(param.Pids) { 356 - return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), s.DumpView, param) 357 - } else { 417 + return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), param) 418 + } else if s.DumpView != nil { 358 419 courses, err := s.DumpView.GetCoursesTypeBy(state, "user_posted", param.Pids, param.Offset, param.Size) 359 420 return s.UpdateCoursesInfo(err, state, courses) 360 421 } 361 422 case "liked_by": 362 423 if util.OurPids(param.Pids) { 363 - return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), s.DumpView, param) 364 - } else { 424 + return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), param) 425 + } else if s.DumpView != nil { 365 426 courses, err := s.DumpView.GetCoursesTypeBy(state, "user_liked", param.Pids, param.Offset, param.Size) 366 427 return s.UpdateCoursesInfo(err, state, courses) 367 428 } 368 429 case "played_by": 369 430 if util.OurPids(param.Pids) { 370 - return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), s.DumpView, param) 371 - } else { 431 + return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), param) 432 + } else if s.DumpView != nil { 372 433 courses, err := s.DumpView.GetCoursesTypeBy(state, "user_played", param.Pids, param.Offset, param.Size) 373 434 return s.UpdateCoursesInfo(err, state, courses) 374 435 } 375 436 case "first_clear_by": 376 437 if util.OurPids(param.Pids) { 377 - return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), s.DumpView, param) 378 - } else { 438 + return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), param) 439 + } else if s.DumpView != nil { 379 440 courses, err := s.DumpView.GetCoursesTypeBy(state, "user_first_cleared", param.Pids, param.Offset, param.Size) 380 441 return s.UpdateCoursesInfo(err, state, courses) 381 442 } 382 443 case "world_record_by": 383 444 if util.OurPids(param.Pids) { 384 - return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), s.DumpView, param) 385 - } else { 445 + return s.DB.SearchCourses(state, datastore.Pid(param.Pids[0]), param) 446 + } else if s.DumpView != nil { 386 447 courses, err := s.DumpView.GetCoursesTypeBy(state, "user_world_record", param.Pids, param.Offset, param.Size) 387 448 return s.UpdateCoursesInfo(err, state, courses) 388 449 } 389 450 case "followee_posted_by": 390 - return s.DB.SearchCourses(state, datastore.Pid(state.Pid), s.DumpView, param) 451 + return s.DB.SearchCourses(state, datastore.Pid(state.Pid), param) 391 452 case "advanced": 392 - return s.DB.SearchCourses(state, datastore.Pid(state.Pid), s.DumpView, param) 453 + return s.DB.SearchCourses(state, datastore.Pid(state.Pid), param) 393 454 case "hot": 394 - return s.DB.SearchCourses(state, datastore.Pid(state.Pid), s.DumpView, param) 455 + return s.DB.SearchCourses(state, datastore.Pid(state.Pid), param) 395 456 case "popular_weekly": 396 - return s.DB.SearchCourses(state, datastore.Pid(state.Pid), s.DumpView, param) 457 + return s.DB.SearchCourses(state, datastore.Pid(state.Pid), param) 397 458 case "popular_all_time": 398 - return s.DB.SearchCourses(state, datastore.Pid(state.Pid), s.DumpView, param) 459 + return s.DB.SearchCourses(state, datastore.Pid(state.Pid), param) 399 460 case "latest": 400 461 courses, err := s.DB.GetCoursesLatest(int(param.Offset), int(param.Size)) 401 462 restSize := param.Size - uint32(len(courses)) 402 - if restSize > 0 { 463 + if restSize > 0 && s.DumpView != nil { 403 464 coursesDump, err := s.DumpView.GetCoursesLatest(state, param.Offset, restSize) 404 465 if err != nil { 405 466 return courses, []uint32{}, len(courses) != 0, err ··· 464 525 return err 465 526 } 466 527 467 - return s.DB.PostPlayResult(int64(param.CourseId), pid, int(param.Tries), int(param.Time), s.DumpView) 528 + return s.DB.PostPlayResult(int64(param.CourseId), pid, int(param.Tries), int(param.Time)) 468 529 } 469 530 470 531 func (s *CombinedView) PostRankingInfo(state *user.State, param datastore.PostRankingInfoParam) error { ··· 477 538 return err 478 539 } 479 540 480 - return s.DB.PostRankingInfo(int64(param.CourseId), pid, param.Unk2, s.DumpView) 541 + return s.DB.PostRankingInfo(int64(param.CourseId), pid, param.Unk2) 481 542 } 482 543 483 544 func (s *CombinedView) CheckAuth(authToken, authTokenHash string) bool { ··· 639 700 return []datastore.CourseInfo{}, err 640 701 } 641 702 642 - return s.DB.GetEndlessCourses(pid, int(param.Difficulty), int(param.Count), s.DumpView) 703 + return s.DB.GetEndlessCourses(pid, int(param.Difficulty), int(param.Count)) 643 704 } 644 705 645 706 func (s *CombinedView) GetWorldMapProgress(state *user.State, param datastore.GetWorldMapProgressParam) (datastore.WorldMapProgressInfo, error) { ··· 683 744 switch param.Type { 684 745 case "played_by": 685 746 if util.OurPids(param.Pids) { 686 - worlds, err := s.DB.GetWorldsPlayedBy(datastore.Pid(state.Pid), param.Pids, param.Offset, param.Size, s.DumpView) 747 + worlds, err := s.DB.GetWorldsPlayedBy(datastore.Pid(state.Pid), param.Pids, param.Offset, param.Size) 687 748 return worlds, []uint32{}, len(worlds) != 0, err 688 749 } 689 750 case "random": 690 - if s.Config.UseDumpForSearches { 751 + if s.Config.UseDumpForSearches && s.DumpView != nil { 691 752 worlds, err := s.DumpView.GetWorldsRandom(state, param.Size) 692 753 return worlds, []uint32{}, len(worlds) != 0, err 693 754 } else { 694 - worlds, err := s.DB.GetWorldsRandom(datastore.Pid(state.Pid), param.Size, s.DumpView) 755 + worlds, err := s.DB.GetWorldsRandom(datastore.Pid(state.Pid), param.Size) 695 756 return worlds, []uint32{}, len(worlds) != 0, err 696 757 } 697 758 default: ··· 707 768 return []datastore.WorldMapInfo{}, err 708 769 } 709 770 710 - return s.DB.GetWorldMapByIdsInterleave(pid, ids, s.DumpView) 771 + return s.DB.GetWorldMapByIdsInterleave(pid, ids) 711 772 } 712 773 713 774 func (s *CombinedView) RegisterWorldMap(state *user.State, param datastore.RegisterWorldMapParam) error {
+10
db/view/dump_view/view.go
··· 434 434 func (s *DumpView) SearchUsersEndlessMode(state *user.State, param datastore.SearchUsersEndlessModeParam) ([]datastore.UserInfo, []uint32, bool, error) { 435 435 return []datastore.UserInfo{}, []uint32{}, false, nil 436 436 } 437 + 438 + func (s *DumpView) GetCourseInfoAtOffset(offset int64) (datastore.CourseInfo, error) { 439 + dumpCourseInfo := dump.CourseInfo{} 440 + err := s.DumpConn.QueryRowx("SELECT * FROM level ORDER BY data_id LIMIT -1 OFFSET ?", offset).StructScan(&dumpCourseInfo) 441 + if err != nil { 442 + return datastore.CourseInfo{}, err 443 + } 444 + info := dump.DatastoreFromCourseInfo(dumpCourseInfo) 445 + return info, nil 446 + }