mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-26 01:23:13 +01:00
feat(channelserver): decouple channel servers for independent operation (#33)
Enable multiple Erupe instances to share a single PostgreSQL database without destroying each other's state, fix existing data races in cross-channel access, and lay groundwork for future distributed channel server deployments. Phase 1 — DB safety: - Scope DELETE FROM servers/sign_sessions to this instance's server IDs - Fix ci++ bug where failed channel start shifted subsequent IDs Phase 2 — Fix data races in cross-channel access: - Lock sessions map in FindSessionByCharID and DisconnectUser - Lock stagesLock in handleMsgSysLockGlobalSema - Snapshot sessions/stages under lock in TransitMessage types 1-4 - Lock channel when finding mail notification targets Phase 3 — ChannelRegistry interface: - Define ChannelRegistry interface with 7 cross-channel operations - Implement LocalChannelRegistry with proper locking - Add SessionSnapshot/StageSnapshot immutable copy types - Delegate WorldcastMHF, FindSessionByCharID, DisconnectUser to Registry - Migrate LockGlobalSema and guild mail handlers to use Registry - Add comprehensive tests including concurrent access Phase 4 — Per-channel enable/disable: - Add Enabled *bool to EntranceChannelInfo (nil defaults to true) - Skip disabled channels in startup loop, preserving ID stability - Add IsEnabled() helper with backward-compatible default - Update config.example.json with Enabled field
This commit is contained in:
@@ -37,6 +37,7 @@ type userBinaryPartID struct {
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
Channels []*Server
|
||||
Registry ChannelRegistry
|
||||
ID uint16
|
||||
GlobalID string
|
||||
IP string
|
||||
@@ -271,6 +272,10 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session)
|
||||
|
||||
// WorldcastMHF broadcasts a packet to all sessions across all channel servers.
|
||||
func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *Server) {
|
||||
if s.Registry != nil {
|
||||
s.Registry.Worldcast(pkt, ignoredSession, ignoredChannel)
|
||||
return
|
||||
}
|
||||
for _, c := range s.Channels {
|
||||
if c == ignoredChannel {
|
||||
continue
|
||||
@@ -317,12 +322,18 @@ func (s *Server) DiscordScreenShotSend(charName string, title string, descriptio
|
||||
|
||||
// FindSessionByCharID looks up a session by character ID across all channels.
|
||||
func (s *Server) FindSessionByCharID(charID uint32) *Session {
|
||||
if s.Registry != nil {
|
||||
return s.Registry.FindSessionByCharID(charID)
|
||||
}
|
||||
for _, c := range s.Channels {
|
||||
c.Lock()
|
||||
for _, session := range c.sessions {
|
||||
if session.charID == charID {
|
||||
c.Unlock()
|
||||
return session
|
||||
}
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -341,7 +352,12 @@ func (s *Server) DisconnectUser(uid uint32) {
|
||||
cids = append(cids, cid)
|
||||
}
|
||||
}
|
||||
if s.Registry != nil {
|
||||
s.Registry.DisconnectUser(cids)
|
||||
return
|
||||
}
|
||||
for _, c := range s.Channels {
|
||||
c.Lock()
|
||||
for _, session := range c.sessions {
|
||||
for _, cid := range cids {
|
||||
if session.charID == cid {
|
||||
@@ -350,6 +366,7 @@ func (s *Server) DisconnectUser(uid uint32) {
|
||||
}
|
||||
}
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user