mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-15 08:25:09 +01:00
stage improvements
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/common/bfutil"
|
"erupe-ce/common/bfutil"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
@@ -32,5 +33,5 @@ func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
|
|||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
func (m *MsgSysCreateStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysCreateStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
panic("Not implemented")
|
return errors.New("NOT IMPLEMENTED")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/common/bfutil"
|
"erupe-ce/common/bfutil"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
@@ -10,7 +11,7 @@ import (
|
|||||||
// MsgSysReserveStage represents the MSG_SYS_RESERVE_STAGE
|
// MsgSysReserveStage represents the MSG_SYS_RESERVE_STAGE
|
||||||
type MsgSysReserveStage struct {
|
type MsgSysReserveStage struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8 // Made with: `16 * x | 1;`, unknown `x` values.
|
Ready uint8 // Bitfield but hex (0x11 or 0x01)
|
||||||
StageID string // NULL terminated string.
|
StageID string // NULL terminated string.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ func (m *MsgSysReserveStage) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Ready = bf.ReadUint8()
|
||||||
stageIDLength := bf.ReadUint8()
|
stageIDLength := bf.ReadUint8()
|
||||||
m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength))))
|
m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength))))
|
||||||
return nil
|
return nil
|
||||||
@@ -30,5 +31,5 @@ func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
|
|||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
func (m *MsgSysReserveStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysReserveStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
panic("Not implemented")
|
return errors.New("NOT IMPLEMENTED")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
"erupe-ce/network/clientctx"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
// MsgSysSetStagePass represents the MSG_SYS_SET_STAGE_PASS
|
// MsgSysSetStagePass represents the MSG_SYS_SET_STAGE_PASS
|
||||||
type MsgSysSetStagePass struct {
|
type MsgSysSetStagePass struct {
|
||||||
Unk0 uint8 // Hardcoded 0 in the binary
|
Unk0 uint8 // Hardcoded 0 in the binary
|
||||||
PasswordLength uint8
|
|
||||||
Password string // NULL-terminated string
|
Password string // NULL-terminated string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,8 +22,8 @@ func (m *MsgSysSetStagePass) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgSysSetStagePass) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysSetStagePass) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadUint8()
|
||||||
m.PasswordLength = bf.ReadUint8()
|
_ = bf.ReadUint8() // Password length
|
||||||
m.Password = string(bf.ReadBytes(uint(m.PasswordLength)))
|
m.Password = string(bf.ReadNullTerminatedBytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,41 +10,35 @@ import (
|
|||||||
func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysEnumerateClient)
|
pkt := p.(*mhfpacket.MsgSysEnumerateClient)
|
||||||
|
|
||||||
// Read-lock the stages map.
|
|
||||||
s.server.stagesLock.RLock()
|
s.server.stagesLock.RLock()
|
||||||
|
|
||||||
stage, ok := s.server.stages[pkt.StageID]
|
stage, ok := s.server.stages[pkt.StageID]
|
||||||
if !ok {
|
if !ok {
|
||||||
s.logger.Fatal("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID))
|
s.logger.Fatal("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock the stages map.
|
|
||||||
s.server.stagesLock.RUnlock()
|
s.server.stagesLock.RUnlock()
|
||||||
|
|
||||||
// Read-lock the stage and make the response with all of the charID's in the stage.
|
// Read-lock the stage and make the response with all of the charID's in the stage.
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
stage.RLock()
|
stage.RLock()
|
||||||
|
var clients []uint32
|
||||||
// TODO(Andoryuuta): Is only the reservations needed? Do clients send this packet for mezeporta as well?
|
switch pkt.Unk1 {
|
||||||
|
case 1: // Not ready
|
||||||
// Make a map to deduplicate the charIDs between the unreserved clients and the reservations.
|
for cid, ready := range stage.reservedClientSlots {
|
||||||
deduped := make(map[uint32]interface{})
|
if !ready {
|
||||||
|
clients = append(clients, cid)
|
||||||
// Add the charIDs
|
}
|
||||||
for session := range stage.clients {
|
}
|
||||||
deduped[session.charID] = nil
|
case 2: // Ready
|
||||||
|
for cid, ready := range stage.reservedClientSlots {
|
||||||
|
if ready {
|
||||||
|
clients = append(clients, cid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for charid := range stage.reservedClientSlots {
|
|
||||||
deduped[charid] = nil
|
|
||||||
}
|
}
|
||||||
|
resp.WriteUint16(uint16(len(clients)))
|
||||||
// Write the deduplicated response
|
for _, cid := range clients {
|
||||||
resp.WriteUint16(uint16(len(deduped))) // Client count
|
resp.WriteUint32(cid)
|
||||||
for charid := range deduped {
|
|
||||||
resp.WriteUint32(charid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage.RUnlock()
|
stage.RUnlock()
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
|
|||||||
@@ -247,119 +247,76 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysReserveStage)
|
pkt := p.(*mhfpacket.MsgSysReserveStage)
|
||||||
|
if stage, exists := s.server.stages[pkt.StageID]; exists {
|
||||||
stageID := pkt.StageID
|
stage.Lock()
|
||||||
fmt.Printf("Got reserve stage req, TargetCount:%v, StageID:%v\n", pkt.Unk0, stageID)
|
defer stage.Unlock()
|
||||||
|
if _, exists := stage.reservedClientSlots[s.charID]; exists {
|
||||||
// Try to get the stage
|
switch pkt.Ready {
|
||||||
s.server.stagesLock.Lock()
|
case 1: // 0x01
|
||||||
stage, gotStage := s.server.stages[stageID]
|
stage.reservedClientSlots[s.charID] = false
|
||||||
s.server.stagesLock.Unlock()
|
case 17: // 0x11
|
||||||
|
stage.reservedClientSlots[s.charID] = true
|
||||||
if !gotStage {
|
}
|
||||||
s.logger.Error("Failed to get stage", zap.String("StageID", stageID))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
|
||||||
return
|
if len(stage.password) > 0 {
|
||||||
}
|
// s.logger.Debug("", zap.String("stgpw", stage.password), zap.String("usrpw", s.stagePass))
|
||||||
|
if stage.password == s.stagePass {
|
||||||
// Try to reserve a slot, fail if full.
|
stage.reservedClientSlots[s.charID] = false
|
||||||
stage.Lock()
|
s.Lock()
|
||||||
defer stage.Unlock()
|
s.reservationStage = stage
|
||||||
|
s.Unlock()
|
||||||
// Quick fix to allow readying up while party is full, more investigation needed
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
// Reserve stage is also sent when a player is ready, probably need to parse the
|
return
|
||||||
// request a little more thoroughly.
|
}
|
||||||
if _, exists := stage.reservedClientSlots[s.charID]; exists {
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
} else {
|
||||||
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
|
stage.reservedClientSlots[s.charID] = false
|
||||||
// Add the charID to the stage's reservation map
|
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
|
||||||
stage.reservedClientSlots[s.charID] = nil
|
s.Lock()
|
||||||
|
s.reservationStage = stage
|
||||||
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
|
s.Unlock()
|
||||||
s.Lock()
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
s.reservationStage = stage
|
}
|
||||||
s.Unlock()
|
} else {
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
}
|
||||||
} else {
|
} else {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
s.logger.Error("Failed to get stage", zap.String("StageID", pkt.StageID))
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
// Clear the saved reservation stage
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
stage := s.reservationStage
|
stage := s.reservationStage
|
||||||
if stage != nil {
|
s.reservationStage = nil
|
||||||
s.reservationStage = nil
|
|
||||||
}
|
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
|
||||||
// Remove the charID from the stage's reservation map
|
|
||||||
if stage != nil {
|
if stage != nil {
|
||||||
stage.Lock()
|
stage.Lock()
|
||||||
_, exists := stage.reservedClientSlots[s.charID]
|
if _, exists := stage.reservedClientSlots[s.charID]; exists {
|
||||||
if exists {
|
|
||||||
delete(stage.reservedClientSlots, s.charID)
|
delete(stage.reservedClientSlots, s.charID)
|
||||||
}
|
}
|
||||||
stage.Unlock()
|
stage.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgSysSetStagePass)
|
||||||
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
s.Lock()
|
||||||
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
|
stage := s.reservationStage
|
||||||
defer s.logger.Debug("MsgSysWaitStageBinary Done!")
|
s.Unlock()
|
||||||
|
if stage != nil {
|
||||||
// Try to get the stage
|
stage.Lock()
|
||||||
stageID := pkt.StageID
|
if _, exists := stage.reservedClientSlots[s.charID]; exists {
|
||||||
s.server.stagesLock.Lock()
|
stage.password = pkt.Password
|
||||||
stage, gotStage := s.server.stages[stageID]
|
|
||||||
s.server.stagesLock.Unlock()
|
|
||||||
|
|
||||||
// TODO(Andoryuuta): This is a hack for a binary part that none of the clients set, figure out what it represents.
|
|
||||||
// In the packet captures, it seemingly comes out of nowhere, so presumably the server makes it.
|
|
||||||
if pkt.BinaryType0 == 1 && pkt.BinaryType1 == 12 {
|
|
||||||
// This might contain the hunter count, or max player count?
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got the stage, lock and try to get the data.
|
|
||||||
var stageBinary []byte
|
|
||||||
var gotBinary bool
|
|
||||||
if gotStage {
|
|
||||||
for {
|
|
||||||
s.logger.Debug("MsgSysWaitStageBinary before lock and get stage")
|
|
||||||
stage.Lock()
|
|
||||||
stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
|
|
||||||
stage.Unlock()
|
|
||||||
s.logger.Debug("MsgSysWaitStageBinary after lock and get stage")
|
|
||||||
|
|
||||||
if gotBinary {
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, stageBinary)
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
s.logger.Debug("Waiting stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
|
|
||||||
|
|
||||||
// Couldn't get binary, sleep for some time and try again.
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(Andoryuuta): Figure out what the game sends on timeout and implement it!
|
|
||||||
/*
|
|
||||||
if timeout {
|
|
||||||
s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
|
|
||||||
s.logger.Warn("Sending blank stage binary")
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
stage.Unlock()
|
||||||
} else {
|
} else {
|
||||||
s.logger.Warn("Failed to get stage", zap.String("StageID", stageID))
|
// Store for use on next ReserveStage
|
||||||
|
s.Lock()
|
||||||
|
s.stagePass = pkt.Password
|
||||||
|
s.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,9 +396,16 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
for sid, stage := range s.server.stages {
|
for sid, stage := range s.server.stages {
|
||||||
stage.RLock()
|
stage.RLock()
|
||||||
defer stage.RUnlock()
|
defer stage.RUnlock()
|
||||||
|
|
||||||
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
|
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for valid stage type
|
||||||
|
if sid[3:5] != "Qs" && sid[3:5] != "Ms" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
joinable++
|
joinable++
|
||||||
|
|
||||||
resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players.
|
resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players.
|
||||||
@@ -453,9 +417,14 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
resp.WriteUint16(hasDeparted) // HasDeparted.
|
resp.WriteUint16(hasDeparted) // HasDeparted.
|
||||||
resp.WriteUint16(stage.maxPlayers) // Max players.
|
resp.WriteUint16(stage.maxPlayers) // Max players.
|
||||||
resp.WriteBool(len(stage.password) > 0) // Password protected.
|
if len(stage.password) > 0 {
|
||||||
resp.WriteUint8(uint8(len(sid)))
|
// This byte has also been seen as 1
|
||||||
resp.WriteBytes([]byte(sid))
|
// The quest is also recognised as locked when this is 2
|
||||||
|
resp.WriteUint8(3)
|
||||||
|
} else {
|
||||||
|
resp.WriteUint8(0)
|
||||||
|
}
|
||||||
|
ps.Uint8(resp, sid, false)
|
||||||
}
|
}
|
||||||
bf.WriteUint16(uint16(joinable))
|
bf.WriteUint16(uint16(joinable))
|
||||||
bf.WriteBytes(resp.Data())
|
bf.WriteBytes(resp.Data())
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type Session struct {
|
|||||||
stageID string
|
stageID string
|
||||||
stage *Stage
|
stage *Stage
|
||||||
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
||||||
|
stagePass string // Temporary storage
|
||||||
charID uint32
|
charID uint32
|
||||||
logKey []byte
|
logKey []byte
|
||||||
sessionStart int64
|
sessionStart int64
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ type Stage struct {
|
|||||||
// These are clients that are CURRENTLY in the stage
|
// These are clients that are CURRENTLY in the stage
|
||||||
clients map[*Session]uint32
|
clients map[*Session]uint32
|
||||||
|
|
||||||
// Map of charID -> interface{}, only the key is used, value is always nil.
|
// Map of charID -> bool, key represents whether they are ready
|
||||||
// These are clients that aren't in the stage, but have reserved a slot (for quests, etc).
|
// These are clients that aren't in the stage, but have reserved a slot (for quests, etc).
|
||||||
reservedClientSlots map[uint32]interface{}
|
reservedClientSlots map[uint32]bool
|
||||||
|
|
||||||
// These are raw binary blobs that the stage owner sets,
|
// These are raw binary blobs that the stage owner sets,
|
||||||
// other clients expect the server to echo them back in the exact same format.
|
// other clients expect the server to echo them back in the exact same format.
|
||||||
@@ -66,7 +66,7 @@ func NewStage(ID string) *Stage {
|
|||||||
s := &Stage{
|
s := &Stage{
|
||||||
id: ID,
|
id: ID,
|
||||||
clients: make(map[*Session]uint32),
|
clients: make(map[*Session]uint32),
|
||||||
reservedClientSlots: make(map[uint32]interface{}),
|
reservedClientSlots: make(map[uint32]bool),
|
||||||
objects: make(map[uint32]*StageObject),
|
objects: make(map[uint32]*StageObject),
|
||||||
rawBinaryData: make(map[stageBinaryKey][]byte),
|
rawBinaryData: make(map[stageBinaryKey][]byte),
|
||||||
maxPlayers: 4,
|
maxPlayers: 4,
|
||||||
|
|||||||
Reference in New Issue
Block a user