partially implement friend list functionality

This commit is contained in:
wish
2022-07-17 00:16:43 +10:00
parent 5f833f93a0
commit 70b5fe4eb7
6 changed files with 101 additions and 25 deletions

View File

@@ -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)
}

View File

@@ -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.

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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 := "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you."
// ps.Uint32(bf, noticeText, true)
bf.WriteUint32(0) // some_last_played_character_id
bf.WriteUint32(14) // unk_flags
ps.Uint16(bf, "", false) // filters
bf.WriteUint32(lastPlayed) // last played character id
bf.WriteUint32(14) // course bitfield
ps.Uint16(bf, "", false) // filters
bf.WriteUint32(0xCA104E20)
ps.Uint16(bf, "", false) // encryption
ps.Uint16(bf, "", false) // encryption
bf.WriteUint8(0x00)
bf.WriteUint32(0xCA110001)
bf.WriteUint32(0x4E200000)

View File

@@ -4,6 +4,7 @@ DROP TABLE IF EXISTS public.sign_sessions;
CREATE TABLE IF NOT EXISTS public.sign_sessions
(
user_id int NOT NULL,
char_id int,
token varchar(16) NOT NULL,
server_id integer
);
@@ -19,4 +20,7 @@ CREATE TABLE IF NOT EXISTS public.servers
ALTER TABLE IF EXISTS public.characters
ADD COLUMN deleted boolean NOT NULL DEFAULT false;
ALTER TABLE IF EXISTS public.characters
ADD COLUMN friends text NOT NULL DEFAULT '';
END;