Merge branch 'feature/discord-login' of https://github.com/matthe815/Erupe into feature/discord-login

This commit is contained in:
Matthew
2023-12-04 13:48:32 -05:00
13 changed files with 414 additions and 209 deletions

View File

@@ -82,7 +82,7 @@ const (
BrightHypnoc BrightHypnoc
RedLavasioth RedLavasioth
Espinas Espinas
OrangeEspinas BurningEspinas
WhiteHypnoc WhiteHypnoc
AqraVashimu AqraVashimu
AqraJebia AqraJebia
@@ -91,7 +91,7 @@ const (
Mon87 Mon87
Mon88 Mon88
Pariapuria Pariapuria
WhiteEspinas PearlEspinas
KamuOrugaron KamuOrugaron
NonoOrugaron NonoOrugaron
Raviente Raviente
@@ -267,7 +267,7 @@ var Monsters = []Monster{
{"Bright Hypnocatrice", true}, {"Bright Hypnocatrice", true},
{"Red Lavasioth", true}, {"Red Lavasioth", true},
{"Espinas", true}, {"Espinas", true},
{"Orange Espinas", true}, {"Burning Espinas", true},
{"White Hypnocatrice", true}, {"White Hypnocatrice", true},
{"Aqra Vashimu", true}, {"Aqra Vashimu", true},
{"Aqra Jebia", true}, {"Aqra Jebia", true},
@@ -276,7 +276,7 @@ var Monsters = []Monster{
{"Mon87", false}, {"Mon87", false},
{"Mon88", false}, {"Mon88", false},
{"Pariapuria", true}, {"Pariapuria", true},
{"White Espinas", true}, {"Pearl Espinas", true},
{"Kamu Orugaron", true}, {"Kamu Orugaron", true},
{"Nono Orugaron", true}, {"Nono Orugaron", true},
{"Raviente", true}, // + Violent {"Raviente", true}, // + Violent

View File

@@ -6,6 +6,7 @@ import (
// StringStack is a basic LIFO "stack" for storing strings. // StringStack is a basic LIFO "stack" for storing strings.
type StringStack struct { type StringStack struct {
Locked bool
stack []string stack []string
} }
@@ -19,6 +20,20 @@ func (s *StringStack) Set(v string) {
s.stack = []string{v} s.stack = []string{v}
} }
// Lock freezes the StringStack
func (s *StringStack) Lock() {
if !s.Locked {
s.Locked = true
}
}
// Unlock unfreezes the StringStack
func (s *StringStack) Unlock() {
if s.Locked {
s.Locked = false
}
}
// Push pushes a string onto the stack. // Push pushes a string onto the stack.
func (s *StringStack) Push(v string) { func (s *StringStack) Push(v string) {
s.stack = append(s.stack, v) s.stack = append(s.stack, v)
@@ -26,11 +41,12 @@ func (s *StringStack) Push(v string) {
// Pop pops a string from the stack. // Pop pops a string from the stack.
func (s *StringStack) Pop() (string, error) { func (s *StringStack) Pop() (string, error) {
var x string
if len(s.stack) == 0 { if len(s.stack) == 0 {
return "", errors.New("no items on stack") return x, errors.New("no items on stack")
} }
x := s.stack[len(s.stack)-1] x = s.stack[len(s.stack)-1]
s.stack = s.stack[:len(s.stack)-1] s.stack = s.stack[:len(s.stack)-1]
return x, nil return x, nil

View File

@@ -34,6 +34,7 @@
"EarthMonsterOverride": [0, 0, 0, 0], "EarthMonsterOverride": [0, 0, 0, 0],
"SaveDumps": { "SaveDumps": {
"Enabled": true, "Enabled": true,
"RawEnabled": false,
"OutputDir": "save-backups" "OutputDir": "save-backups"
} }
}, },
@@ -46,7 +47,8 @@
"DisableLoginBoost": false, "DisableLoginBoost": false,
"DisableBoostTime": false, "DisableBoostTime": false,
"BoostTimeDuration": 7200, "BoostTimeDuration": 7200,
"GuildMealDuration": 3600, "ClanMealDuration": 3600,
"ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]],
"BonusQuestAllowance": 3, "BonusQuestAllowance": 3,
"DailyQuestAllowance": 1, "DailyQuestAllowance": 1,
"MezfesSoloTickets": 10, "MezfesSoloTickets": 10,

View File

@@ -118,6 +118,7 @@ type DevModeOptions struct {
type SaveDumpOptions struct { type SaveDumpOptions struct {
Enabled bool Enabled bool
RawEnabled bool
OutputDir string OutputDir string
} }
@@ -132,7 +133,8 @@ type GameplayOptions struct {
DisableLoginBoost bool // Disables the Login Boost system DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time DisableBoostTime bool // Disables the daily NetCafe Boost Time
BoostTimeDuration int // Second that the NetCafe Boost Time lasts BoostTimeDuration int // Second that the NetCafe Boost Time lasts
GuildMealDuration int // Second that a Guild Meal can be activated for after cooking ClanMealDuration int // Second that a Clan Meal can be activated for after cooking
ClanMemberLimits [][]uint8 // Array of maximum Clan Members -> [Rank, Members]
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
MezfesSoloTickets uint32 // Number of solo tickets given weekly MezfesSoloTickets uint32 // Number of solo tickets given weekly

View File

@@ -13,7 +13,6 @@ type MsgMhfOprMember struct {
AckHandle uint32 AckHandle uint32
Blacklist bool Blacklist bool
Operation bool Operation bool
Unk uint16
CharIDs []uint32 CharIDs []uint32
} }

View File

@@ -3,13 +3,15 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfUnreserveSrg represents the MSG_MHF_UNRESERVE_SRG // MsgMhfUnreserveSrg represents the MSG_MHF_UNRESERVE_SRG
type MsgMhfUnreserveSrg struct{} type MsgMhfUnreserveSrg struct {
AckHandle uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfUnreserveSrg) Opcode() network.PacketID { func (m *MsgMhfUnreserveSrg) Opcode() network.PacketID {
@@ -18,7 +20,8 @@ func (m *MsgMhfUnreserveSrg) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfUnreserveSrg) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfUnreserveSrg) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -2,7 +2,6 @@ package channelserver
import ( import (
"encoding/binary" "encoding/binary"
"encoding/hex"
"erupe-ce/common/mhfcourse" "erupe-ce/common/mhfcourse"
"erupe-ce/common/mhfmon" "erupe-ce/common/mhfmon"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
@@ -130,7 +129,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
if !s.server.erupeConfig.DevModeOptions.DisableTokenCheck { if !s.server.erupeConfig.DevModeOptions.DisableTokenCheck {
var token string var token string
err := s.server.db.QueryRow("SELECT token FROM sign_sessions WHERE token=$1", pkt.LoginTokenString).Scan(&token) err := s.server.db.QueryRow("SELECT token FROM sign_sessions ss INNER JOIN public.users u on ss.user_id = u.id WHERE token=$1 AND ss.id=$2 AND u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.LoginTokenString, pkt.LoginTokenNumber, pkt.CharID0).Scan(&token)
if err != nil { if err != nil {
s.rawConn.Close() s.rawConn.Close()
s.logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0)) s.logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0))
@@ -366,143 +365,117 @@ func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfTransitMessage) pkt := p.(*mhfpacket.MsgMhfTransitMessage)
local := false
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
local = true
}
var maxResults, port, count uint16
var cid uint32
var term, ip string
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
switch pkt.SearchType {
case 1:
maxResults = 1
cid = bf.ReadUint32()
case 2:
bf.ReadUint16() // term length
maxResults = bf.ReadUint16()
bf.ReadUint8() // Unk
term = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
case 3:
_ip := bf.ReadBytes(4)
ip = fmt.Sprintf("%d.%d.%d.%d", _ip[3], _ip[2], _ip[1], _ip[0])
port = bf.ReadUint16()
bf.ReadUint16() // term length
maxResults = bf.ReadUint16()
bf.ReadUint8()
term = string(bf.ReadNullTerminatedBytes())
}
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint16(0) resp.WriteUint16(0)
var count uint16
switch pkt.SearchType { switch pkt.SearchType {
case 1: // CID case 1, 2, 3: // usersearchidx, usersearchname, lobbysearchname
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
CharID := bf.ReadUint32()
for _, c := range s.server.Channels { for _, c := range s.server.Channels {
for _, session := range c.sessions { for _, session := range c.sessions {
if session.charID == CharID {
count++
sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID)
resp.WriteBool(true)
resp.WriteUint8(uint8(len(sessionName) + 1))
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])))
resp.WriteBytes(make([]byte, 40))
resp.WriteUint8(uint8(len(sessionStage) + 1))
resp.WriteBytes(make([]byte, 8))
resp.WriteNullTerminatedBytes(sessionName)
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}])
resp.WriteNullTerminatedBytes(sessionStage)
}
}
}
case 2: // Name
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
bf.ReadUint16() // lenSearchTerm
bf.ReadUint16() // maxResults
bf.ReadUint8() // Unk
searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
for _, c := range s.server.Channels {
for _, session := range c.sessions {
if count == 100 {
break
}
if strings.Contains(session.Name, searchTerm) {
count++
sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID)
resp.WriteBool(true)
resp.WriteUint8(uint8(len(sessionName) + 1))
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{session.charID, 3}])))
resp.WriteBytes(make([]byte, 40))
resp.WriteUint8(uint8(len(sessionStage) + 1))
resp.WriteBytes(make([]byte, 8))
resp.WriteNullTerminatedBytes(sessionName)
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])
resp.WriteNullTerminatedBytes(sessionStage)
}
}
}
case 3: // Enumerate Party
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
ip := bf.ReadBytes(4)
ipString := fmt.Sprintf("%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0])
port := bf.ReadUint16()
bf.ReadUint16() // lenStage
maxResults := bf.ReadUint16()
bf.ReadBytes(1)
stageID := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
for _, c := range s.server.Channels {
if c.IP == ipString && c.Port == port {
for _, stage := range c.stages {
if stage.id == stageID {
if count == maxResults { if count == maxResults {
break break
} }
for session := range stage.clients { if pkt.SearchType == 1 && session.charID != cid {
continue
}
if pkt.SearchType == 2 && !strings.Contains(session.Name, term) {
continue
}
if pkt.SearchType == 3 && session.server.IP != ip && session.server.Port != port && session.stage.id != term {
continue
}
count++ count++
hrp := uint16(1)
gr := uint16(0)
s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
sessionName := stringsupport.UTF8ToSJIS(session.Name) sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
if !local {
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
} else {
resp.WriteUint32(0x0100007F)
}
resp.WriteUint16(c.Port) resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID) resp.WriteUint32(session.charID)
resp.WriteUint8(uint8(len(sessionStage) + 1)) resp.WriteUint8(uint8(len(sessionStage) + 1))
resp.WriteUint8(uint8(len(sessionName) + 1)) resp.WriteUint8(uint8(len(sessionName) + 1))
resp.WriteUint8(0) resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])))
resp.WriteUint8(7) // lenBinary
resp.WriteBytes(make([]byte, 48)) // TODO: This case might be <=G2
if _config.ErupeConfig.RealClientMode <= _config.G1 {
resp.WriteBytes(make([]byte, 8))
} else {
resp.WriteBytes(make([]byte, 40))
}
resp.WriteBytes(make([]byte, 8))
resp.WriteNullTerminatedBytes(sessionStage) resp.WriteNullTerminatedBytes(sessionStage)
resp.WriteNullTerminatedBytes(sessionName) resp.WriteNullTerminatedBytes(sessionName)
resp.WriteUint16(hrp) resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}])
resp.WriteUint16(gr)
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
} }
} }
} case 4: // lobbysearch
}
}
case 4: // Find Party
type FindPartyParams struct { type FindPartyParams struct {
StagePrefix string StagePrefix string
RankRestriction uint16 RankRestriction int16
Targets []uint16 Targets []int16
Unk0 []uint16 Unk0 []int16
Unk1 []uint16 Unk1 []int16
QuestID []uint16 QuestID []int16
} }
findPartyParams := FindPartyParams{ findPartyParams := FindPartyParams{
StagePrefix: "sl2Ls210", StagePrefix: "sl2Ls210",
} }
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) numParams := bf.ReadUint8()
numParams := int(bf.ReadUint8()) maxResults = bf.ReadUint16()
maxResults := bf.ReadUint16() for i := uint8(0); i < numParams; i++ {
for i := 0; i < numParams; i++ {
switch bf.ReadUint8() { switch bf.ReadUint8() {
case 0: case 0:
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.RankRestriction = bf.ReadUint16() findPartyParams.RankRestriction = bf.ReadInt16()
} else { } else {
findPartyParams.RankRestriction = uint16(bf.ReadInt8()) findPartyParams.RankRestriction = int16(bf.ReadInt8())
} }
} }
case 1: case 1:
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadUint16()) findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadInt16())
} else { } else {
findPartyParams.Targets = append(findPartyParams.Targets, uint16(bf.ReadInt8())) findPartyParams.Targets = append(findPartyParams.Targets, int16(bf.ReadInt8()))
} }
} }
case 2: case 2:
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
var value int16 var value int16
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
value = bf.ReadInt16() value = bf.ReadInt16()
@@ -523,30 +496,30 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
} }
} }
case 3: // Unknown case 3: // Unknown
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadUint16()) findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadInt16())
} else { } else {
findPartyParams.Unk0 = append(findPartyParams.Unk0, uint16(bf.ReadInt8())) findPartyParams.Unk0 = append(findPartyParams.Unk0, int16(bf.ReadInt8()))
} }
} }
case 4: // Looking for n or already have n case 4: // Looking for n or already have n
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadUint16()) findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadInt16())
} else { } else {
findPartyParams.Unk1 = append(findPartyParams.Unk1, uint16(bf.ReadInt8())) findPartyParams.Unk1 = append(findPartyParams.Unk1, int16(bf.ReadInt8()))
} }
} }
case 5: case 5:
values := int(bf.ReadUint8()) values := bf.ReadUint8()
for i := 0; i < values; i++ { for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 { if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadUint16()) findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadInt16())
} else { } else {
findPartyParams.QuestID = append(findPartyParams.QuestID, uint16(bf.ReadInt8())) findPartyParams.QuestID = append(findPartyParams.QuestID, int16(bf.ReadInt8()))
} }
} }
} }
@@ -559,47 +532,81 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) { if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
sb3.Seek(4, 0) sb3.Seek(4, 0)
stageRankRestriction := sb3.ReadUint16()
stageTarget := sb3.ReadUint16() stageDataParams := 7
if stageRankRestriction > findPartyParams.RankRestriction { if _config.ErupeConfig.RealClientMode <= _config.G10 {
stageDataParams = 4
} else if _config.ErupeConfig.RealClientMode <= _config.Z1 {
stageDataParams = 6
}
var stageData []int16
for i := 0; i < stageDataParams; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
stageData = append(stageData, sb3.ReadInt16())
} else {
stageData = append(stageData, int16(sb3.ReadInt8()))
}
}
if findPartyParams.RankRestriction >= 0 {
if stageData[0] > findPartyParams.RankRestriction {
continue continue
} }
}
var hasTarget bool
if len(findPartyParams.Targets) > 0 { if len(findPartyParams.Targets) > 0 {
for _, target := range findPartyParams.Targets { for _, target := range findPartyParams.Targets {
if target == stageTarget { if target == stageData[1] {
hasTarget = true
break break
} }
} }
if !hasTarget {
continue continue
} }
}
count++ count++
sessionStage := stringsupport.UTF8ToSJIS(stage.id) if !local {
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
} else {
resp.WriteUint32(0x0100007F)
}
resp.WriteUint16(c.Port) resp.WriteUint16(c.Port)
resp.WriteUint16(0) // Static? resp.WriteUint16(0) // Static?
resp.WriteUint16(0) // Unk resp.WriteUint16(0) // Unk, [0 1 2]
resp.WriteUint16(uint16(len(stage.clients))) resp.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots)))
resp.WriteUint16(stage.maxPlayers)
resp.WriteUint16(0) // Num clients entered from stage
resp.WriteUint16(stage.maxPlayers) resp.WriteUint16(stage.maxPlayers)
// TODO: Retail returned the number of clients in quests, not workshop/my series
resp.WriteUint16(uint16(len(stage.reservedClientSlots)))
resp.WriteUint8(0) // Static?
resp.WriteUint8(uint8(stage.maxPlayers))
resp.WriteUint8(1) // Static? resp.WriteUint8(1) // Static?
resp.WriteUint8(uint8(len(sessionStage) + 1)) resp.WriteUint8(uint8(len(stage.id) + 1))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}]))) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}])))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
resp.WriteUint16(stageRankRestriction)
resp.WriteUint16(stageTarget) for i := range stageData {
resp.WriteBytes(make([]byte, 12)) if _config.ErupeConfig.RealClientMode >= _config.Z1 {
resp.WriteNullTerminatedBytes(sessionStage) resp.WriteInt16(stageData[i])
} else {
resp.WriteInt8(int8(stageData[i]))
}
}
resp.WriteUint8(0) // Unk
resp.WriteUint8(0) // Unk
resp.WriteNullTerminatedBytes([]byte(stage.id))
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}]) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}])
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
} }
} }
} }
} }
if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
resp.Seek(0, io.SeekStart) resp.Seek(0, io.SeekStart)
resp.WriteUint16(count) resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, resp.Data())
@@ -640,12 +647,167 @@ func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumeratePrice) pkt := p.(*mhfpacket.MsgMhfEnumeratePrice)
//resp := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
//resp.WriteUint16(0) // Entry type 1 count var lbPrices []struct {
//resp.WriteUint16(0) // Entry type 2 count Unk0 uint16
// directly lifted for now because lacking it crashes the counter on having actual events present Unk1 uint16
data, _ := hex.DecodeString("0000000066000003E800000000007300640100000320000000000006006401000003200000000000300064010000044C00000000007200640100000384000000000034006401000003840000000000140064010000051400000000006E006401000003E8000000000016006401000003E8000000000001006401000003200000000000430064010000057800000000006F006401000003840000000000330064010000044C00000000000B006401000003E800000000000F006401000006400000000000700064010000044C0000000000110064010000057800000000004C006401000003E8000000000059006401000006A400000000006D006401000005DC00000000004B006401000005DC000000000050006401000006400000000000350064010000070800000000006C0064010000044C000000000028006401000005DC00000000005300640100000640000000000060006401000005DC00000000005E0064010000051400000000007B006401000003E80000000000740064010000070800000000006B0064010000025800000000001B0064010000025800000000001C006401000002BC00000000001F006401000006A400000000007900640100000320000000000008006401000003E80000000000150064010000070800000000007A0064010000044C00000000000E00640100000640000000000055006401000007D0000000000002006401000005DC00000000002F0064010000064000000000002A0064010000076C00000000007E006401000002BC0000000000440064010000038400000000005C0064010000064000000000005B006401000006A400000000007D0064010000076C00000000007F006401000005DC0000000000540064010000064000000000002900640100000960000000000024006401000007D0000000000081006401000008340000000000800064010000038400000000001A006401000003E800000000002D0064010000038400000000004A006401000006A400000000005A00640100000384000000000027006401000007080000000000830064010000076C000000000040006401000006400000000000690064010000044C000000000025006401000004B000000000003100640100000708000000000082006401000003E800000000006500640100000640000000000051006401000007D000000000008C0064010000070800000000004D0064010000038400000000004E0064010000089800000000008B006401000004B000000000002E006401000009600000000000920064010000076C00000000008E00640100000514000000000068006401000004B000000000002B006401000003E800000000002C00640100000BB8000000000093006401000008FC00000000009000640100000AF0000000000094006401000006A400000000008D0064010000044C000000000052006401000005DC00000000004F006401000008980000000000970064010000070800000000006A0064010000064000000000005F00640100000384000000000026006401000008FC000000000096006401000007D00000000000980064010000076C000000000041006401000006A400000000003B006401000007080000000000360064010000083400000000009F00640100000A2800000000009A0064010000076C000000000021006401000007D000000000006300640100000A8C0000000000990064010000089800000000009E006401000007080000000000A100640100000C1C0000000000A200640100000C800000000000A400640100000DAC0000000000A600640100000C800000000000A50064010010") Unk2 uint32
doAckBufSucceed(s, pkt.AckHandle, data) }
var wantedList []struct {
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint16
Unk4 uint16
Unk5 uint16
Unk6 uint16
Unk7 uint16
Unk8 uint16
Unk9 uint16
}
gzPrices := []struct {
Unk0 uint16
Gz uint16
Unk1 uint16
Unk2 uint16
MonID uint16
Unk3 uint16
Unk4 uint8
}{
{0, 1000, 0, 0, mhfmon.Pokaradon, 100, 1},
{0, 800, 0, 0, mhfmon.YianKutKu, 100, 1},
{0, 800, 0, 0, mhfmon.DaimyoHermitaur, 100, 1},
{0, 1100, 0, 0, mhfmon.Farunokku, 100, 1},
{0, 900, 0, 0, mhfmon.Congalala, 100, 1},
{0, 900, 0, 0, mhfmon.Gypceros, 100, 1},
{0, 1300, 0, 0, mhfmon.Hyujikiki, 100, 1},
{0, 1000, 0, 0, mhfmon.Basarios, 100, 1},
{0, 1000, 0, 0, mhfmon.Rathian, 100, 1},
{0, 800, 0, 0, mhfmon.ShogunCeanataur, 100, 1},
{0, 1400, 0, 0, mhfmon.Midogaron, 100, 1},
{0, 900, 0, 0, mhfmon.Blangonga, 100, 1},
{0, 1100, 0, 0, mhfmon.Rathalos, 100, 1},
{0, 1000, 0, 0, mhfmon.Khezu, 100, 1},
{0, 1600, 0, 0, mhfmon.Giaorugu, 100, 1},
{0, 1100, 0, 0, mhfmon.Gravios, 100, 1},
{0, 1400, 0, 0, mhfmon.Tigrex, 100, 1},
{0, 1000, 0, 0, mhfmon.Pariapuria, 100, 1},
{0, 1700, 0, 0, mhfmon.Anorupatisu, 100, 1},
{0, 1500, 0, 0, mhfmon.Lavasioth, 100, 1},
{0, 1500, 0, 0, mhfmon.Espinas, 100, 1},
{0, 1600, 0, 0, mhfmon.Rajang, 100, 1},
{0, 1800, 0, 0, mhfmon.Rebidiora, 100, 1},
{0, 1100, 0, 0, mhfmon.YianGaruga, 100, 1},
{0, 1500, 0, 0, mhfmon.AqraVashimu, 100, 1},
{0, 1600, 0, 0, mhfmon.Gurenzeburu, 100, 1},
{0, 1500, 0, 0, mhfmon.Dyuragaua, 100, 1},
{0, 1300, 0, 0, mhfmon.Gougarf, 100, 1},
{0, 1000, 0, 0, mhfmon.Shantien, 100, 1},
{0, 1800, 0, 0, mhfmon.Disufiroa, 100, 1},
{0, 600, 0, 0, mhfmon.Velocidrome, 100, 1},
{0, 600, 0, 0, mhfmon.Gendrome, 100, 1},
{0, 700, 0, 0, mhfmon.Iodrome, 100, 1},
{0, 1700, 0, 0, mhfmon.Baruragaru, 100, 1},
{0, 800, 0, 0, mhfmon.Cephadrome, 100, 1},
{0, 1000, 0, 0, mhfmon.Plesioth, 100, 1},
{0, 1800, 0, 0, mhfmon.Zerureusu, 100, 1},
{0, 1100, 0, 0, mhfmon.Diablos, 100, 1},
{0, 1600, 0, 0, mhfmon.Berukyurosu, 100, 1},
{0, 2000, 0, 0, mhfmon.Fatalis, 100, 1},
{0, 1500, 0, 0, mhfmon.BlackGravios, 100, 1},
{0, 1600, 0, 0, mhfmon.GoldRathian, 100, 1},
{0, 1900, 0, 0, mhfmon.Meraginasu, 100, 1},
{0, 700, 0, 0, mhfmon.Bulldrome, 100, 1},
{0, 900, 0, 0, mhfmon.NonoOrugaron, 100, 1},
{0, 1600, 0, 0, mhfmon.KamuOrugaron, 100, 1},
{0, 1700, 0, 0, mhfmon.Forokururu, 100, 1},
{0, 1900, 0, 0, mhfmon.Diorex, 100, 1},
{0, 1500, 0, 0, mhfmon.AqraJebia, 100, 1},
{0, 1600, 0, 0, mhfmon.SilverRathalos, 100, 1},
{0, 2400, 0, 0, mhfmon.CrimsonFatalis, 100, 1},
{0, 2000, 0, 0, mhfmon.Inagami, 100, 1},
{0, 2100, 0, 0, mhfmon.GarubaDaora, 100, 1},
{0, 900, 0, 0, mhfmon.Monoblos, 100, 1},
{0, 1000, 0, 0, mhfmon.RedKhezu, 100, 1},
{0, 900, 0, 0, mhfmon.Hypnocatrice, 100, 1},
{0, 1700, 0, 0, mhfmon.PearlEspinas, 100, 1},
{0, 900, 0, 0, mhfmon.PurpleGypceros, 100, 1},
{0, 1800, 0, 0, mhfmon.Poborubarumu, 100, 1},
{0, 1900, 0, 0, mhfmon.Lunastra, 100, 1},
{0, 1600, 0, 0, mhfmon.Kuarusepusu, 100, 1},
{0, 1100, 0, 0, mhfmon.PinkRathian, 100, 1},
{0, 1200, 0, 0, mhfmon.AzureRathalos, 100, 1},
{0, 1800, 0, 0, mhfmon.Varusaburosu, 100, 1},
{0, 1000, 0, 0, mhfmon.Gogomoa, 100, 1},
{0, 1600, 0, 0, mhfmon.BurningEspinas, 100, 1},
{0, 2000, 0, 0, mhfmon.Harudomerugu, 100, 1},
{0, 1800, 0, 0, mhfmon.Akantor, 100, 1},
{0, 900, 0, 0, mhfmon.BrightHypnoc, 100, 1},
{0, 2200, 0, 0, mhfmon.Gureadomosu, 100, 1},
{0, 1200, 0, 0, mhfmon.GreenPlesioth, 100, 1},
{0, 2400, 0, 0, mhfmon.Zinogre, 100, 1},
{0, 1900, 0, 0, mhfmon.Gasurabazura, 100, 1},
{0, 1300, 0, 0, mhfmon.Abiorugu, 100, 1},
{0, 1200, 0, 0, mhfmon.BlackDiablos, 100, 1},
{0, 1000, 0, 0, mhfmon.WhiteMonoblos, 100, 1},
{0, 3000, 0, 0, mhfmon.Deviljho, 100, 1},
{0, 2300, 0, 0, mhfmon.YamaKurai, 100, 1},
{0, 2800, 0, 0, mhfmon.Brachydios, 100, 1},
{0, 1700, 0, 0, mhfmon.Toridcless, 100, 1},
{0, 1100, 0, 0, mhfmon.WhiteHypnoc, 100, 1},
{0, 1500, 0, 0, mhfmon.RedLavasioth, 100, 1},
{0, 2200, 0, 0, mhfmon.Barioth, 100, 1},
{0, 1800, 0, 0, mhfmon.Odibatorasu, 100, 1},
{0, 1600, 0, 0, mhfmon.Doragyurosu, 100, 1},
{0, 900, 0, 0, mhfmon.BlueYianKutKu, 100, 1},
{0, 2300, 0, 0, mhfmon.ToaTesukatora, 100, 1},
{0, 2000, 0, 0, mhfmon.Uragaan, 100, 1},
{0, 1900, 0, 0, mhfmon.Teostra, 100, 1},
{0, 1700, 0, 0, mhfmon.Chameleos, 100, 1},
{0, 1800, 0, 0, mhfmon.KushalaDaora, 100, 1},
{0, 2100, 0, 0, mhfmon.Nargacuga, 100, 1},
{0, 2600, 0, 0, mhfmon.Guanzorumu, 100, 1},
{0, 1900, 0, 0, mhfmon.Kirin, 100, 1},
{0, 2000, 0, 0, mhfmon.Rukodiora, 100, 1},
{0, 2700, 0, 0, mhfmon.StygianZinogre, 100, 1},
{0, 2200, 0, 0, mhfmon.Voljang, 100, 1},
{0, 1800, 0, 0, mhfmon.Zenaserisu, 100, 1},
{0, 3100, 0, 0, mhfmon.GoreMagala, 100, 1},
{0, 3200, 0, 0, mhfmon.ShagaruMagala, 100, 1},
{0, 3500, 0, 0, mhfmon.Eruzerion, 100, 1},
{0, 3200, 0, 0, mhfmon.Amatsu, 100, 1},
}
bf.WriteUint16(uint16(len(lbPrices)))
for _, lb := range lbPrices {
bf.WriteUint16(lb.Unk0)
bf.WriteUint16(lb.Unk1)
bf.WriteUint32(lb.Unk2)
}
bf.WriteUint16(uint16(len(wantedList)))
for _, wanted := range wantedList {
bf.WriteUint32(wanted.Unk0)
bf.WriteUint32(wanted.Unk1)
bf.WriteUint32(wanted.Unk2)
bf.WriteUint16(wanted.Unk3)
bf.WriteUint16(wanted.Unk4)
bf.WriteUint16(wanted.Unk5)
bf.WriteUint16(wanted.Unk6)
bf.WriteUint16(wanted.Unk7)
bf.WriteUint16(wanted.Unk8)
bf.WriteUint16(wanted.Unk9)
}
bf.WriteUint8(uint8(len(gzPrices)))
for _, gz := range gzPrices {
bf.WriteUint16(gz.Unk0)
bf.WriteUint16(gz.Gz)
bf.WriteUint16(gz.Unk1)
bf.WriteUint16(gz.Unk2)
bf.WriteUint16(gz.MonID)
bf.WriteUint16(gz.Unk3)
bf.WriteUint8(gz.Unk4)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) {
@@ -938,10 +1100,10 @@ func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) {
column = "promo_points" column = "promo_points"
} }
var value int var value int16
err := s.server.db.QueryRow(fmt.Sprintf(`SELECT %s FROM characters WHERE id = $1`, column), s.charID).Scan(&value) err := s.server.db.QueryRow(fmt.Sprintf(`SELECT %s FROM characters WHERE id = $1`, column), s.charID).Scan(&value)
if err == nil { if err == nil {
if value-int(pkt.Delta) < 0 { if value+pkt.Delta < 0 {
s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = 0 WHERE id = $1`, column), s.charID) s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = 0 WHERE id = $1`, column), s.charID)
} else { } else {
s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = %s + $1 WHERE id = $2`, column, column), pkt.Delta, s.charID) s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = %s + $1 WHERE id = $2`, column, column), pkt.Delta, s.charID)
@@ -981,7 +1143,10 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUnreserveSrg)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -29,6 +29,9 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
for _, cid := range stage.clients { for _, cid := range stage.clients {
clients = append(clients, cid) clients = append(clients, cid)
} }
for cid := range stage.reservedClientSlots {
clients = append(clients, cid)
}
case 1: // Not ready case 1: // Not ready
for cid, ready := range stage.reservedClientSlots { for cid, ready := range stage.reservedClientSlots {
if !ready { if !ready {

View File

@@ -45,6 +45,9 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return return
} }
if s.server.erupeConfig.DevModeOptions.SaveDumps.RawEnabled {
dumpSaveData(s, saveData, "raw-savedata")
}
s.logger.Info("Updating save with blob") s.logger.Info("Updating save with blob")
characterSaveData.decompSave = saveData characterSaveData.decompSave = saveData
} }

