diff --git a/Erupe/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index c558c75e6..60a69b4be 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -165,7 +165,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { panic(err) } - _, err = s.server.db.Exec("UPDATE sign_sessions SET server_id=$1 WHERE token=$2", s.server.ID, s.token) + _, err = s.server.db.Exec("UPDATE sign_sessions SET server_id=$1, char_id=$2 WHERE token=$3", s.server.ID, s.charID, s.token) if err != nil { panic(err) } @@ -198,7 +198,7 @@ func logoutPlayer(s *Session) { delete(s.server.sessions, s.rawConn) s.rawConn.Close() - _, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL WHERE token=$1", s.token) + _, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token) if err != nil { panic(err) } diff --git a/Erupe/server/entranceserver/entrance_server.go b/Erupe/server/entranceserver/entrance_server.go index 74143fc67..8b6eb0ed5 100644 --- a/Erupe/server/entranceserver/entrance_server.go +++ b/Erupe/server/entranceserver/entrance_server.go @@ -113,7 +113,7 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) { data := makeSv2Resp(s.erupeConfig.Entrance.Entries, s) if len(pkt) > 5 { - data = append(data, makeUsrResp(pkt)...) + data = append(data, makeUsrResp(pkt, s)...) } cc.SendPacket(data) // Close because we only need to send the response once. diff --git a/Erupe/server/entranceserver/make_resp.go b/Erupe/server/entranceserver/make_resp.go index 55517deca..3579be57e 100644 --- a/Erupe/server/entranceserver/make_resp.go +++ b/Erupe/server/entranceserver/make_resp.go @@ -88,24 +88,23 @@ func makeSv2Resp(servers []config.EntranceServerInfo, s *Server) []byte { return bf.Data() } -func makeUsrResp(pkt []byte) []byte { - // TODO(Andoryuuta): Figure out what this user data is. - // Is it for the friends list at the world selection screen? - // If so, how does it work without the entrance server connection being authenticated? - - // uint16 for number of requested ids - // uint32 for each id - // response seems to be server number starting from 10 10 00 00 for server 1 channel 1? +func makeUsrResp(pkt []byte, s *Server) []byte { bf := byteframe.NewByteFrameFromBytes(pkt) _ = bf.ReadUint32() // ALL+ _ = bf.ReadUint8() // 0x00 - userEntries := bf.ReadUint16() - // actual process will be reading all ids and returning real server, just returning all in server 1 for now - bf = byteframe.NewByteFrame() + resp := byteframe.NewByteFrame() for i := 0; i < int(userEntries); i++ { - bf.WriteBytes([]byte{0x10, 0x10, 0x00, 0x00}) + cid := bf.ReadUint32() + var sid uint16 + err := s.db.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid) + if err != nil { + resp.WriteBytes(make([]byte, 4)) + continue + } else { + resp.WriteUint16(sid) + resp.WriteUint16(0) + } } - return makeHeader(bf.Data(), "USR", userEntries, 0x00) - + return makeHeader(resp.Data(), "USR", userEntries, 0x00) } diff --git a/Erupe/server/signserver/dbutils.go b/Erupe/server/signserver/dbutils.go index 79d85ebd5..3cadaefbc 100644 --- a/Erupe/server/signserver/dbutils.go +++ b/Erupe/server/signserver/dbutils.go @@ -2,6 +2,7 @@ package signserver import ( "time" + "strings" "golang.org/x/crypto/bcrypt" ) @@ -87,13 +88,60 @@ type character struct { func (s *Server) getCharactersForUser(uid int) ([]character, error) { characters := []character{} - err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false", uid) + err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY last_login DESC", uid) if err != nil { return nil, err } return characters, nil } +type members struct { + ID uint32 `db:"id"` + Name string `db:"name"` +} + +func (s *Server) getFriendsForCharacter(cid uint32) ([]members, error) { + friends := []members{} + var friendsCSV string + err := s.db.QueryRow("SELECT friends FROM characters WHERE id=$1", cid).Scan(&friendsCSV) + friendsSlice := strings.Split(friendsCSV, ",") + if friendsSlice[0] == "" { + return nil, nil + } + friendQuery := "SELECT id, name FROM characters WHERE id=" + for i := 0; i < len(friendsSlice); i++ { + friendQuery += friendsSlice[i] + if i + 1 != len(friendsSlice) { + friendQuery += " OR id=" + } + } + err = s.db.Select(&friends, friendQuery) + if err != nil { + return nil, err + } + return friends, nil +} + +func (s *Server) getGuildmatesForCharacter(cid uint32) ([]members, error) { + guildmates := []members{} + var inGuild int + _ = s.db.QueryRow("SELECT count(*) FROM guild_characters WHERE character_id=$1", cid).Scan(&inGuild) + if inGuild > 0 { + var guildID int + err := s.db.QueryRow("SELECT guild_id FROM guild_characters WHERE character_id=$1", cid).Scan(&guildID) + if err != nil { + return nil, err + } + err = s.db.Select(&guildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, cid) + if err != nil { + return nil, err + } + return guildmates, nil + } else { + return nil, nil + } +} + func (s *Server) deleteCharacter(cid int, token string) error { var verify int err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE token = $1", token).Scan(&verify) diff --git a/Erupe/server/signserver/dsgn_resp.go b/Erupe/server/signserver/dsgn_resp.go index 4a817702d..2b55b6fd4 100644 --- a/Erupe/server/signserver/dsgn_resp.go +++ b/Erupe/server/signserver/dsgn_resp.go @@ -46,10 +46,14 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint8(uint8(len(chars))) // character count bf.WriteUint32(0xFFFFFFFF) // login_token_number bf.WriteBytes([]byte(token)) // login_token - bf.WriteUint32(uint32(time.Now().Unix())) // unk timestamp + bf.WriteUint32(uint32(time.Now().Unix())) // current time ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false) + lastPlayed := uint32(0) for _, char := range chars { + if lastPlayed == 0 { + lastPlayed = char.ID + } bf.WriteUint32(char.ID) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 @@ -71,18 +75,39 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint16(0) // Unk } - bf.WriteUint8(0) // friends_list_count - bf.WriteUint8(0) // guild_members_count + friends, err := s.server.getFriendsForCharacter(lastPlayed) + if err != nil || friends == nil { + bf.WriteUint8(0) + } else { + bf.WriteUint8(uint8(len(friends))) + for _, friend := range friends { + bf.WriteUint32(lastPlayed) + bf.WriteUint32(friend.ID) + ps.Uint8(bf, friend.Name, true) + } + } + + guildmates, err := s.server.getGuildmatesForCharacter(lastPlayed) + if err != nil || guildmates == nil { + bf.WriteUint8(0) + } else { + for _, guildmate := range guildmates { + bf.WriteUint32(lastPlayed) + bf.WriteUint32(guildmate.ID) + ps.Uint8(bf, guildmate.Name, true) + } + } + bf.WriteUint8(0) // notice_count // noticeText := "