mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-02-05 09:42:56 +01:00
partially implement friend list functionality
This commit is contained in:
@@ -165,7 +165,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
panic(err)
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -198,7 +198,7 @@ func logoutPlayer(s *Session) {
|
|||||||
delete(s.server.sessions, s.rawConn)
|
delete(s.server.sessions, s.rawConn)
|
||||||
s.rawConn.Close()
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
|||||||
|
|
||||||
data := makeSv2Resp(s.erupeConfig.Entrance.Entries, s)
|
data := makeSv2Resp(s.erupeConfig.Entrance.Entries, s)
|
||||||
if len(pkt) > 5 {
|
if len(pkt) > 5 {
|
||||||
data = append(data, makeUsrResp(pkt)...)
|
data = append(data, makeUsrResp(pkt, s)...)
|
||||||
}
|
}
|
||||||
cc.SendPacket(data)
|
cc.SendPacket(data)
|
||||||
// Close because we only need to send the response once.
|
// Close because we only need to send the response once.
|
||||||
|
|||||||
@@ -88,24 +88,23 @@ func makeSv2Resp(servers []config.EntranceServerInfo, s *Server) []byte {
|
|||||||
return bf.Data()
|
return bf.Data()
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeUsrResp(pkt []byte) []byte {
|
func makeUsrResp(pkt []byte, s *Server) []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?
|
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt)
|
bf := byteframe.NewByteFrameFromBytes(pkt)
|
||||||
_ = bf.ReadUint32() // ALL+
|
_ = bf.ReadUint32() // ALL+
|
||||||
_ = bf.ReadUint8() // 0x00
|
_ = bf.ReadUint8() // 0x00
|
||||||
|
|
||||||
userEntries := bf.ReadUint16()
|
userEntries := bf.ReadUint16()
|
||||||
// actual process will be reading all ids and returning real server, just returning all in server 1 for now
|
resp := byteframe.NewByteFrame()
|
||||||
bf = byteframe.NewByteFrame()
|
|
||||||
for i := 0; i < int(userEntries); i++ {
|
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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package signserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
@@ -87,13 +88,60 @@ type character struct {
|
|||||||
|
|
||||||
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
||||||
characters := []character{}
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return characters, nil
|
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 {
|
func (s *Server) deleteCharacter(cid int, token string) error {
|
||||||
var verify int
|
var verify int
|
||||||
err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE token = $1", token).Scan(&verify)
|
err := s.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE token = $1", token).Scan(&verify)
|
||||||
|
|||||||
@@ -46,10 +46,14 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
bf.WriteUint8(uint8(len(chars))) // character count
|
bf.WriteUint8(uint8(len(chars))) // character count
|
||||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||||
bf.WriteBytes([]byte(token)) // login_token
|
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)
|
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false)
|
||||||
|
|
||||||
|
lastPlayed := uint32(0)
|
||||||
for _, char := range chars {
|
for _, char := range chars {
|
||||||
|
if lastPlayed == 0 {
|
||||||
|
lastPlayed = char.ID
|
||||||
|
}
|
||||||
bf.WriteUint32(char.ID)
|
bf.WriteUint32(char.ID)
|
||||||
|
|
||||||
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
// 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.WriteUint16(0) // Unk
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint8(0) // friends_list_count
|
friends, err := s.server.getFriendsForCharacter(lastPlayed)
|
||||||
bf.WriteUint8(0) // guild_members_count
|
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
|
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."
|
// 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)
|
// ps.Uint32(bf, noticeText, true)
|
||||||
|
|
||||||
bf.WriteUint32(0) // some_last_played_character_id
|
bf.WriteUint32(lastPlayed) // last played character id
|
||||||
bf.WriteUint32(14) // unk_flags
|
bf.WriteUint32(14) // course bitfield
|
||||||
ps.Uint16(bf, "", false) // filters
|
ps.Uint16(bf, "", false) // filters
|
||||||
bf.WriteUint32(0xCA104E20)
|
bf.WriteUint32(0xCA104E20)
|
||||||
ps.Uint16(bf, "", false) // encryption
|
ps.Uint16(bf, "", false) // encryption
|
||||||
bf.WriteUint8(0x00)
|
bf.WriteUint8(0x00)
|
||||||
bf.WriteUint32(0xCA110001)
|
bf.WriteUint32(0xCA110001)
|
||||||
bf.WriteUint32(0x4E200000)
|
bf.WriteUint32(0x4E200000)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ DROP TABLE IF EXISTS public.sign_sessions;
|
|||||||
CREATE TABLE IF NOT EXISTS public.sign_sessions
|
CREATE TABLE IF NOT EXISTS public.sign_sessions
|
||||||
(
|
(
|
||||||
user_id int NOT NULL,
|
user_id int NOT NULL,
|
||||||
|
char_id int,
|
||||||
token varchar(16) NOT NULL,
|
token varchar(16) NOT NULL,
|
||||||
server_id integer
|
server_id integer
|
||||||
);
|
);
|
||||||
@@ -19,4 +20,7 @@ CREATE TABLE IF NOT EXISTS public.servers
|
|||||||
ALTER TABLE IF EXISTS public.characters
|
ALTER TABLE IF EXISTS public.characters
|
||||||
ADD COLUMN deleted boolean NOT NULL DEFAULT false;
|
ADD COLUMN deleted boolean NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.characters
|
||||||
|
ADD COLUMN friends text NOT NULL DEFAULT '';
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|||||||
Reference in New Issue
Block a user