View File

@@ -989,20 +989,21 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} }
bf.WriteUint32(guild.PugiOutfits) bf.WriteUint32(guild.PugiOutfits)
if guild.Rank() >= 3 { limit := s.server.erupeConfig.GameplayOptions.ClanMemberLimits[0][1]
bf.WriteUint8(40) for _, j := range s.server.erupeConfig.GameplayOptions.ClanMemberLimits {
} else if guild.Rank() >= 7 { if guild.Rank() >= uint16(j[0]) {
bf.WriteUint8(50) limit = j[1]
} else if guild.Rank() >= 10 {
bf.WriteUint8(60)
} else {
bf.WriteUint8(30)
} }
}
if limit > 100 {
limit = 100
}
bf.WriteUint8(limit)
bf.WriteUint32(55000) bf.WriteUint32(55000)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint16(0) // Changing Room RP bf.WriteUint16(0) // Changing Room RP
bf.WriteUint16(0) bf.WriteUint16(0) // Ignored
if guild.AllianceID > 0 { if guild.AllianceID > 0 {
alliance, err := GetAllianceData(s, guild.AllianceID) alliance, err := GetAllianceData(s, guild.AllianceID)
@@ -1012,7 +1013,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(alliance.ID) bf.WriteUint32(alliance.ID)
bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
bf.WriteUint16(alliance.TotalMembers) bf.WriteUint16(alliance.TotalMembers)
bf.WriteUint8(0) bf.WriteUint8(0) // Ignored
bf.WriteUint8(0) bf.WriteUint8(0)
ps.Uint16(bf, alliance.Name, true) ps.Uint16(bf, alliance.Name, true)
if alliance.SubGuild1ID > 0 { if alliance.SubGuild1ID > 0 {
@@ -1831,7 +1832,7 @@ func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking) pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking)
guild, _ := GetGuildInfoByCharacterId(s, s.charID) guild, _ := GetGuildInfoByCharacterId(s, s.charID)
startTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.GuildMealDuration-3600) * time.Second) startTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.ClanMealDuration-3600) * time.Second)
if pkt.OverwriteID != 0 { if pkt.OverwriteID != 0 {
s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, startTime, pkt.OverwriteID) s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, startTime, pkt.OverwriteID)
} else { } else {

View File

@@ -157,6 +157,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
s.stage.reservedClientSlots[s.charID] = false s.stage.reservedClientSlots[s.charID] = false
s.stage.Unlock() s.stage.Unlock()
s.stageMoveStack.Push(s.stage.id) s.stageMoveStack.Push(s.stage.id)
s.stageMoveStack.Lock()
} }
if s.reservationStage != nil { if s.reservationStage != nil {
@@ -170,6 +171,7 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysBackStage) pkt := p.(*mhfpacket.MsgSysBackStage)
// Transfer back to the saved stage ID before the previous move or enter. // Transfer back to the saved stage ID before the previous move or enter.
s.stageMoveStack.Unlock()
backStage, err := s.stageMoveStack.Pop() backStage, err := s.stageMoveStack.Pop()
if backStage == "" || err != nil { if backStage == "" || err != nil {
backStage = "sl1Ns200p0a0u0" backStage = "sl1Ns200p0a0u0"
@@ -190,7 +192,9 @@ func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysMoveStage) pkt := p.(*mhfpacket.MsgSysMoveStage)
// Set a new move stack from the given stage ID // Set a new move stack from the given stage ID
if !s.stageMoveStack.Locked {
s.stageMoveStack.Set(pkt.StageID) s.stageMoveStack.Set(pkt.StageID)
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID) doStageTransfer(s, pkt.AckHandle, pkt.StageID)
} }
@@ -386,7 +390,11 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(uint16(len(stage.reservedClientSlots))) bf.WriteUint16(uint16(len(stage.reservedClientSlots)))
bf.WriteUint16(uint16(len(stage.clients))) bf.WriteUint16(uint16(len(stage.clients)))
if strings.HasPrefix(stage.id, "sl2Ls") {
bf.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots)))
} else {
bf.WriteUint16(uint16(len(stage.clients))) bf.WriteUint16(uint16(len(stage.clients)))
}
bf.WriteUint16(stage.maxPlayers) bf.WriteUint16(stage.maxPlayers)
var flags uint8 var flags uint8
if stage.locked { if stage.locked {

View File

@@ -32,13 +32,14 @@ func (s *Server) createNewUser(ctx context.Context, username string, password st
return id, rights, err return id, rights, err
} }
func (s *Server) createLoginToken(ctx context.Context, uid uint32) (string, error) { func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
loginToken := token.Generate(16) loginToken := token.Generate(16)
_, err := s.db.ExecContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2)", uid, loginToken) var tid uint32
err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
if err != nil { if err != nil {
return "", err return 0, "", err
} }
return loginToken, nil return tid, loginToken, nil
} }
func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) { func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) {

View File

@@ -27,6 +27,7 @@ type LauncherResponse struct {
} }
type User struct { type User struct {
TokenID uint32 `json:"tokenId"`
Token string `json:"token"` Token string `json:"token"`
Rights uint32 `json:"rights"` Rights uint32 `json:"rights"`
} }
@@ -65,13 +66,14 @@ type ExportData struct {
Character map[string]interface{} `json:"character"` Character map[string]interface{} `json:"character"`
} }
func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string, characters []Character) AuthData { func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData {
resp := AuthData{ resp := AuthData{
CurrentTS: uint32(channelserver.TimeAdjusted().Unix()), CurrentTS: uint32(channelserver.TimeAdjusted().Unix()),
ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()), ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()),
EntranceCount: 1, EntranceCount: 1,
User: User{ User: User{
Rights: userRights, Rights: userRights,
TokenID: userTokenID,
Token: userToken, Token: userToken,
}, },
Characters: characters, Characters: characters,
@@ -142,7 +144,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
return return
} }
userToken, err := s.createLoginToken(ctx, userID) userTokenID, userToken, err := s.createLoginToken(ctx, userID)
if err != nil { if err != nil {
s.logger.Warn("Error registering login token", zap.Error(err)) s.logger.Warn("Error registering login token", zap.Error(err))
w.WriteHeader(500) w.WriteHeader(500)
@@ -157,7 +159,7 @@ func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
if characters == nil { if characters == nil {
characters = []Character{} characters = []Character{}
} }
respData := s.newAuthData(userID, userRights, userToken, characters) respData := s.newAuthData(userID, userRights, userTokenID, userToken, characters)
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(respData) json.NewEncoder(w).Encode(respData)
} }
@@ -191,13 +193,13 @@ func (s *Server) Register(w http.ResponseWriter, r *http.Request) {
return return
} }
userToken, err := s.createLoginToken(ctx, userID) userTokenID, userToken, err := s.createLoginToken(ctx, userID)
if err != nil { if err != nil {
s.logger.Error("Error registering login token", zap.Error(err)) s.logger.Error("Error registering login token", zap.Error(err))
w.WriteHeader(500) w.WriteHeader(500)
return return
} }
respData := s.newAuthData(userID, userRights, userToken, []Character{}) respData := s.newAuthData(userID, userRights, userTokenID, userToken, []Character{})
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(respData) json.NewEncoder(w).Encode(respData)
} }