rework stages

This commit is contained in:
wish
2022-07-22 12:56:24 +10:00
parent 97ceb25374
commit 1f4c2e8708
3 changed files with 160 additions and 184 deletions

View File

@@ -183,10 +183,6 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
panic(err)
}
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser {
CharID: s.charID,
}, s)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -1,12 +1,11 @@
package channelserver
import (
"fmt"
"time"
"strings"
"erupe-ce/network/mhfpacket"
"erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring"
"erupe-ce/network/mhfpacket"
"go.uber.org/zap"
)
@@ -15,7 +14,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
s.server.Lock()
defer s.server.Unlock()
if _, exists := s.server.stages[pkt.StageID]; exists {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} else {
stage := NewStage(pkt.StageID)
stage.maxPlayers = uint16(pkt.PlayerCount)
@@ -27,36 +26,33 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {}
func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
// Remove this session from old stage clients list and put myself in the new one.
s.server.stagesLock.Lock()
newStage, gotNewStage := s.server.stages[stageID]
s.server.stagesLock.Unlock()
s.server.Lock()
stage, exists := s.server.stages[stageID]
s.server.Unlock()
if s.stage != nil {
removeSessionFromStage(s)
if exists {
stage.Lock()
stage.clients[s] = s.charID
stage.Unlock()
} else { // Create new stage object
s.server.Lock()
s.server.stages[stageID] = NewStage(stageID)
stage = s.server.stages[stageID]
s.server.Unlock()
stage.Lock()
stage.clients[s] = s.charID
stage.Unlock()
}
// Add the new stage.
if gotNewStage {
newStage.Lock()
newStage.clients[s] = s.charID
newStage.Unlock()
} else {
// Fix stages
s.logger.Info("Fix Map Appliqued")
s.server.stagesLock.Lock()
s.server.stages[stageID] = NewStage(stageID)
newStage = s.server.stages[stageID]
s.server.stagesLock.Unlock()
newStage.Lock()
newStage.clients[s] = s.charID
newStage.Unlock()
// Ensure this session no longer belongs to reservations.
if s.stage != nil {
removeSessionFromStage(s)
}
// Save our new stage ID and pointer to the new stage itself.
s.Lock()
s.stageID = string(stageID)
s.stage = newStage
s.stage = s.server.stages[stageID]
s.Unlock()
// Tell the client to cleanup its current stage objects.
@@ -65,138 +61,133 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
// Confirm the stage entry.
doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00})
// Notify existing stage clients that this new client has entered.
if s.stage != nil { // avoids lock up when using bed for dream quests
var pkt mhfpacket.MHFPacket
// Notify the entree client about all of the existing clients in the stage.
s.logger.Info("Notifying entree about existing stage clients")
clientNotif := byteframe.NewByteFrame()
s.server.Lock()
s.server.BroadcastMHF(&mhfpacket.MsgSysDeleteUser{
CharID: s.charID,
}, s)
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser {
CharID: s.charID,
}, s)
for session := range s.server.sessions {
session := s.server.sessions[session]
// Send existing players back to the client
pkt = &mhfpacket.MsgSysInsertUser{
CharID: session.charID,
}
clientNotif.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(clientNotif, session.clientContext)
for i := 1; i <= 3; i++ {
pkt = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i),
}
clientNotif.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(clientNotif, session.clientContext)
}
}
s.server.Unlock()
clientNotif.WriteUint16(0x0010) // End it.
s.QueueSend(clientNotif.Data())
// Notify the client to duplicate the existing objects.
s.logger.Info("Notifying entree about existing stage objects")
s.logger.Info("Sending existing stage objects")
clientDupObjNotif := byteframe.NewByteFrame()
s.stage.RLock()
for _, obj := range s.stage.objects {
cur := &mhfpacket.MsgSysDuplicateObject{
ObjID: obj.id,
X: obj.x,
Y: obj.y,
Z: obj.z,
Unk0: 0,
OwnerCharID: obj.ownerCharID,
}
clientDupObjNotif.WriteUint16(uint16(cur.Opcode()))
cur.Build(clientDupObjNotif, s.clientContext)
cur := &mhfpacket.MsgSysDuplicateObject{
ObjID: obj.id,
X: obj.x,
Y: obj.y,
Z: obj.z,
Unk0: 0,
OwnerCharID: obj.ownerCharID,
}
clientDupObjNotif.WriteUint16(uint16(cur.Opcode()))
cur.Build(clientDupObjNotif, s.clientContext)
}
s.stage.RUnlock()
clientDupObjNotif.WriteUint16(0x0010) // End it.
s.QueueSend(clientDupObjNotif.Data())
if len(clientDupObjNotif.Data()) > 2 {
s.QueueSend(clientDupObjNotif.Data())
}
}
}
func removeEmptyStages(s *Session) {
s.server.Lock()
for sid, stage := range s.server.stages {
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
if strings.HasPrefix(sid, "sl1Qs") || strings.HasPrefix(sid, "sl2Qs") || strings.HasPrefix(sid, "sl3Qs") {
delete(s.server.stages, sid)
defer s.server.Unlock()
for _, stage := range s.server.stages {
// Destroy empty Quest/My series/Guild stages.
if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" {
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
delete(s.server.stages, stage.id)
s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
}
}
}
s.server.Unlock()
}
func removeSessionFromStage(s *Session) {
s.stage.Lock()
defer s.stage.Unlock()
// Remove client from old stage.
s.stage.Lock()
delete(s.stage.clients, s)
delete(s.stage.reservedClientSlots, s.charID)
// Remove client from all reservations
s.server.Lock()
for _, stage := range s.server.stages {
if _, exists := stage.reservedClientSlots[s.charID]; exists {
delete(stage.reservedClientSlots, s.charID)
}
}
s.server.Unlock()
// Delete old stage objects owned by the client.
s.logger.Info("Sending MsgSysDeleteObject to old stage clients")
s.logger.Info("Sending notification to old stage clients")
for objID, stageObject := range s.stage.objects {
if stageObject.ownerCharID == s.charID {
// Broadcast the deletion to clients in the stage.
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{
clientNotif := byteframe.NewByteFrame()
var pkt mhfpacket.MHFPacket
pkt = &mhfpacket.MsgSysDeleteObject{
ObjID: stageObject.id,
}, s)
}
clientNotif.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(clientNotif, s.clientContext)
clientNotif.WriteUint16(0x0010)
for client, _ := range s.stage.clients {
client.QueueSend(clientNotif.Data())
}
// TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't.
// Actually delete it form the objects map.
// Actually delete it from the objects map.
delete(s.stage.objects, objID)
}
}
for objListID, stageObjectList := range s.stage.objectList {
if stageObjectList.charid == s.charID {
//Added to prevent duplicates from flooding ObjectMap and causing server hangs
s.stage.objectList[objListID].status=false
s.stage.objectList[objListID].charid=0
// Added to prevent duplicates from flooding ObjectMap and causing server hangs
s.stage.objectList[objListID].status = false
s.stage.objectList[objListID].charid = 0
}
}
s.stage.Unlock()
removeEmptyStages(s)
}
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage)
fmt.Printf("The Stage is %s\n",pkt.StageID)
// Push our current stage ID to the movement stack before entering another one.
s.Lock()
s.stageMoveStack.Push(s.stageID)
s.Unlock()
s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{})
if s.reservationStage != nil {
s.reservationStage = nil
}
if pkt.StageID == "sl1Ns200p0a0u0" { // First entry
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{
CharID: s.charID,
}, s)
var temp mhfpacket.MHFPacket
loginNotif := byteframe.NewByteFrame()
s.server.Lock()
for _, session := range s.server.sessions {
if s == session {
continue
}
temp = &mhfpacket.MsgSysInsertUser{
CharID: session.charID,
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
for i := 1; i <= 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i),
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
}
}
s.server.Unlock()
loginNotif.WriteUint16(0x0010) // End it.
if len(loginNotif.Data()) > 2 {
s.QueueSend(loginNotif.Data())
}
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
}
func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysBackStage)
if s.stage != nil {
removeSessionFromStage(s)
}
// Transfer back to the saved stage ID before the previous move or enter.
s.Lock()
backStage, err := s.stageMoveStack.Pop()
@@ -260,24 +251,17 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
if len(stage.password) > 0 {
// s.logger.Debug("", zap.String("stgpw", stage.password), zap.String("usrpw", s.stagePass))
if stage.password == s.stagePass {
stage.reservedClientSlots[s.charID] = false
s.Lock()
s.reservationStage = stage
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
if stage.password != s.stagePass {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
} else {
stage.reservedClientSlots[s.charID] = false
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
s.Lock()
s.reservationStage = stage
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
stage.reservedClientSlots[s.charID] = false
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
s.Lock()
s.reservationStage = stage
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
@@ -308,12 +292,13 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
s.Unlock()
if stage != nil {
stage.Lock()
// Will only exist if host.
if _, exists := stage.reservedClientSlots[s.charID]; exists {
stage.password = pkt.Password
}
stage.Unlock()
} else {
// Store for use on next ReserveStage
// Store for use on next ReserveStage.
s.Lock()
s.stagePass = pkt.Password
s.Unlock()
@@ -322,66 +307,66 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetStageBinary)
// Try to get the stage
stageID := pkt.StageID
s.server.stagesLock.Lock()
stage, gotStage := s.server.stages[stageID]
s.server.stagesLock.Unlock()
// If we got the stage, lock and set the data.
if gotStage {
if stage, exists := s.server.stages[pkt.StageID]; exists {
stage.Lock()
stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload
stage.Unlock()
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", stageID))
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
s.logger.Debug("handleMsgSysSetStageBinary Done!")
}
func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysGetStageBinary)
// Try to get the stage
stageID := pkt.StageID
s.server.stagesLock.Lock()
stage, gotStage := s.server.stages[stageID]
s.server.stagesLock.Unlock()
// If we got the stage, lock and try to get the data.
var stageBinary []byte
var gotBinary bool
if gotStage {
if stage, exists := s.server.stages[pkt.StageID]; exists {
stage.Lock()
stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
if binaryData, exists := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]; exists {
doAckBufSucceed(s, pkt.AckHandle, binaryData)
} else if pkt.BinaryType1 == 4 {
// Unknown binary type that is supposedly generated server side
// Temporary response
doAckBufSucceed(s, pkt.AckHandle, []byte{})
} else {
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{})
}
stage.Unlock()
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", stageID))
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
if gotBinary {
doAckBufSucceed(s, pkt.AckHandle, stageBinary)
} else if pkt.BinaryType1 == 4 {
// This particular type seems to be expecting data that isn't set
// is it required before the party joining can be completed
//s.QueueAck(pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x10})
// TODO(Andoryuuta): This doesn't fit a normal ack packet? where is this from?
// This would be a buffered(0x01), non-error(0x00), with no data payload (size 0x00, 0x00) packet.
// but for some reason has a 0x10 on the end that the client shouldn't parse?
doAckBufSucceed(s, pkt.AckHandle, []byte{}) // Without the previous 0x10 suffix
} else {
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{})
}
s.logger.Debug("MsgSysGetStageBinary Done!")
}
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
if stage, exists := s.server.stages[pkt.StageID]; exists {
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
}
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))
time.Sleep(1 * time.Second)
continue
}
}
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
s.logger.Debug("MsgSysWaitStageBinary Done!")
}
func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnumerateStage)
@@ -409,14 +394,10 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
joinable++
resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players.
resp.WriteUint16(0) // Unknown value
var hasDeparted uint16
if stage.hasDeparted {
hasDeparted = 1
}
resp.WriteUint16(hasDeparted) // HasDeparted.
resp.WriteUint16(stage.maxPlayers) // Max players.
resp.WriteUint16(0) // Unk
resp.WriteUint8(0) // Unk
resp.WriteBool(len(stage.clients) > 0) // Has departed.
resp.WriteUint16(stage.maxPlayers) // Max players.
if len(stage.password) > 0 {
// This byte has also been seen as 1
// The quest is also recognised as locked when this is 2

View File

@@ -55,10 +55,9 @@ type Stage struct {
// other clients expect the server to echo them back in the exact same format.
rawBinaryData map[stageBinaryKey][]byte
maxPlayers uint16
hasDeparted bool
password string
createdAt string
maxPlayers uint16
password string
createdAt string
}
// NewStage creates a new stage with intialized values.
@@ -71,7 +70,7 @@ func NewStage(ID string) *Stage {
rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 4,
gameObjectCount: 1,
objectList: make(map[uint8]*ObjectMap),
objectList: make(map[uint8]*ObjectMap),
createdAt: time.Now().Format("01-02-2006 15:04:05"),
}
s.InitObjectList()