mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-27 10:03:06 +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:
58
server/channelserver/channel_registry.go
Normal file
58
server/channelserver/channel_registry.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// ChannelRegistry abstracts cross-channel operations behind an interface.
|
||||
// The default LocalChannelRegistry wraps the in-process []*Server slice.
|
||||
// Future implementations may use DB/Redis/NATS for multi-process deployments.
|
||||
type ChannelRegistry interface {
|
||||
// Worldcast broadcasts a packet to all sessions across all channels.
|
||||
Worldcast(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *Server)
|
||||
|
||||
// FindSessionByCharID looks up a session by character ID across all channels.
|
||||
FindSessionByCharID(charID uint32) *Session
|
||||
|
||||
// DisconnectUser disconnects all sessions belonging to the given character IDs.
|
||||
DisconnectUser(cids []uint32)
|
||||
|
||||
// FindChannelForStage searches all channels for a stage whose ID has the
|
||||
// given suffix and returns the owning channel's GlobalID, or "" if not found.
|
||||
FindChannelForStage(stageSuffix string) string
|
||||
|
||||
// SearchSessions searches sessions across all channels using a predicate,
|
||||
// returning up to max snapshot results.
|
||||
SearchSessions(predicate func(SessionSnapshot) bool, max int) []SessionSnapshot
|
||||
|
||||
// SearchStages searches stages across all channels with a prefix filter,
|
||||
// returning up to max snapshot results.
|
||||
SearchStages(stagePrefix string, max int) []StageSnapshot
|
||||
|
||||
// NotifyMailToCharID finds the session for charID and sends a mail notification.
|
||||
NotifyMailToCharID(charID uint32, sender *Session, mail *Mail)
|
||||
}
|
||||
|
||||
// SessionSnapshot is an immutable copy of session data taken under lock.
|
||||
type SessionSnapshot struct {
|
||||
CharID uint32
|
||||
Name string
|
||||
StageID string
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
UserBinary3 []byte // Copy of userBinaryParts index 3
|
||||
}
|
||||
|
||||
// StageSnapshot is an immutable copy of stage data taken under lock.
|
||||
type StageSnapshot struct {
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
StageID string
|
||||
ClientCount int
|
||||
Reserved int
|
||||
MaxPlayers uint16
|
||||
RawBinData0 []byte
|
||||
RawBinData1 []byte
|
||||
RawBinData3 []byte
|
||||
}
|
||||
Reference in New Issue
Block a user