mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 15:43:49 +01:00
fix(stage): fix race condition with stages.
This commit is contained in:
@@ -111,10 +111,20 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
if !s.userEnteredStage {
|
||||
s.userEnteredStage = true
|
||||
|
||||
// Lock server to safely iterate over sessions map
|
||||
// We need to copy the session list first to avoid holding the lock during packet building
|
||||
s.server.Lock()
|
||||
var sessionList []*Session
|
||||
for _, session := range s.server.sessions {
|
||||
if s == session {
|
||||
continue
|
||||
}
|
||||
sessionList = append(sessionList, session)
|
||||
}
|
||||
s.server.Unlock()
|
||||
|
||||
// Build packets for each session without holding the lock
|
||||
for _, session := range sessionList {
|
||||
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
|
||||
newNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(newNotif, s.clientContext)
|
||||
@@ -132,12 +142,22 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
if s.stage != nil { // avoids lock up when using bed for dream quests
|
||||
// Notify the client to duplicate the existing objects.
|
||||
s.logger.Info(fmt.Sprintf("Sending existing stage objects to %s", s.Name))
|
||||
|
||||
// Lock stage to safely iterate over objects map
|
||||
// We need to copy the objects list first to avoid holding the lock during packet building
|
||||
s.stage.RLock()
|
||||
var temp mhfpacket.MHFPacket
|
||||
var objectList []*Object
|
||||
for _, obj := range s.stage.objects {
|
||||
if obj.ownerCharID == s.charID {
|
||||
continue
|
||||
}
|
||||
objectList = append(objectList, obj)
|
||||
}
|
||||
s.stage.RUnlock()
|
||||
|
||||
// Build packets for each object without holding the lock
|
||||
var temp mhfpacket.MHFPacket
|
||||
for _, obj := range objectList {
|
||||
temp = &mhfpacket.MsgSysDuplicateObject{
|
||||
ObjID: obj.id,
|
||||
X: obj.x,
|
||||
@@ -149,7 +169,6 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
|
||||
newNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(newNotif, s.clientContext)
|
||||
}
|
||||
s.stage.RUnlock()
|
||||
}
|
||||
|
||||
// FIX: Always send stage transfer packet, even if empty.
|
||||
@@ -166,7 +185,12 @@ func destructEmptyStages(s *Session) {
|
||||
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" || stage.id[3:5] == "Ls" {
|
||||
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
|
||||
// Lock stage to safely check its client and reservation counts
|
||||
stage.Lock()
|
||||
isEmpty := len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0
|
||||
stage.Unlock()
|
||||
|
||||
if isEmpty {
|
||||
delete(s.server.stages, stage.id)
|
||||
s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
|
||||
}
|
||||
@@ -183,6 +207,7 @@ func removeSessionFromStage(s *Session) {
|
||||
delete(s.stage.clients, s)
|
||||
|
||||
// Collect objects to delete while holding lock
|
||||
// We must copy the objects to delete to avoid modifying the map while iterating
|
||||
s.logger.Info("Sending notification to old stage clients")
|
||||
var objectsToDelete []*Object
|
||||
for _, object := range s.stage.objects {
|
||||
@@ -209,6 +234,30 @@ func removeSessionFromStage(s *Session) {
|
||||
destructEmptySemaphores(s)
|
||||
}
|
||||
|
||||
func isStageFull(s *Session, StageID string) bool {
|
||||
s.server.Lock()
|
||||
stage, exists := s.server.stages[StageID]
|
||||
s.server.Unlock()
|
||||
|
||||
if exists {
|
||||
// Lock stage to safely check client counts
|
||||
// Read the values we need while holding RLock, then release immediately
|
||||
// to avoid deadlock with other functions that might hold server lock
|
||||
stage.RLock()
|
||||
reserved := len(stage.reservedClientSlots)
|
||||
clients := len(stage.clients)
|
||||
_, hasReservation := stage.reservedClientSlots[s.charID]
|
||||
maxPlayers := stage.maxPlayers
|
||||
stage.RUnlock()
|
||||
|
||||
if hasReservation {
|
||||
return false
|
||||
}
|
||||
return reserved+clients >= int(maxPlayers)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysEnterStage)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user