Merge pull request #17 from ZeruLight/feature/transit-message

handle TransitMessage
This commit is contained in:
wish
2022-08-05 16:59:46 +10:00
committed by GitHub
11 changed files with 230 additions and 51 deletions

View File

@@ -17,6 +17,7 @@
"FestaEvent": 0, "FestaEvent": 0,
"TournamentEvent": 0, "TournamentEvent": 0,
"MezFesEvent": true, "MezFesEvent": true,
"MezFesAlt": false,
"DisableMailItems": true, "DisableMailItems": true,
"DisableTokenCheck": false, "DisableTokenCheck": false,
"SaveDumps": { "SaveDumps": {

View File

@@ -37,6 +37,7 @@ type DevModeOptions struct {
FestaEvent int // Hunter's Festa event status FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status TournamentEvent int // VS Tournament event status
MezFesEvent bool // MezFes status MezFesEvent bool // MezFes status
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
DisableMailItems bool // Hack to prevent english versions of MHF from crashing DisableMailItems bool // Hack to prevent english versions of MHF from crashing
SaveDumps SaveDumpOptions SaveDumps SaveDumpOptions

View File

@@ -180,7 +180,13 @@ func main() {
DB: db, DB: db,
DiscordBot: discordBot, DiscordBot: discordBot,
}) })
err = c.Start(int(ce.Port)) if ee.IP == "" {
c.IP = erupeConfig.Host
} else {
c.IP = ee.IP
}
c.Port = ce.Port
err = c.Start()
if err != nil { if err != nil {
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error())) preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
} else { } else {

View File

@@ -1,20 +1,20 @@
package mhfpacket 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"
) )
// MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE // MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE
type MsgMhfTransitMessage struct { type MsgMhfTransitMessage struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 Unk0 uint8
Unk1 uint8 Unk1 uint8
Unk2 uint16 SearchType uint16
MessageData []byte MessageData []byte
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -24,12 +24,12 @@ func (m *MsgMhfTransitMessage) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16() m.SearchType = bf.ReadUint16()
m.MessageData = bf.ReadBytes(uint(bf.ReadUint16())) m.MessageData = bf.ReadBytes(uint(bf.ReadUint16()))
return nil return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -1,15 +1,19 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/common/bfutil"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgSysAcquireSemaphore represents the MSG_SYS_ACQUIRE_SEMAPHORE // MsgSysAcquireSemaphore represents the MSG_SYS_ACQUIRE_SEMAPHORE
type MsgSysAcquireSemaphore struct{} type MsgSysAcquireSemaphore struct {
AckHandle uint32
SemaphoreID string
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID { func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
SemaphoreIDLength := bf.ReadUint8()
m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength))))
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -1,19 +1,19 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/common/stringsupport"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE // MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE
type MsgSysEnumerateStage struct { type MsgSysEnumerateStage struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 // Hardcoded 1 in the binary Unk0 uint8 // Hardcoded 1 in the binary
StageIDLength uint8 StagePrefix string // NULL terminated string.
StageID string // NULL terminated string.
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,8 +25,8 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID {
func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint8()
m.StageIDLength = bf.ReadUint8() bf.ReadUint8()
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
return nil return nil
} }

View File

