mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 17:05:03 +01:00
Merge pull request #12 from Andoryuuta/fix-mp-quests
Fix multiplayer questing
This commit is contained in:
@@ -20,14 +20,19 @@ func (m *MsgSysCastedBinary) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgSysCastedBinary) Parse(bf *byteframe.ByteFrame) error {
|
func (m *MsgSysCastedBinary) Parse(bf *byteframe.ByteFrame) error {
|
||||||
panic("Not implemented")
|
m.CharID = bf.ReadUint32()
|
||||||
|
m.Type0 = bf.ReadUint8()
|
||||||
|
m.Type1 = bf.ReadUint8()
|
||||||
|
dataSize := bf.ReadUint16()
|
||||||
|
m.RawDataPayload = bf.ReadBytes(uint(dataSize))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
func (m *MsgSysCastedBinary) Build(bf *byteframe.ByteFrame) error {
|
func (m *MsgSysCastedBinary) Build(bf *byteframe.ByteFrame) error {
|
||||||
bf.WriteUint32(m.CharID)
|
bf.WriteUint32(m.CharID)
|
||||||
bf.WriteUint8(m.Type0)
|
bf.WriteUint8(m.Type0)
|
||||||
bf.WriteUint8(m.Type0)
|
bf.WriteUint8(m.Type1)
|
||||||
bf.WriteUint16(uint16(len(m.RawDataPayload)))
|
bf.WriteUint16(uint16(len(m.RawDataPayload)))
|
||||||
bf.WriteBytes(m.RawDataPayload)
|
bf.WriteBytes(m.RawDataPayload)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// MsgSysUnreserveStage represents the MSG_SYS_UNRESERVE_STAGE
|
// MsgSysUnreserveStage represents the MSG_SYS_UNRESERVE_STAGE
|
||||||
type MsgSysUnreserveStage struct{}
|
type MsgSysUnreserveStage struct {
|
||||||
|
// Contains no fields.
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgSysUnreserveStage) Opcode() network.PacketID {
|
func (m *MsgSysUnreserveStage) Opcode() network.PacketID {
|
||||||
@@ -15,7 +17,7 @@ func (m *MsgSysUnreserveStage) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgSysUnreserveStage) Parse(bf *byteframe.ByteFrame) error {
|
func (m *MsgSysUnreserveStage) Parse(bf *byteframe.ByteFrame) error {
|
||||||
panic("Not implemented")
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -193,17 +193,18 @@ func handleMsgSysPing(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysCastBinary)
|
pkt := p.(*mhfpacket.MsgSysCastBinary)
|
||||||
|
|
||||||
|
// Simply forward the packet to all the other clients.
|
||||||
|
// (The client never uses Type0 upon receiving)
|
||||||
|
// TODO(Andoryuuta): Does this broadcast need to be limited? (world, stage, guild, etc).
|
||||||
|
resp := &mhfpacket.MsgSysCastedBinary{
|
||||||
|
CharID: s.charID,
|
||||||
|
Type0: pkt.Type0,
|
||||||
|
Type1: pkt.Type1,
|
||||||
|
RawDataPayload: pkt.RawDataPayload,
|
||||||
|
}
|
||||||
|
s.server.BroadcastMHF(resp, s)
|
||||||
|
|
||||||
if pkt.Type0 == 3 && pkt.Type1 == 1 {
|
if pkt.Type0 == 3 && pkt.Type1 == 1 {
|
||||||
fmt.Println("Got chat message!")
|
|
||||||
|
|
||||||
resp := &mhfpacket.MsgSysCastedBinary{
|
|
||||||
CharID: s.charID,
|
|
||||||
Type0: 1,
|
|
||||||
Type1: 1,
|
|
||||||
RawDataPayload: pkt.RawDataPayload,
|
|
||||||
}
|
|
||||||
s.server.BroadcastMHF(resp, s)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Made the inside of the casted binary
|
// Made the inside of the casted binary
|
||||||
payload := byteframe.NewByteFrame()
|
payload := byteframe.NewByteFrame()
|
||||||
@@ -538,16 +539,60 @@ 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)
|
||||||
|
|
||||||
fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID)
|
stageID := stripNullTerminator(pkt.StageID)
|
||||||
|
fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%v\n", pkt.Unk0, stageID)
|
||||||
|
|
||||||
// TODO(Andoryuuta): Add proper player-slot reservations for stages.
|
// Try to get the stage
|
||||||
|
s.server.stagesLock.Lock()
|
||||||
|
stage, gotStage := s.server.stages[stageID]
|
||||||
|
s.server.stagesLock.Unlock()
|
||||||
|
|
||||||
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
if !gotStage {
|
||||||
|
s.logger.Fatal("Failed to get stage", zap.String("StageID", stageID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to reserve a slot, fail if full.
|
||||||
|
stage.Lock()
|
||||||
|
defer stage.Unlock()
|
||||||
|
|
||||||
|
if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
|
||||||
|
// Add the charID to the stage's reservation map
|
||||||
|
stage.reservedClientSlots[s.charID] = nil
|
||||||
|
|
||||||
|
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
|
||||||
|
s.Lock()
|
||||||
|
s.reservationStage = stage
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
|
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
} else {
|
||||||
|
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
// Clear the saved reservation stage
|
||||||
|
s.Lock()
|
||||||
|
stage := s.reservationStage
|
||||||
|
if stage != nil {
|
||||||
|
s.reservationStage = nil
|
||||||
|
}
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {}
|
// Remove the charID from the stage's reservation map
|
||||||
|
if stage != nil {
|
||||||
|
stage.Lock()
|
||||||
|
_, exists := stage.reservedClientSlots[s.charID]
|
||||||
|
if exists {
|
||||||
|
delete(stage.reservedClientSlots, s.charID)
|
||||||
|
}
|
||||||
|
stage.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
// TODO(Andoryuuta): Implement me!
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
|
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
|
||||||
@@ -559,19 +604,31 @@ func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
stage, gotStage := s.server.stages[stageID]
|
stage, gotStage := s.server.stages[stageID]
|
||||||
s.server.stagesLock.Unlock()
|
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?
|
||||||
|
doSizedAckResp(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.
|
// If we got the stage, lock and try to get the data.
|
||||||
var stageBinary []byte
|
var stageBinary []byte
|
||||||
var gotBinary bool
|
var gotBinary bool
|
||||||
if gotStage {
|
if gotStage {
|
||||||
for {
|
for {
|
||||||
|
s.logger.Debug("MsgSysWaitStageBinary before lock and get stage")
|
||||||
stage.Lock()
|
stage.Lock()
|
||||||
stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
|
stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
|
||||||
stage.Unlock()
|
stage.Unlock()
|
||||||
|
s.logger.Debug("MsgSysWaitStageBinary after lock and get stage")
|
||||||
|
|
||||||
if gotBinary {
|
if gotBinary {
|
||||||
doSizedAckResp(s, pkt.AckHandle, stageBinary)
|
doSizedAckResp(s, pkt.AckHandle, stageBinary)
|
||||||
break
|
break
|
||||||
} else {
|
} 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.
|
// Couldn't get binary, sleep for some time and try again.
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
continue
|
continue
|
||||||
@@ -661,16 +718,24 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
stage.RLock()
|
stage.RLock()
|
||||||
|
|
||||||
// TODO(Andoryuuta): Add proper player-slot reservations for stages.
|
// TODO(Andoryuuta): Is only the reservations needed? Do clients send this packet for mezeporta as well?
|
||||||
if len(stage.clients) >= 1 {
|
|
||||||
resp.WriteUint16(uint16(len(stage.clients))) // Client count
|
// Make a map to deduplicate the charIDs between the unreserved clients and the reservations.
|
||||||
for session := range stage.clients {
|
deduped := make(map[uint32]interface{})
|
||||||
resp.WriteUint32(session.charID) // Client represented by charID
|
|
||||||
}
|
// Add the charIDs
|
||||||
} else {
|
for session := range stage.clients {
|
||||||
// Just give our client.
|
deduped[session.charID] = nil
|
||||||
resp.WriteUint16(1)
|
}
|
||||||
resp.WriteUint32(s.charID)
|
|
||||||
|
for charid := range stage.reservedClientSlots {
|
||||||
|
deduped[charid] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the deduplicated response
|
||||||
|
resp.WriteUint16(uint16(len(deduped))) // Client count
|
||||||
|
for charid := range deduped {
|
||||||
|
resp.WriteUint32(charid)
|
||||||
}
|
}
|
||||||
|
|
||||||
stage.RUnlock()
|
stage.RUnlock()
|
||||||
@@ -682,21 +747,28 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysEnumerateStage)
|
pkt := p.(*mhfpacket.MsgSysEnumerateStage)
|
||||||
|
|
||||||
// Read-lock the stages.
|
// Read-lock the server stage map.
|
||||||
s.server.stagesLock.RLock()
|
s.server.stagesLock.RLock()
|
||||||
defer s.server.stagesLock.RUnlock()
|
defer s.server.stagesLock.RUnlock()
|
||||||
|
|
||||||
// Build the response
|
// Build the response
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint16(uint16(len(s.server.stages)))
|
resp.WriteUint16(uint16(len(s.server.stages)))
|
||||||
for sid := range s.server.stages {
|
for sid, stage := range s.server.stages {
|
||||||
// Found parsing code, field sizes are correct, but unknown purposes still.
|
stage.RLock()
|
||||||
//resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00})
|
defer stage.RUnlock()
|
||||||
resp.WriteUint16(1) // Current players.
|
|
||||||
resp.WriteUint16(0) // Unknown value
|
resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Current players.
|
||||||
resp.WriteUint16(0) // HasDeparted.
|
resp.WriteUint16(0) // Unknown value
|
||||||
resp.WriteUint16(4) // Max players.
|
|
||||||
resp.WriteUint8(2) // Password protected.
|
var hasDeparted uint16
|
||||||
|
if stage.hasDeparted {
|
||||||
|
hasDeparted = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.WriteUint16(hasDeparted) // HasDeparted.
|
||||||
|
resp.WriteUint16(stage.maxPlayers) // Max players.
|
||||||
|
resp.WriteBool(len(stage.password) > 0) // Password protected.
|
||||||
resp.WriteUint8(uint8(len(sid)))
|
resp.WriteUint8(uint8(len(sid)))
|
||||||
resp.WriteBytes([]byte(sid))
|
resp.WriteBytes([]byte(sid))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ type Session struct {
|
|||||||
cryptConn *network.CryptConn
|
cryptConn *network.CryptConn
|
||||||
sendPackets chan []byte
|
sendPackets chan []byte
|
||||||
|
|
||||||
stageID string
|
stageID string
|
||||||
stage *Stage
|
stage *Stage
|
||||||
charID uint32
|
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
||||||
logKey []byte
|
charID uint32
|
||||||
|
logKey []byte
|
||||||
|
|
||||||
// A stack containing the stage movement history (push on enter/move, pop on back)
|
// A stack containing the stage movement history (push on enter/move, pop on back)
|
||||||
stageMoveStack *stringstack.StringStack
|
stageMoveStack *stringstack.StringStack
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ type StageObject struct {
|
|||||||
x, y, z float32
|
x, y, z float32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stageBinaryKey is a struct used as a map key for identifying a stage binary part.
|
||||||
type stageBinaryKey struct {
|
type stageBinaryKey struct {
|
||||||
id0 uint8
|
id0 uint8
|
||||||
id1 uint8
|
id1 uint8
|
||||||
@@ -23,21 +24,43 @@ type stageBinaryKey struct {
|
|||||||
// Stage holds stage-specific information
|
// Stage holds stage-specific information
|
||||||
type Stage struct {
|
type Stage struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
id string // Stage ID string
|
|
||||||
gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation.
|
// Stage ID string
|
||||||
objects map[uint32]*StageObject // Map of ObjID -> StageObject
|
id string
|
||||||
clients map[*Session]uint32 // Map of session -> charID
|
|
||||||
rawBinaryData map[stageBinaryKey][]byte // Raw binary data set by the client.
|
// Total count of objects ever created for this stage. Used for ObjID generation.
|
||||||
|
gameObjectCount uint32
|
||||||
|
|
||||||
|
// Map of ObjID -> StageObject
|
||||||
|
objects map[uint32]*StageObject
|
||||||
|
|
||||||
|
// Map of session -> charID.
|
||||||
|
// These are clients that are CURRENTLY in the stage
|
||||||
|
clients map[*Session]uint32
|
||||||
|
|
||||||
|
// Map of charID -> interface{}, only the key is used, value is always nil.
|
||||||
|
// These are clients that aren't in the stage, but have reserved a slot (for quests, etc).
|
||||||
|
reservedClientSlots map[uint32]interface{}
|
||||||
|
|
||||||
|
// These are raw binary blobs that the stage owner sets,
|
||||||
|
// other clients expect the server to echo them back in the exact same format.
|
||||||
|
rawBinaryData map[stageBinaryKey][]byte
|
||||||
|
|
||||||
|
maxPlayers uint16
|
||||||
|
hasDeparted bool
|
||||||
|
password string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStage creates a new stage with intialized values.
|
// NewStage creates a new stage with intialized values.
|
||||||
func NewStage(ID string) *Stage {
|
func NewStage(ID string) *Stage {
|
||||||
s := &Stage{
|
s := &Stage{
|
||||||
id: ID,
|
id: ID,
|
||||||
objects: make(map[uint32]*StageObject),
|
objects: make(map[uint32]*StageObject),
|
||||||
clients: make(map[*Session]uint32),
|
clients: make(map[*Session]uint32),
|
||||||
rawBinaryData: make(map[stageBinaryKey][]byte),
|
reservedClientSlots: make(map[uint32]interface{}),
|
||||||
gameObjectCount: 1,
|
rawBinaryData: make(map[stageBinaryKey][]byte),
|
||||||
|
maxPlayers: 4,
|
||||||
|
gameObjectCount: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|||||||
Reference in New Issue
Block a user