From a0b50bdf8d58784ca8698316fe3c72f94e268fd6 Mon Sep 17 00:00:00 2001 From: rockisch Date: Wed, 15 Nov 2023 18:33:12 -0300 Subject: [PATCH 1/3] Implement final changes for custom launcher --- config.json | 3 +- config/config.go | 5 +- server/signv2server/dbutils.go | 10 ++ server/signv2server/endpoints.go | 176 +++++++++++++++++++++++---- server/signv2server/signv2_server.go | 1 + 5 files changed, 166 insertions(+), 29 deletions(-) diff --git a/config.json b/config.json index e3e2b7ea4..93ea5aec4 100644 --- a/config.json +++ b/config.json @@ -133,7 +133,8 @@ }, "SignV2": { "Enabled": false, - "Port": 8080 + "Port": 8080, + "PatchServer": "" }, "Channel": { "Enabled": true diff --git a/config/config.go b/config/config.go index ee0e6b377..a9103e6b7 100644 --- a/config/config.go +++ b/config/config.go @@ -195,8 +195,9 @@ type Sign struct { // SignV2 holds the new sign server config type SignV2 struct { - Enabled bool - Port int + Enabled bool + Port int + PatchServer string } type Channel struct { diff --git a/server/signv2server/dbutils.go b/server/signv2server/dbutils.go index fb9711da9..dde729ac9 100644 --- a/server/signv2server/dbutils.go +++ b/server/signv2server/dbutils.go @@ -122,3 +122,13 @@ func (s *Server) getReturnExpiry(uid uint32) time.Time { s.db.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid) return returnExpiry } + +func (s *Server) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) { + row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid) + result := make(map[string]interface{}) + err := row.MapScan(result) + if err != nil { + return nil, err + } + return result, nil +} diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index c087a70df..9ca0488f3 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -14,15 +14,33 @@ import ( "golang.org/x/crypto/bcrypt" ) +const ( + NotificationDefault = iota + NotificationNew +) + +type LauncherBanner struct { + Src string `json:"src"` + Link string `json:"link"` +} + type LauncherMessage struct { Message string `json:"message"` Date int64 `json:"date"` Link string `json:"link"` + Kind int `json:"kind"` +} + +type LauncherLink struct { + Name string `json:"name"` + Link string `json:"link"` + Icon string `json:"icon"` } type LauncherResponse struct { - Important []LauncherMessage `json:"important"` - Normal []LauncherMessage `json:"normal"` + Banners []LauncherBanner `json:"banners"` + Messages []LauncherMessage `json:"messages"` + Links []LauncherLink `json:"links"` } type User struct { @@ -37,7 +55,7 @@ type Character struct { Weapon uint32 `json:"weapon" db:"weapon_type"` HR uint32 `json:"hr" db:"hrp"` GR uint32 `json:"gr"` - LastLogin int64 `json:"lastLogin" db:"last_login"` + LastLogin int32 `json:"lastLogin" db:"last_login"` } type MezFes struct { @@ -53,10 +71,11 @@ type AuthData struct { CurrentTS uint32 `json:"currentTs"` ExpiryTS uint32 `json:"expiryTs"` EntranceCount uint32 `json:"entranceCount"` - Notifications []string `json:"notifications"` + Notices []string `json:"notices"` User User `json:"user"` Characters []Character `json:"characters"` MezFes *MezFes `json:"mezFes"` + PatchServer string `json:"patchServer"` } func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, characters []Character) AuthData { @@ -68,7 +87,14 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, Rights: userRights, Token: userToken, }, - Characters: characters, + Characters: characters, + PatchServer: s.erupeConfig.SignV2.PatchServer, + Notices: []string{}, + } + if s.erupeConfig.DevModeOptions.MaxLauncherHR { + for i := range resp.Characters { + resp.Characters[i].HR = 7 + } } if s.erupeConfig.DevModeOptions.MezFesEvent { stalls := []uint32{10, 3, 6, 9, 4, 8, 5, 7} @@ -85,38 +111,102 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, } } if !s.erupeConfig.HideLoginNotice { - resp.Notifications = append(resp.Notifications, strings.Join(s.erupeConfig.LoginNotices[:], "")) + resp.Notices = append(resp.Notices, strings.Join(s.erupeConfig.LoginNotices[:], "")) } return resp } func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) { var respData LauncherResponse - respData.Important = []LauncherMessage{ + respData.Banners = []LauncherBanner{ { - Message: "Server Update 9 Released!", - Date: time.Date(2022, 8, 2, 0, 0, 0, 0, time.UTC).Unix(), + Src: "http://zerulight.cc/launcher/en/images/bnr/1030_0.jpg", + Link: "http://localhost", + }, + { + Src: "http://zerulight.cc/launcher/en/images/bnr/0801_3.jpg", + Link: "http://localhost", + }, + { + Src: "http://zerulight.cc/launcher/en/images/bnr/0705_3.jpg", + Link: "http://localhost", + }, + { + Src: "http://zerulight.cc/launcher/en/images/bnr/1211_11.jpg", + Link: "http://localhost", + }, + { + Src: "http://zerulight.cc/launcher/en/images/bnr/reg_mezefes.jpg", + Link: "http://localhost", + }, + } + respData.Messages = []LauncherMessage{ + { + Message: "Server Update 9.2 — Quest fixes,\nGacha support and tons of bug fixes!", + Date: time.Date(2023, 4, 1, 0, 0, 0, 0, time.UTC).Unix(), Link: "https://discord.com/channels/368424389416583169/929509970624532511/1003985850255818762", + Kind: NotificationNew, }, { - Message: "Eng 2.0 & Ravi Patch Released!", - Date: time.Date(2022, 5, 3, 0, 0, 0, 0, time.UTC).Unix(), + Message: "English Patch 4.1 — Fix \"Unknown\" weapons, NPC changes & Diva Support.", + Date: time.Date(2023, 2, 27, 0, 0, 0, 0, time.UTC).Unix(), Link: "https://discord.com/channels/368424389416583169/929509970624532511/969305400795078656", + Kind: NotificationNew, }, { - Message: "Launcher Patch V1.0 Released!", - Date: time.Date(2022, 4, 24, 0, 0, 0, 0, time.UTC).Unix(), + Message: "Server Update 9.1! Hunter Festival, Return worlds and NetCafe are back!", + Date: time.Date(2022, 11, 4, 0, 0, 0, 0, time.UTC).Unix(), Link: "https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050", + Kind: NotificationDefault, }, - } - respData.Normal = []LauncherMessage{ { - Message: "Join the community Discord for updates!", - Date: time.Date(2022, 4, 24, 0, 0, 0, 0, time.UTC).Unix(), + Message: "Deerby & Supream have been updating Ferias! You can find any and all MHF info/data there!", + Date: time.Date(2022, 7, 7, 0, 0, 0, 0, time.UTC).Unix(), Link: "https://discord.gg/CFnzbhQ", + Kind: NotificationDefault, + }, + { + Message: "Server hosts, get Chakratos' Save Manager! Use it to enhance your Erupe server!", + Date: time.Date(2022, 7, 7, 0, 0, 0, 0, time.UTC).Unix(), + Link: "https://discord.gg/CFnzbhQ", + Kind: NotificationDefault, + }, + { + Message: "Server Update 9.0 is out! Enjoy MezFes and all the other new content!", + Date: time.Date(2022, 8, 2, 0, 0, 0, 0, time.UTC).Unix(), + Link: "https://discord.gg/CFnzbhQ", + Kind: NotificationDefault, + }, + { + Message: "English Community Translation 2 is here! Get the latest translation patch!", + Date: time.Date(2022, 5, 4, 0, 0, 0, 0, time.UTC).Unix(), + Link: "https://discord.gg/CFnzbhQ", + Kind: NotificationDefault, + }, + { + Message: "Join the community Discord for future updates!", + Date: time.Date(2022, 5, 4, 0, 0, 0, 0, time.UTC).Unix(), + Link: "https://discord.gg/CFnzbhQ", + Kind: NotificationDefault, + }, + } + respData.Links = []LauncherLink{ + { + Name: "GitHub", + Link: "https://github.com/ZeruLight/Erupe", + Icon: "https://cdn-icons-png.flaticon.com/512/25/25231.png", + }, + { + Name: "Discord", + Link: "https://discord.gg/DnwcpXM488", + Icon: "https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png", + }, + { + Name: "Equal Dragon Weapon Info", + Link: "https://discord.gg/DnwcpXM488", + Icon: "", }, } - w.WriteHeader(200) w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -130,7 +220,6 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } var ( @@ -141,7 +230,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { err := s.db.QueryRow("SELECT id, password, rights FROM users WHERE username = $1", reqData.Username).Scan(&userID, &password, &userRights) if err == sql.ErrNoRows { w.WriteHeader(400) - w.Write([]byte("Username does not exist")) + w.Write([]byte("username-error")) return } else if err != nil { s.logger.Warn("SQL query error", zap.Error(err)) @@ -150,7 +239,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { } if bcrypt.CompareHashAndPassword([]byte(password), []byte(reqData.Password)) != nil { w.WriteHeader(400) - w.Write([]byte("Your password is incorrect")) + w.Write([]byte("password-error")) return } @@ -166,8 +255,10 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) return } + if characters == nil { + characters = []Character{} + } respData := s.newAuthData(userID, userRights, userToken, characters) - w.WriteHeader(200) w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -181,7 +272,10 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) + return + } + if reqData.Username == "" || reqData.Password == "" { + w.WriteHeader(400) return } s.logger.Info("Creating account", zap.String("username", reqData.Username)) @@ -190,7 +284,7 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { var pqErr *pq.Error if errors.As(err, &pqErr) && pqErr.Constraint == "users_username_key" { w.WriteHeader(400) - w.Write([]byte("User already exists")) + w.Write([]byte("username-exists-error")) return } s.logger.Error("Error checking user", zap.Error(err), zap.String("username", reqData.Username)) @@ -205,6 +299,7 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) { return } respData := s.newAuthData(userID, userRights, userToken, []Character{}) + w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) } @@ -216,7 +311,6 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } @@ -231,6 +325,10 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) return } + if s.erupeConfig.DevModeOptions.MaxLauncherHR { + character.HR = 7 + } + w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(character) } @@ -243,7 +341,6 @@ func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { s.logger.Error("JSON decode error", zap.Error(err)) w.WriteHeader(400) - w.Write([]byte("Invalid data received")) return } userID, err := s.userIDFromToken(ctx, reqData.Token) @@ -256,5 +353,32 @@ func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) return } + w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(struct{}{}) } + +func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + var reqData struct { + Token string `json:"token"` + CharID uint32 `json:"charId"` + } + if err := json.NewDecoder(r.Body).Decode(&reqData); err != nil { + s.logger.Error("JSON decode error", zap.Error(err)) + w.WriteHeader(400) + return + } + userID, err := s.userIDFromToken(ctx, reqData.Token) + if err != nil { + w.WriteHeader(401) + return + } + save, err := s.exportSave(ctx, userID, reqData.CharID) + if err != nil { + s.logger.Error("Failed to export save", zap.Error(err), zap.String("token", reqData.Token), zap.Uint32("charID", reqData.CharID)) + w.WriteHeader(500) + return + } + w.Header().Add("Content-Type", "application/json") + json.NewEncoder(w).Encode(save) +} diff --git a/server/signv2server/signv2_server.go b/server/signv2server/signv2_server.go index c6db00b09..fedbabba2 100644 --- a/server/signv2server/signv2_server.go +++ b/server/signv2server/signv2_server.go @@ -51,6 +51,7 @@ func (s *Server) Start() error { r.HandleFunc("/register", s.Register) r.HandleFunc("/character/create", s.CreateCharacter) r.HandleFunc("/character/delete", s.DeleteCharacter) + r.HandleFunc("/character/export", s.ExportSave) handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r) s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler) s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port) From 2481d4871fda474eb20f3bbfac9fe95cc23d9284 Mon Sep 17 00:00:00 2001 From: rockisch Date: Thu, 23 Nov 2023 20:54:27 -0300 Subject: [PATCH 2/3] Ensure save export can be changed in the future --- server/signv2server/endpoints.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index 9ca0488f3..a2ab3b151 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -78,6 +78,10 @@ type AuthData struct { PatchServer string `json:"patchServer"` } +type ExportData struct { + Character map[string]interface{} `json:"character"` +} + func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, characters []Character) AuthData { resp := AuthData{ CurrentTS: uint32(channelserver.TimeAdjusted().Unix()), @@ -373,12 +377,15 @@ func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) { w.WriteHeader(401) return } - save, err := s.exportSave(ctx, userID, reqData.CharID) + character, err := s.exportSave(ctx, userID, reqData.CharID) if err != nil { s.logger.Error("Failed to export save", zap.Error(err), zap.String("token", reqData.Token), zap.Uint32("charID", reqData.CharID)) w.WriteHeader(500) return } + save := ExportData{ + Character: character, + } w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(save) } From 0481b15b9bb11e5929623a170a99be96092c877d Mon Sep 17 00:00:00 2001 From: rockisch Date: Thu, 23 Nov 2023 22:12:36 -0300 Subject: [PATCH 3/3] Allow signv2 response to be configured --- config.json | 5 +- config/config.go | 21 ++++ server/channelserver/sys_channel_server.go | 2 +- server/signv2server/endpoints.go | 118 ++------------------- 4 files changed, 33 insertions(+), 113 deletions(-) diff --git a/config.json b/config.json index 93ea5aec4..9fcb4ef68 100644 --- a/config.json +++ b/config.json @@ -134,7 +134,10 @@ "SignV2": { "Enabled": false, "Port": 8080, - "PatchServer": "" + "PatchServer": "", + "Banners": [], + "Messages": [], + "Links": [] }, "Channel": { "Enabled": true diff --git a/config/config.go b/config/config.go index a9103e6b7..e992d6594 100644 --- a/config/config.go +++ b/config/config.go @@ -198,6 +198,27 @@ type SignV2 struct { Enabled bool Port int PatchServer string + Banners []SignV2Banner + Messages []SignV2Message + Links []SignV2Link +} + +type SignV2Banner struct { + Src string `json:"src"` // Displayed image URL + Link string `json:"link"` // Link accessed on click +} + +type SignV2Message struct { + Message string `json:"message"` // Displayed message + Date int64 `json:"date"` // Displayed date + Kind int `json:"kind"` // 0 for 'Default', 1 for 'New' + Link string `json:"link"` // Link accessed on click +} + +type SignV2Link struct { + Name string `json:"name"` // Displayed name + Icon string `json:"icon"` // Displayed icon. It will be cast as a monochrome color as long as it is transparent. + Link string `json:"link"` // Link accessed on click } type Channel struct { diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index d12d713f5..70f52e461 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -9,7 +9,7 @@ import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" - "erupe-ce/config" + _config "erupe-ce/config" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "erupe-ce/server/discordbot" diff --git a/server/signv2server/endpoints.go b/server/signv2server/endpoints.go index a2ab3b151..cde28591c 100644 --- a/server/signv2server/endpoints.go +++ b/server/signv2server/endpoints.go @@ -4,10 +4,10 @@ import ( "database/sql" "encoding/json" "errors" + _config "erupe-ce/config" "erupe-ce/server/channelserver" "net/http" "strings" - "time" "github.com/lib/pq" "go.uber.org/zap" @@ -19,28 +19,10 @@ const ( NotificationNew ) -type LauncherBanner struct { - Src string `json:"src"` - Link string `json:"link"` -} - -type LauncherMessage struct { - Message string `json:"message"` - Date int64 `json:"date"` - Link string `json:"link"` - Kind int `json:"kind"` -} - -type LauncherLink struct { - Name string `json:"name"` - Link string `json:"link"` - Icon string `json:"icon"` -} - type LauncherResponse struct { - Banners []LauncherBanner `json:"banners"` - Messages []LauncherMessage `json:"messages"` - Links []LauncherLink `json:"links"` + Banners []_config.SignV2Banner `json:"banners"` + Messages []_config.SignV2Message `json:"messages"` + Links []_config.SignV2Link `json:"links"` } type User struct { @@ -122,95 +104,9 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) { var respData LauncherResponse - respData.Banners = []LauncherBanner{ - { - Src: "http://zerulight.cc/launcher/en/images/bnr/1030_0.jpg", - Link: "http://localhost", - }, - { - Src: "http://zerulight.cc/launcher/en/images/bnr/0801_3.jpg", - Link: "http://localhost", - }, - { - Src: "http://zerulight.cc/launcher/en/images/bnr/0705_3.jpg", - Link: "http://localhost", - }, - { - Src: "http://zerulight.cc/launcher/en/images/bnr/1211_11.jpg", - Link: "http://localhost", - }, - { - Src: "http://zerulight.cc/launcher/en/images/bnr/reg_mezefes.jpg", - Link: "http://localhost", - }, - } - respData.Messages = []LauncherMessage{ - { - Message: "Server Update 9.2 — Quest fixes,\nGacha support and tons of bug fixes!", - Date: time.Date(2023, 4, 1, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/1003985850255818762", - Kind: NotificationNew, - }, - { - Message: "English Patch 4.1 — Fix \"Unknown\" weapons, NPC changes & Diva Support.", - Date: time.Date(2023, 2, 27, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/969305400795078656", - Kind: NotificationNew, - }, - { - Message: "Server Update 9.1! Hunter Festival, Return worlds and NetCafe are back!", - Date: time.Date(2022, 11, 4, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.com/channels/368424389416583169/929509970624532511/969286397301248050", - Kind: NotificationDefault, - }, - { - Message: "Deerby & Supream have been updating Ferias! You can find any and all MHF info/data there!", - Date: time.Date(2022, 7, 7, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - Kind: NotificationDefault, - }, - { - Message: "Server hosts, get Chakratos' Save Manager! Use it to enhance your Erupe server!", - Date: time.Date(2022, 7, 7, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - Kind: NotificationDefault, - }, - { - Message: "Server Update 9.0 is out! Enjoy MezFes and all the other new content!", - Date: time.Date(2022, 8, 2, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - Kind: NotificationDefault, - }, - { - Message: "English Community Translation 2 is here! Get the latest translation patch!", - Date: time.Date(2022, 5, 4, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - Kind: NotificationDefault, - }, - { - Message: "Join the community Discord for future updates!", - Date: time.Date(2022, 5, 4, 0, 0, 0, 0, time.UTC).Unix(), - Link: "https://discord.gg/CFnzbhQ", - Kind: NotificationDefault, - }, - } - respData.Links = []LauncherLink{ - { - Name: "GitHub", - Link: "https://github.com/ZeruLight/Erupe", - Icon: "https://cdn-icons-png.flaticon.com/512/25/25231.png", - }, - { - Name: "Discord", - Link: "https://discord.gg/DnwcpXM488", - Icon: "https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a6a49cf127bf92de1e2_icon_clyde_blurple_RGB.png", - }, - { - Name: "Equal Dragon Weapon Info", - Link: "https://discord.gg/DnwcpXM488", - Icon: "", - }, - } + respData.Banners = s.erupeConfig.SignV2.Banners + respData.Messages = s.erupeConfig.SignV2.Messages + respData.Links = s.erupeConfig.SignV2.Links w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(respData) }