Merge pull request #66 from ZeruLight/feature/vita

feature/vita
This commit is contained in:
wish
2023-04-15 13:36:49 +10:00
committed by GitHub
8 changed files with 105 additions and 80 deletions

View File

@@ -66,7 +66,7 @@ func CourseExists(ID uint16, c []Course) bool {
// GetCourseStruct returns a slice of Course(s) from a rights integer // GetCourseStruct returns a slice of Course(s) from a rights integer
func GetCourseStruct(rights uint32) ([]Course, uint32) { func GetCourseStruct(rights uint32) ([]Course, uint32) {
resp := []Course{{ID: 1}} resp := []Course{{ID: 1}, {ID: 24}}
s := Courses() s := Courses()
slices.SortStableFunc(s, func(i, j Course) bool { slices.SortStableFunc(s, func(i, j Course) bool {
return i.ID > j.ID return i.ID > j.ID

View File

@@ -72,6 +72,10 @@
"Name": "Course", "Name": "Course",
"Enabled": true, "Enabled": true,
"Prefix": "!course" "Prefix": "!course"
}, {
"Name": "PSN",
"Enabled": true,
"Prefix": "!psn"
} }
], ],
"Courses": [ "Courses": [

5
patch-schema/psn-id.sql Normal file
View File

@@ -0,0 +1,5 @@
BEGIN;
ALTER TABLE users ADD COLUMN IF NOT EXISTS psn_id TEXT;
END;

View File

@@ -82,6 +82,21 @@ func sendServerChatMessage(s *Session, message string) {
} }
func parseChatCommand(s *Session, command string) { func parseChatCommand(s *Session, command string) {
if strings.HasPrefix(command, commands["PSN"].Prefix) {
if commands["PSN"].Enabled {
var id string
n, err := fmt.Sscanf(command, fmt.Sprintf("%s %%s", commands["PSN"].Prefix), &id)
if err != nil || n != 1 {
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandPSNError"], commands["PSN"].Prefix))
} else {
_, err = s.server.db.Exec(`UPDATE users u SET psn_id=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, id, s.charID)
if err == nil {
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandPSNSuccess"], id))
}
}
}
}
if strings.HasPrefix(command, commands["Reload"].Prefix) { if strings.HasPrefix(command, commands["Reload"].Prefix) {
// Flush all objects and users and reload // Flush all objects and users and reload
if commands["Reload"].Enabled { if commands["Reload"].Enabled {

View File

@@ -20,6 +20,8 @@ func getLangStrings(s *Server) map[string]string {
strings["commandCourseLocked"] = "%sコースはロックされています" strings["commandCourseLocked"] = "%sコースはロックされています"
strings["commandTeleportError"] = "テレポートコマンドエラー 構文:%s x y" strings["commandTeleportError"] = "テレポートコマンドエラー 構文:%s x y"
strings["commandTeleportSuccess"] = "%d %dにテレポート" strings["commandTeleportSuccess"] = "%d %dにテレポート"
strings["commandPSNError"] = "PSN連携コマンドエラー %s <psn id>"
strings["commandPSNSuccess"] = "PSN「%s」が連携されています"
strings["commandRaviNoCommand"] = "ラヴィコマンドが指定されていません" strings["commandRaviNoCommand"] = "ラヴィコマンドが指定されていません"
strings["commandRaviStartSuccess"] = "大討伐を開始します" strings["commandRaviStartSuccess"] = "大討伐を開始します"
@@ -68,6 +70,8 @@ func getLangStrings(s *Server) map[string]string {
strings["commandCourseLocked"] = "%s Course is locked" strings["commandCourseLocked"] = "%s Course is locked"
strings["commandTeleportError"] = "Error in command. Format: %s x y" strings["commandTeleportError"] = "Error in command. Format: %s x y"
strings["commandTeleportSuccess"] = "Teleporting to %d %d" strings["commandTeleportSuccess"] = "Teleporting to %d %d"
strings["commandPSNError"] = "Error in command. Format: %s <psn id>"
strings["commandPSNSuccess"] = "Connected PSN ID: %s"
strings["commandRaviNoCommand"] = "No Raviente command specified!" strings["commandRaviNoCommand"] = "No Raviente command specified!"
strings["commandRaviStartSuccess"] = "The Great Slaying will begin in a moment" strings["commandRaviStartSuccess"] = "The Great Slaying will begin in a moment"

View File

@@ -11,15 +11,7 @@ import (
"strings" "strings"
) )
func makeSignInFailureResp(respID RespID) []byte { func (s *Session) makeSignResponse(uid int) []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint8(uint8(respID))
return bf.Data()
}
func (s *Session) makeSignInResp(uid int) []byte {
returnExpiry := s.server.getReturnExpiry(uid)
// Get the characters from the DB. // Get the characters from the DB.
chars, err := s.server.getCharactersForUser(uid) chars, err := s.server.getCharactersForUser(uid)
if err != nil { if err != nil {
@@ -27,7 +19,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
} }
sessToken := token.Generate(16) sessToken := token.Generate(16)
s.server.registerToken(uid, sessToken) _ = s.server.registerToken(uid, sessToken)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
@@ -116,6 +108,11 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getLastCID(uid))
bf.WriteUint32(s.server.getUserRights(uid)) bf.WriteUint32(s.server.getUserRights(uid))
ps.Uint16(bf, "", false) // filters ps.Uint16(bf, "", false) // filters
if s.client == VITA || s.client == PS3 {
var psnUser string
s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
}
bf.WriteUint16(0xCA10) bf.WriteUint16(0xCA10)
bf.WriteUint16(0x4E20) bf.WriteUint16(0x4E20)
ps.Uint16(bf, "", false) // unk key ps.Uint16(bf, "", false) // unk key
@@ -124,7 +121,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint16(0x0001) bf.WriteUint16(0x0001)
bf.WriteUint16(0x4E20) bf.WriteUint16(0x4E20)
ps.Uint16(bf, "", false) // unk ipv4 ps.Uint16(bf, "", false) // unk ipv4
bf.WriteUint32(uint32(returnExpiry.Unix())) bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
bf.WriteUint32(0x00000000) bf.WriteUint32(0x00000000)
bf.WriteUint32(0x0A5197DF) // unk id bf.WriteUint32(0x0A5197DF) // unk id

View File

@@ -1,10 +1,7 @@
package signserver package signserver
//revive:disable type RespID uint8
type RespID uint16
//go:generate stringer -type=RespID
const ( const (
SIGN_UNKNOWN RespID = iota SIGN_UNKNOWN RespID = iota
SIGN_SUCCESS SIGN_SUCCESS

View File

@@ -13,6 +13,14 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
type Client int
const (
PC100 Client = iota
VITA
PS3
)
// Session holds state for the sign server connection. // Session holds state for the sign server connection.
type Session struct { type Session struct {
sync.Mutex sync.Mutex
@@ -20,6 +28,7 @@ type Session struct {
server *Server server *Server
rawConn net.Conn rawConn net.Conn
cryptConn *network.CryptConn cryptConn *network.CryptConn
client Client
} }
func (s *Session) work() { func (s *Session) work() {
@@ -42,91 +51,68 @@ func (s *Session) handlePacket(pkt []byte) error {
bf := byteframe.NewByteFrameFromBytes(pkt) bf := byteframe.NewByteFrameFromBytes(pkt)
reqType := string(bf.ReadNullTerminatedBytes()) reqType := string(bf.ReadNullTerminatedBytes())
switch reqType { switch reqType {
case "DLTSKEYSIGN:100": case "DLTSKEYSIGN:100", "DSGN:100":
fallthrough s.handleDSGN(bf)
case "DSGN:100": case "PS3SGN:100":
err := s.handleDSGNRequest(bf) s.client = PS3
if err != nil { s.handlePSSGN(bf)
return nil case "VITASGN:100":
} s.client = VITA
s.handlePSSGN(bf)
case "DELETE:100": case "DELETE:100":
loginTokenString := string(bf.ReadNullTerminatedBytes()) loginTokenString := string(bf.ReadNullTerminatedBytes())
characterID := int(bf.ReadUint32()) characterID := int(bf.ReadUint32())
_ = int(bf.ReadUint32()) // login_token_number _ = int(bf.ReadUint32()) // login_token_number
s.server.deleteCharacter(characterID, loginTokenString) err := s.server.deleteCharacter(characterID, loginTokenString)
if err == nil {
s.logger.Info("Deleted character", zap.Int("CharacterID", characterID)) s.logger.Info("Deleted character", zap.Int("CharacterID", characterID))
err := s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS
if err != nil {
return nil
} }
default: default:
s.logger.Warn("Unknown sign request", zap.String("reqType", reqType)) s.logger.Warn("Unknown request", zap.String("reqType", reqType))
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt)) fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
} }
} }
return nil return nil
} }
func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { func (s *Session) authenticate(username string, password string) {
reqUsername := string(bf.ReadNullTerminatedBytes())
reqPassword := string(bf.ReadNullTerminatedBytes())
_ = string(bf.ReadNullTerminatedBytes()) // Unk
newCharaReq := false newCharaReq := false
if reqUsername[len(reqUsername)-1] == 43 { // '+' if username[len(username)-1] == 43 { // '+'
reqUsername = reqUsername[:len(reqUsername)-1] username = username[:len(username)-1]
newCharaReq = true newCharaReq = true
} }
var ( var id int
id int var hash string
password string bf := byteframe.NewByteFrame()
)
err := s.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password) err := s.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", username).Scan(&id, &hash)
var serverRespBytes []byte
switch { switch {
case err == sql.ErrNoRows: case err == sql.ErrNoRows:
s.logger.Info("User not found", zap.String("Username", reqUsername)) s.logger.Info("User not found", zap.String("Username", username))
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount {
s.logger.Info("Creating user", zap.String("Username", reqUsername)) s.logger.Info("Creating user", zap.String("Username", username))
err = s.server.registerDBAccount(reqUsername, reqPassword) err = s.server.registerDBAccount(username, password)
if err != nil { if err == nil {
s.logger.Error("Error registering new user", zap.Error(err)) bf.WriteBytes(s.makeSignResponse(id))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
break
} }
} else { } else {
break bf.WriteUint8(uint8(SIGN_EAUTH))
} }
var id int
err = s.server.db.QueryRow("SELECT id FROM users WHERE username = $1", reqUsername).Scan(&id)
if err != nil {
s.logger.Error("Error getting new user ID", zap.Error(err))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
break
}
serverRespBytes = s.makeSignInResp(id)
break
case err != nil: case err != nil:
serverRespBytes = makeSignInFailureResp(SIGN_EABORT) bf.WriteUint8(uint8(SIGN_EABORT))
s.logger.Error("Error getting user details", zap.Error(err)) s.logger.Error("Error getting user details", zap.Error(err))
break
default: default:
if bcrypt.CompareHashAndPassword([]byte(password), []byte(reqPassword)) == nil { if bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil || s.client == VITA || s.client == PS3 {
s.logger.Debug("Passwords match!") s.logger.Debug("Passwords match!")
if newCharaReq { if newCharaReq {
err = s.server.newUserChara(reqUsername) err = s.server.newUserChara(username)
if err != nil { if err != nil {
s.logger.Error("Error adding new character to user", zap.Error(err)) s.logger.Error("Error adding new character to user", zap.Error(err))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT) bf.WriteUint8(uint8(SIGN_EABORT))
break break
} }
} }
@@ -137,22 +123,39 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
// serverRespBytes = makeSignInFailureResp(SIGN_EABORT) // serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
// break // break
// } // }
serverRespBytes = s.makeSignInResp(id) bf.WriteBytes(s.makeSignResponse(id))
} else { } else {
s.logger.Warn("Incorrect password") s.logger.Warn("Incorrect password")
serverRespBytes = makeSignInFailureResp(SIGN_EPASS) bf.WriteUint8(uint8(SIGN_EPASS))
} }
} }
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(serverRespBytes), hex.Dump(serverRespBytes)) fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(bf.Data()), hex.Dump(bf.Data()))
} }
err = s.cryptConn.SendPacket(serverRespBytes) err = s.cryptConn.SendPacket(bf.Data())
if err != nil {
return err
} }
return nil func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
_ = bf.ReadNullTerminatedBytes() // 0000000256
_ = bf.ReadNullTerminatedBytes() // 1
_ = bf.ReadBytes(82)
psnUser := string(bf.ReadNullTerminatedBytes())
var reqUsername string
err := s.server.db.QueryRow(`SELECT username FROM users WHERE psn_id = $1`, psnUser).Scan(&reqUsername)
if err == sql.ErrNoRows {
resp := byteframe.NewByteFrame()
resp.WriteUint8(uint8(SIGN_ECOGLINK))
s.cryptConn.SendPacket(resp.Data())
return
}
s.authenticate(reqUsername, "")
}
func (s *Session) handleDSGN(bf *byteframe.ByteFrame) {
reqUsername := string(bf.ReadNullTerminatedBytes())
reqPassword := string(bf.ReadNullTerminatedBytes())
_ = string(bf.ReadNullTerminatedBytes()) // Unk
s.authenticate(reqUsername, reqPassword)
} }