package main // todo: make a consistent "player" type and have these functions return it // todo: dont manually patch column name into various internal funcs (would make the extra funcs unnecessary) import ( "database/sql" "errors" "io/fs" "os" ) func _playerExistsBy(query string, value any) (bool, error) { sqlStatement := ` SELECT username FROM users WHERE ` + query + ` = ?; ` var x string err := DB.QueryRow(sqlStatement, value).Scan(&x) if err != nil { if err == sql.ErrNoRows { return false, nil } return false, err } return true, nil } func playerExistsByUUID(uuid string) (bool, error) { return _playerExistsBy("uuid", uuid) } func playerExistsByUsername(username string) (bool, error) { return _playerExistsBy("username", username) } func _getPlayerBy(query string, value any) (PlayerData, error) { // todo: make this function less repetitive if possible sqlStatement := ` SELECT id, username, password, uuid, client_token, auth_token, web_token, created_at FROM users WHERE ` + query + ` = ?; ` var player PlayerData err := DB.QueryRow(sqlStatement, value).Scan( &player.Id, &player.Username, &player.Password, &player.UUID, &player.ClientToken, &player.AuthToken, &player.WebToken, &player.CreatedAt, ) if err != nil { if err == sql.ErrNoRows { return player, nil } return player, err } return player, nil } func getPlayerByUUID(uuid string) (PlayerData, error) { return _getPlayerBy("uuid", uuid) } func getPlayerByUsername(username string) (PlayerData, error) { return _getPlayerBy("username", username) } func getPlayerByAuthToken(auth string) (PlayerData, error) { return _getPlayerBy("auth_token", auth) } // todo: dedupe skin and cape functions if possible func getPlayerTexture(textureId string, uuid string) ([]byte, error) { if !(textureId == "skin" || textureId == "cape") { return []byte{}, &InvalidDataError{} } tex, err := os.ReadFile(textureId + "s/" + uuid + ".png") if err != nil { if errors.Is(err, fs.ErrNotExist) { return []byte{}, &NotFoundError{} } return []byte{}, err } return tex, nil } func setPlayerTexture(textureId string, uuid string, tex []byte) error { if !(textureId == "skin" || textureId == "cape") { return &InvalidDataError{} } if len(tex) > config.MaxTextureSize { return &TooLargeError{} } exists, err := playerExistsByUUID(uuid) if err != nil { return err } if !exists { return &NotFoundError{} } err = os.WriteFile(textureId+"s/"+uuid+".png", tex, 0644) if err != nil { return err } return nil } func playerHasTexture(textureId string, uuid string) (bool, error) { if !(textureId == "skin" || textureId == "cape") { return false, &InvalidDataError{} } _, err := os.Stat(textureId + "s/" + uuid + ".png") if err != nil { if errors.Is(err, fs.ErrNotExist) { return false, nil } return false, err } return true, nil } func _checkPlayerPassBy(query string, value any, password string) (bool, error) { sqlStatement := ` SELECT password FROM users WHERE ` + query + ` = ?; ` row := DB.QueryRow(sqlStatement, value) var pass string err := row.Scan(&pass) if err != nil { return false, err } if pass != password { return false, nil } return true, nil } func checkPlayerPassByUUID(uuid string, password string) (bool, error) { return _checkPlayerPassBy("uuid", uuid, password) } func checkPlayerPassByUsername(username string, password string) (bool, error) { return _checkPlayerPassBy("username", username, password) }