From bcb71536ec9977725fb55dc1739a58c60d816304 Mon Sep 17 00:00:00 2001 From: wish Date: Sat, 11 Mar 2023 23:17:14 +1100 Subject: [PATCH] initial rights v4 concept --- common/mhfcourse/mhfcourse.go | 99 ++++++++++++++++++++ network/mhfpacket/msg_sys_update_right.go | 90 ++---------------- server/channelserver/handlers.go | 24 +---- server/channelserver/handlers_cafe.go | 3 +- server/channelserver/handlers_cast_binary.go | 29 +++--- server/channelserver/sys_session.go | 15 +-- server/signserver/dbutils.go | 4 +- 7 files changed, 135 insertions(+), 129 deletions(-) create mode 100644 common/mhfcourse/mhfcourse.go diff --git a/common/mhfcourse/mhfcourse.go b/common/mhfcourse/mhfcourse.go new file mode 100644 index 000000000..48b310489 --- /dev/null +++ b/common/mhfcourse/mhfcourse.go @@ -0,0 +1,99 @@ +package mhfcourse + +import ( + "golang.org/x/exp/slices" + "math" + "time" +) + +type Course struct { + ID uint16 + Expiry time.Time +} + +func (c Course) Aliases() []string { + aliases := make(map[uint16][]string) + aliases[1] = []string{"Trial", "TL"} + aliases[2] = []string{"HunterLife", "HL"} + aliases[3] = []string{"Extra", "ExtraA", "EX"} + aliases[4] = []string{"ExtraB"} + aliases[5] = []string{"Mobile"} + aliases[6] = []string{"Premium"} + aliases[7] = []string{"Pallone", "ExtraC"} + aliases[8] = []string{"Assist", "***ist", "Legend", "Rasta"} + aliases[9] = []string{"N"} + aliases[10] = []string{"Hiden", "Secret"} + aliases[11] = []string{"HunterSupport", "HunterAid", "Support", "Aid", "Royal"} + aliases[12] = []string{"NBoost", "NetCafeBoost", "Boost"} + aliases[20] = []string{"DEBUG"} + aliases[21] = []string{"COG_LINK_EXPIRED"} + aliases[22] = []string{"360_GOLD"} + aliases[23] = []string{"PS3_TROP"} + aliases[24] = []string{"COG"} + aliases[25] = []string{"CAFE_SP"} + aliases[26] = []string{"NetCafe", "Cafe", "OfficialCafe", "Official"} + aliases[27] = []string{"HLRenewing", "HLR", "HLRenewal", "HLRenew", "CardHL"} + aliases[28] = []string{"EXRenewing", "EXR", "EXRenewal", "EXRenew", "CardEX"} + aliases[29] = []string{"Free"} + return aliases[c.ID] +} + +func Courses() []Course { + courses := make([]Course, 32) + for i := range courses { + courses[i].ID = uint16(i) + courses[i].Expiry = time.Time{} + } + return courses +} + +func (c Course) Value() uint32 { + return uint32(math.Pow(2, float64(c.ID))) +} + +// CourseExists returns true if the named course exists in the given slice +func CourseExists(ID uint16, c []Course) bool { + for _, course := range c { + if course.ID == ID { + return true + } + } + return false +} + +// GetCourseStruct returns a slice of Course(s) from a rights integer +func GetCourseStruct(rights uint32) ([]Course, uint32) { + resp := []Course{{ID: 1, Expiry: time.Time{}}} + s := Courses() + slices.SortStableFunc(s, func(i, j Course) bool { + return i.ID > j.ID + }) + var normalCafeCourseSet, netcafeCourseSet bool + for _, course := range s { + if rights-course.Value() < 0x80000000 { + switch course.ID { + case 26: + if normalCafeCourseSet { + break + } + normalCafeCourseSet = true + resp = append(resp, Course{ID: 25, Expiry: time.Time{}}) + fallthrough + case 9: + if netcafeCourseSet { + break + } + netcafeCourseSet = true + resp = append(resp, Course{ID: 30, Expiry: time.Time{}}) + } + course.Expiry = time.Date(2030, 1, 1, 0, 0, 0, 0, time.FixedZone("UTC+9", 9*60*60)) + resp = append(resp, course) + rights -= course.Value() + } + } + rights = 0 + for _, course := range resp { + rights += course.Value() + } + return resp, rights +} diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index 8364e37bf..c384deece 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -3,50 +3,17 @@ package mhfpacket import ( "errors" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfcourse" ps "erupe-ce/common/pascalstring" "erupe-ce/network" "erupe-ce/network/clientctx" - "golang.org/x/exp/slices" - "math" ) -/* -00 58 // Opcode - -00 00 00 00 -00 00 00 4e - -00 04 // Count -00 00 // Skipped(padding?) - -00 01 00 00 00 00 00 00 -00 02 00 00 5d fa 14 c0 -00 03 00 00 5d fa 14 c0 -00 06 00 00 5d e7 05 10 - -00 00 // Count of some buf up to 0x800 bytes following it. - -00 10 // Trailer -*/ - -// ClientRight represents a right that the client has. -type ClientRight struct { - ID uint16 - Unk0 uint16 - Timestamp uint32 -} - -type Course struct { - Aliases []string - ID uint16 - Value uint32 -} - // MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT type MsgSysUpdateRight struct { ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value. Bitfield uint32 - Rights []ClientRight + Rights []mhfcourse.Course UnkSize uint16 // Count of some buf up to 0x800 bytes following it. } @@ -68,54 +35,13 @@ func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.Client bf.WriteUint16(0) for _, v := range m.Rights { bf.WriteUint16(v.ID) - bf.WriteUint16(v.Unk0) - bf.WriteUint32(v.Timestamp) + bf.WriteUint16(0) + if v.Expiry.IsZero() { + bf.WriteUint32(0) + } else { + bf.WriteUint32(uint32(v.Expiry.Unix())) + } } ps.Uint16(bf, "", false) // update client login token / password in the game's launcherstate struct return nil } - -func Courses() []Course { - var courses = []Course{ - {Aliases: []string{"Trial", "TL"}, ID: 1}, - {Aliases: []string{"HunterLife", "HL"}, ID: 2}, // BASIC - {Aliases: []string{"Extra", "ExtraA", "EX"}, ID: 3}, - {Aliases: []string{"ExtraB"}, ID: 4}, - {Aliases: []string{"Mobile"}, ID: 5}, - {Aliases: []string{"Premium"}, ID: 6}, - {Aliases: []string{"Pallone", "ExtraC"}, ID: 7}, - {Aliases: []string{"Assist", "Legend", "Rasta"}, ID: 8}, // LEGEND - {Aliases: []string{"N"}, ID: 9}, - {Aliases: []string{"Hiden", "Secret"}, ID: 10}, // SECRET - {Aliases: []string{"HunterSupport", "HunterAid", "Support", "Aid", "Royal"}, ID: 11}, // ROYAL - {Aliases: []string{"NBoost", "NetCafeBoost", "Boost"}, ID: 12}, - // 13-19 = (unknown), 20 = DEBUG, 21 = COG_LINK_EXPIRED, 22 = 360_GOLD, 23 = PS3_TROP - {Aliases: []string{"COG"}, ID: 24}, - // 25 = CAFE_SP, active with 26 but useless on it's own? Just use OFFICIAL and set bit - {Aliases: []string{"NetCafe", "Cafe", "OfficialCafe", "Official"}, ID: 26}, - {Aliases: []string{"HLRenewing", "HLR", "HLRenewal", "HLRenew"}, ID: 27}, // CARD - {Aliases: []string{"EXRenewing", "EXR", "EXRenewal", "EXRenew"}, ID: 28}, // CARD_EX - {Aliases: []string{"Free"}, ID: 29}, - // 30 = NETCAFE, what actually gives you any NetCafe gameplay benefits - } - for i := range courses { - courses[i].Value = uint32(math.Pow(2, float64(courses[i].ID))) - } - return courses -} - -// GetCourseStruct returns a slice of Course(s) from a rights integer -func GetCourseStruct(rights uint32) []Course { - var resp []Course - s := Courses() - slices.SortStableFunc(s, func(i, j Course) bool { - return i.ID > j.ID - }) - for _, course := range s { - if rights-course.Value < 0x80000000 { - resp = append(resp, course) - rights -= course.Value - } - } - return resp -} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 343b12094..d22f8be3b 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -3,6 +3,7 @@ package channelserver import ( "encoding/binary" "encoding/hex" + "erupe-ce/common/mhfcourse" ps "erupe-ce/common/pascalstring" "erupe-ce/common/stringsupport" "fmt" @@ -74,28 +75,13 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) { } func updateRights(s *Session) { - rightsInt := uint32(0x0E) + rightsInt := uint32(2) s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt) - s.courses = mhfpacket.GetCourseStruct(rightsInt) - rights := []mhfpacket.ClientRight{{1, 0, 0}} - var normalCafeBitSet, netcafeBitSet bool - for _, course := range s.courses { - if (course.ID == 9 || course.ID == 26) && !netcafeBitSet { - netcafeBitSet = true - rightsInt += 0x40000000 - rights = append(rights, mhfpacket.ClientRight{ID: 30}) - } - if (course.ID == 26) && !normalCafeBitSet { - normalCafeBitSet = true - rightsInt += 0x2000000 - rights = append(rights, mhfpacket.ClientRight{ID: 25}) - } - rights = append(rights, mhfpacket.ClientRight{ID: course.ID, Timestamp: 0x70DB59F0}) - } + s.courses, rightsInt = mhfcourse.GetCourseStruct(rightsInt) update := &mhfpacket.MsgSysUpdateRight{ ClientRespAckHandle: 0, Bitfield: rightsInt, - Rights: rights, + Rights: s.courses, UnkSize: 0, } s.QueueSendMHF(update) @@ -226,7 +212,7 @@ func logoutPlayer(s *Session) { timePlayed += sessionTime var rpGained int - if s.FindCourse("NetCafe").ID != 0 || s.FindCourse("N").ID != 0 { + if mhfcourse.CourseExists(30, s.courses) { rpGained = timePlayed / 900 timePlayed = timePlayed % 900 s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID) diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 2973472de..46a6425a0 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -2,6 +2,7 @@ package channelserver import ( "erupe-ce/common/byteframe" + "erupe-ce/common/mhfcourse" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" "fmt" @@ -88,7 +89,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { if err != nil { panic(err) } - if s.FindCourse("NetCafe").ID != 0 || s.FindCourse("N").ID != 0 { + if mhfcourse.CourseExists(30, s.courses) { cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime } bf.WriteUint32(cafeTime) // Total cafe time diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 9ef5eec4e..7c4066d1d 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -3,6 +3,7 @@ package channelserver import ( "encoding/hex" "erupe-ce/common/byteframe" + "erupe-ce/common/mhfcourse" "erupe-ce/config" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" @@ -192,13 +193,14 @@ func parseChatCommand(s *Session, command string) { sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseError"], commands["Course"].Prefix)) } else { name = strings.ToLower(name) - for _, course := range mhfpacket.Courses() { - for _, alias := range course.Aliases { + for _, course := range mhfcourse.Courses() { + for _, alias := range course.Aliases() { if strings.ToLower(name) == strings.ToLower(alias) { - if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases[0], Enabled: true}) { - if s.FindCourse(name).ID != 0 { - ei := slices.IndexFunc(s.courses, func(c mhfpacket.Course) bool { - for _, alias := range c.Aliases { + if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases()[0], Enabled: true}) { + var delta, rightsInt uint32 + if mhfcourse.CourseExists(course.ID, s.courses) { + ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool { + for _, alias := range c.Aliases() { if strings.ToLower(name) == strings.ToLower(alias) { return true } @@ -206,21 +208,22 @@ func parseChatCommand(s *Session, command string) { return false }) if ei != -1 { + delta = uint32(-1 * math.Pow(2, float64(course.ID))) s.courses = append(s.courses[:ei], s.courses[ei+1:]...) - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseDisabled"], course.Aliases[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseDisabled"], course.Aliases()[0])) } } else { + delta = uint32(math.Pow(2, float64(course.ID))) s.courses = append(s.courses, course) - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseEnabled"], course.Aliases[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseEnabled"], course.Aliases()[0])) } - var newInt uint32 - for _, course := range s.courses { - newInt += uint32(math.Pow(2, float64(course.ID))) + s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt) + if rightsInt > 0 { + s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", rightsInt+delta, s.charID) } - s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", newInt, s.charID) updateRights(s) } else { - sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseLocked"], course.Aliases[0])) + sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandCourseLocked"], course.Aliases()[0])) } } } diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 0bdf8e335..40d54fdb3 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -3,10 +3,10 @@ package channelserver import ( "encoding/binary" "encoding/hex" + "erupe-ce/common/mhfcourse" "fmt" "io" "net" - "strings" "sync" "time" @@ -43,7 +43,7 @@ type Session struct { charID uint32 logKey []byte sessionStart int64 - courses []mhfpacket.Course + courses []mhfcourse.Course token string kqf []byte kqfOverride bool @@ -268,14 +268,3 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data)) } } - -func (s *Session) FindCourse(name string) mhfpacket.Course { - for _, course := range s.courses { - for _, alias := range course.Aliases { - if strings.ToLower(name) == strings.ToLower(alias) { - return course - } - } - } - return mhfpacket.Course{} -} diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go index 213a74a14..ee4dcc493 100644 --- a/server/signserver/dbutils.go +++ b/server/signserver/dbutils.go @@ -1,6 +1,7 @@ package signserver import ( + "erupe-ce/common/mhfcourse" "strings" "time" @@ -119,8 +120,9 @@ func (s *Server) getLastCID(uid int) uint32 { } func (s *Server) getUserRights(uid int) uint32 { - var rights uint32 + rights := uint32(2) _ = s.db.QueryRow("SELECT rights FROM users WHERE id=$1", uid).Scan(&rights) + _, rights = mhfcourse.GetCourseStruct(rights) return rights }