mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-12 23:14:36 +01:00
Merge pull request #17 from ZeruLight/feature/transit-message
handle TransitMessage
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
"FestaEvent": 0,
|
||||
"TournamentEvent": 0,
|
||||
"MezFesEvent": true,
|
||||
"MezFesAlt": false,
|
||||
"DisableMailItems": true,
|
||||
"DisableTokenCheck": false,
|
||||
"SaveDumps": {
|
||||
|
||||
@@ -37,6 +37,7 @@ type DevModeOptions struct {
|
||||
FestaEvent int // Hunter's Festa event status
|
||||
TournamentEvent int // VS Tournament event status
|
||||
MezFesEvent bool // MezFes status
|
||||
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
|
||||
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
|
||||
DisableMailItems bool // Hack to prevent english versions of MHF from crashing
|
||||
SaveDumps SaveDumpOptions
|
||||
|
||||
8
main.go
8
main.go
@@ -180,7 +180,13 @@ func main() {
|
||||
DB: db,
|
||||
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 {
|
||||
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
|
||||
} else {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"errors"
|
||||
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE
|
||||
type MsgMhfTransitMessage struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
Unk2 uint16
|
||||
MessageData []byte
|
||||
AckHandle uint32
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
SearchType uint16
|
||||
MessageData []byte
|
||||
}
|
||||
|
||||
// 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
|
||||
func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint8()
|
||||
m.Unk1 = bf.ReadUint8()
|
||||
m.Unk2 = bf.ReadUint16()
|
||||
m.MessageData = bf.ReadBytes(uint(bf.ReadUint16()))
|
||||
return nil
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint8()
|
||||
m.Unk1 = bf.ReadUint8()
|
||||
m.SearchType = bf.ReadUint16()
|
||||
m.MessageData = bf.ReadBytes(uint(bf.ReadUint16()))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Build builds a binary packet from the current data.
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
import (
|
||||
"errors"
|
||||
"erupe-ce/common/bfutil"
|
||||
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
|
||||
@@ -18,7 +22,10 @@ func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
|
||||
|
||||
// Parse parses the packet from binary
|
||||
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.
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
import (
|
||||
"errors"
|
||||
"erupe-ce/common/stringsupport"
|
||||
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE
|
||||
type MsgSysEnumerateStage struct {
|
||||
AckHandle uint32
|
||||
Unk0 uint8 // Hardcoded 1 in the binary
|
||||
StageIDLength uint8
|
||||
StageID string // NULL terminated string.
|
||||
AckHandle uint32
|
||||
Unk0 uint8 // Hardcoded 1 in the binary
|
||||
StagePrefix string // NULL terminated string.
|
||||
}
|
||||
|
||||
// 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 {
|
||||
m.AckHandle = bf.ReadUint32()
|
||||
m.Unk0 = bf.ReadUint8()
|
||||
m.StageIDLength = bf.ReadUint8()
|
||||
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength)))
|
||||
bf.ReadUint8()
|
||||
m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
@@ -341,10 +345,157 @@ func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfTransitMessage)
|
||||
// TODO: figure out what this is supposed to return
|
||||
// probably what world+land the targeted character is on?
|
||||
// stubbed response will just say user not found
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint16(0)
|
||||
var count uint16
|
||||
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) {}
|
||||
|
||||
@@ -124,7 +124,16 @@ func handleMsgSysCreateAcquireSemaphore(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) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
@@ -198,22 +199,24 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysLockStage)
|
||||
// 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})
|
||||
}
|
||||
|
||||
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.reservationStage.RLock()
|
||||
defer s.reservationStage.RUnlock()
|
||||
if s.reservationStage != nil {
|
||||
s.reservationStage.RLock()
|
||||
defer s.reservationStage.RUnlock()
|
||||
|
||||
for charID := range s.reservationStage.reservedClientSlots {
|
||||
session := s.server.FindSessionByCharID(charID)
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||
for charID := range s.reservationStage.reservedClientSlots {
|
||||
session := s.server.FindSessionByCharID(charID)
|
||||
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||
}
|
||||
|
||||
delete(s.server.stages, s.reservationStage.id)
|
||||
}
|
||||
|
||||
s.server.Lock()
|
||||
defer s.server.Unlock()
|
||||
|
||||
delete(s.server.stages, s.reservationStage.id)
|
||||
destructEmptyStages(s)
|
||||
}
|
||||
|
||||
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -366,8 +369,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for valid stage type
|
||||
if sid[3:5] != "Qs" && sid[3:5] != "Ms" && sid[3:5] != "Ls" {
|
||||
if !strings.Contains(stage.id, pkt.StagePrefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ type Server struct {
|
||||
sync.Mutex
|
||||
Channels []*Server
|
||||
ID uint16
|
||||
IP string
|
||||
Port uint16
|
||||
logger *zap.Logger
|
||||
db *sqlx.DB
|
||||
erupeConfig *config.Config
|
||||
@@ -196,8 +198,8 @@ func NewServer(config *Config) *Server {
|
||||
}
|
||||
|
||||
// Start starts the server in a new goroutine.
|
||||
func (s *Server) Start(port int) error {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
func (s *Server) Start() error {
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
||||
bf.WriteUint32(0x0A5197DF)
|
||||
|
||||
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
|
||||
alt := false
|
||||
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
|
||||
if mezfes {
|
||||
// Start time
|
||||
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.WriteUint8(2) // Unk
|
||||
bf.WriteUint32(20) // Single tickets
|
||||
bf.WriteUint32(0) // Group tickets
|
||||
bf.WriteUint32(10) // Group tickets
|
||||
bf.WriteUint8(8) // Stalls open
|
||||
bf.WriteUint8(0xA) // Unk
|
||||
bf.WriteUint8(0x3) // Pachinko
|
||||
|
||||
Reference in New Issue
Block a user