@@ -4,7 +4,11 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"erupe-ce/common/stringsupport"
"fmt" "fmt"
"io"
"net"
"strings"
"io/ioutil" "io/ioutil"
"math/bits" "math/bits"
@@ -341,10 +345,157 @@ 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)
// TODO: figure out what this is supposed to return resp := byteframe.NewByteFrame()
// probably what world+land the targeted character is on? resp.WriteUint16(0)
// stubbed response will just say user not found var count uint16
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) switch pkt.SearchType {
case 1: // CID
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
CharID := bf.ReadUint32()
for _, c := range s.server.Channels {
for _, session := range c.sessions {
if session.charID == CharID {
count++
sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
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.stageID)
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 {
break
}
for session := range stage.clients {
count++
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
sessionName := stringsupport.UTF8ToSJIS(session.Name)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID)
resp.WriteUint8(uint8(len(sessionStage) + 1))
resp.WriteUint8(uint8(len(sessionName) + 1))
resp.WriteUint8(0)
resp.WriteUint8(7) // lenBinary
resp.WriteBytes(make([]byte, 48))
resp.WriteNullTerminatedBytes(sessionStage)
resp.WriteNullTerminatedBytes(sessionName)
resp.WriteUint16(999) // HR
resp.WriteUint16(999) // GR
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
}
}
}
}
}
case 4: // Find Party
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
bf.ReadUint8()
maxResults := bf.ReadUint16()
bf.ReadUint8()
bf.ReadUint8()
partyType := bf.ReadUint16()
_ = bf.DataFromCurrent() // Restrictions
var stagePrefix string
switch partyType {
case 0: // Public Bar
stagePrefix = "sl2Ls210"
case 1: // Tokotoko Partnya
stagePrefix = "sl2Ls463"
case 2: // Hunting Prowess Match
stagePrefix = "sl2Ls286"
case 3: // Volpakkun Together
stagePrefix = "sl2Ls465"
case 5: // Quick Party
// Unk
}
for _, c := range s.server.Channels {
for _, stage := range c.stages {
if count == maxResults {
break
}
if strings.HasPrefix(stage.id, stagePrefix) {
count++
sessionStage := stringsupport.UTF8ToSJIS(stage.id)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port)
// TODO: This is half right, could be trimmed
resp.WriteUint16(0)
resp.WriteUint16(uint16(len(stage.clients)))
resp.WriteUint16(uint16(len(stage.clients)))
resp.WriteUint16(stage.maxPlayers)
resp.WriteUint16(0)
resp.WriteUint16(uint16(len(stage.clients)))
//
resp.WriteUint16(uint16(len(sessionStage) + 1))
resp.WriteUint8(1)
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
resp.WriteBytes(make([]byte, 16))
resp.WriteNullTerminatedBytes(sessionStage)
resp.WriteBytes([]byte{0x00})
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
}
}
}
}
resp.Seek(0, io.SeekStart)
resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {} func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -124,7 +124,16 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
} }
func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
//pkt := p.(*mhfpacket.MsgSysAcquireSemaphore) pkt := p.(*mhfpacket.MsgSysAcquireSemaphore)
if sema, exists := s.server.semaphore[pkt.SemaphoreID]; exists {
sema.clients[s] = s.charID
bf := byteframe.NewByteFrame()
bf.WriteUint32(sema.id)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
} }
func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -2,6 +2,7 @@ package channelserver
import ( import (
"fmt" "fmt"
"strings"
"time" "time"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
@@ -198,22 +199,24 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLockStage) pkt := p.(*mhfpacket.MsgSysLockStage)
// TODO(Andoryuuta): What does this packet _actually_ do? // TODO(Andoryuuta): What does this packet _actually_ do?
// I think this is supposed to mark a stage as no longer able to accept client reservations
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
s.reservationStage.RLock() if s.reservationStage != nil {
defer s.reservationStage.RUnlock() s.reservationStage.RLock()
defer s.reservationStage.RUnlock()
for charID := range s.reservationStage.reservedClientSlots { for charID := range s.reservationStage.reservedClientSlots {
session := s.server.FindSessionByCharID(charID) session := s.server.FindSessionByCharID(charID)
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
delete(s.server.stages, s.reservationStage.id)
} }
s.server.Lock() destructEmptyStages(s)
defer s.server.Unlock()
delete(s.server.stages, s.reservationStage.id)
} }
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
@@ -366,8 +369,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
continue continue
} }
// Check for valid stage type if !strings.Contains(stage.id, pkt.StagePrefix) {
if sid[3:5] != "Qs" && sid[3:5] != "Ms" && sid[3:5] != "Ls" {
continue continue
} }

View File

@@ -54,6 +54,8 @@ type Server struct {
sync.Mutex sync.Mutex
Channels []*Server Channels []*Server
ID uint16 ID uint16
IP string
Port uint16
logger *zap.Logger logger *zap.Logger
db *sqlx.DB db *sqlx.DB
erupeConfig *config.Config erupeConfig *config.Config
@@ -196,8 +198,8 @@ func NewServer(config *Config) *Server {
} }
// Start starts the server in a new goroutine. // Start starts the server in a new goroutine.
func (s *Server) Start(port int) error { func (s *Server) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -128,7 +128,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint32(0x0A5197DF) bf.WriteUint32(0x0A5197DF)
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
alt := false alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
if mezfes { if mezfes {
// Start time // Start time
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
@@ -136,7 +136,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix())) bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix()))
bf.WriteUint8(2) // Unk bf.WriteUint8(2) // Unk
bf.WriteUint32(20) // Single tickets bf.WriteUint32(20) // Single tickets
bf.WriteUint32(0) // Group tickets bf.WriteUint32(10) // Group tickets
bf.WriteUint8(8) // Stalls open bf.WriteUint8(8) // Stalls open
bf.WriteUint8(0xA) // Unk bf.WriteUint8(0xA) // Unk
bf.WriteUint8(0x3) // Pachinko bf.WriteUint8(0x3) // Pachinko