mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 08:55:31 +01:00
@@ -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
|
||||||
|
|||||||
@@ -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
5
patch-schema/psn-id.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS psn_id TEXT;
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user