From 4f889b1bf11211acf49698afdf72404dfc3c350d Mon Sep 17 00:00:00 2001 From: wish Date: Mon, 4 Dec 2023 00:00:40 +1100 Subject: [PATCH] initial refactor-servers commit --- common/token/token.go | 8 +++ config.json | 66 ++++++++++------------ config/config.go | 40 ++++++------- main.go | 66 ++++++++++++++-------- server/channelserver/sys_channel_server.go | 4 ++ server/entranceserver/entrance_server.go | 6 ++ server/entranceserver/make_resp.go | 33 ++++++----- 7 files changed, 122 insertions(+), 101 deletions(-) diff --git a/common/token/token.go b/common/token/token.go index c474fdaf5..95dda2181 100644 --- a/common/token/token.go +++ b/common/token/token.go @@ -1,6 +1,7 @@ package token import ( + crand "crypto/rand" "math/rand" "time" ) @@ -20,3 +21,10 @@ func Generate(length int) string { func RNG() *rand.Rand { return rand.New(rand.NewSource(time.Now().UnixNano())) } + +// RandBytes returns x random bytes +func RandBytes(x int) []byte { + y := make([]byte, x) + crand.Read(y) + return y +} diff --git a/config.json b/config.json index 5d66b9e33..ef9477faf 100644 --- a/config.json +++ b/config.json @@ -156,45 +156,37 @@ "Links": [] }, "Channel": { - "Enabled": true + "Enabled": true, + "Worlds": [ + { + "Name": "Novice", "Description": "Up to 2★", "IP": "", "Type": 3, "Recommended": 1, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}] + }, { + "Name": "Rookie", "Description": "Up to 4★", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}] + }, { + "Name": "Sincere", "Description": "All Quests", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}] + }, { + "Name": "Brave", "Description": "Only G Quests", "IP": "", "Type": 1, "Recommended": 5, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}] + }, { + "Name": "Noble", "Description": "All Quests", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}, {"MaxPlayers": 100}] + }, { + "Name": "Spirit", "Description": "All Quests", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}, {"MaxPlayers": 100}] + }, { + "Name": "Legend", "Description": "All Quests", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 100}] + }, { + "Name": "Fancy", "Description": "Minigames!", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0, + "Lands": [{"MaxPlayers": 80}] + } + ] }, "Entrance": { "Enabled": true, - "Port": 53310, - "Entries": [ - { - "Name": "Newbie", "Description": "", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54001, "MaxPlayers": 100 }, - { "Port": 54002, "MaxPlayers": 100 } - ] - }, { - "Name": "Normal", "Description": "", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54003, "MaxPlayers": 100 }, - { "Port": 54004, "MaxPlayers": 100 } - ] - }, { - "Name": "Cities", "Description": "", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54005, "MaxPlayers": 100 } - ] - }, { - "Name": "Tavern", "Description": "", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54006, "MaxPlayers": 100 } - ] - }, { - "Name": "Return", "Description": "", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54007, "MaxPlayers": 100 } - ] - }, { - "Name": "MezFes", "Description": "", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0, - "Channels": [ - { "Port": 54008, "MaxPlayers": 100 } - ] - } - ] + "Port": 53310 } } diff --git a/config/config.go b/config/config.go index 153cfedb3..a91ca70ae 100644 --- a/config/config.go +++ b/config/config.go @@ -228,35 +228,29 @@ type SignV2Link struct { type Channel struct { Enabled bool + Worlds []World +} + +type World struct { + IP string + Type uint8 + Season uint8 + Recommended uint8 + Name string + Description string + AllowedClientFlags uint32 + Lands []Land +} + +type Land struct { + Port uint16 + MaxPlayers uint16 } // Entrance holds the entrance server config. type Entrance struct { Enabled bool Port uint16 - Entries []EntranceServerInfo -} - -// EntranceServerInfo represents an entry in the serverlist. -type EntranceServerInfo struct { - IP string - Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar - Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue - Recommended uint8 // Something to do with server recommendation on 0, 3, and 5. - Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW). - Description string // Server description - // 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing? - // THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"! - AllowedClientFlags uint32 - - Channels []EntranceChannelInfo -} - -// EntranceChannelInfo represents an entry in a server's channel list. -type EntranceChannelInfo struct { - Port uint16 - MaxPlayers uint16 - CurrentPlayers uint16 } var ErupeConfig *Config diff --git a/main.go b/main.go index c56d90b0f..521cdda59 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "runtime/debug" + "slices" "syscall" "time" @@ -150,7 +151,7 @@ func main() { entranceServer = entranceserver.NewServer( &entranceserver.Config{ Logger: logger.Named("entrance"), - ErupeConfig: _config.ErupeConfig, + ErupeConfig: config, DB: db, }) err = entranceServer.Start() @@ -169,7 +170,7 @@ func main() { signServer = signserver.NewServer( &signserver.Config{ Logger: logger.Named("sign"), - ErupeConfig: _config.ErupeConfig, + ErupeConfig: config, DB: db, }) err = signServer.Start() @@ -187,7 +188,7 @@ func main() { newSignServer = signv2server.NewServer( &signv2server.Config{ Logger: logger.Named("sign"), - ErupeConfig: _config.ErupeConfig, + ErupeConfig: config, DB: db, }) err = newSignServer.Start() @@ -199,20 +200,20 @@ func main() { logger.Info("SignV2: Disabled") } - var channels []*channelserver.Server + var worlds [][]*channelserver.Server + var ports []uint16 if config.Channel.Enabled { channelQuery := "" - si := 0 - ci := 0 - count := 1 - for j, ee := range config.Entrance.Entries { - for i, ce := range ee.Channels { - sid := (4096 + si*256) + (16 + ci) + var count int + for j, ee := range config.Channel.Worlds { + var lands []*channelserver.Server + for i, ce := range ee.Lands { + sid := (4096 + j*256) + (16 + i) c := *channelserver.NewServer(&channelserver.Config{ ID: uint16(sid), - Logger: logger.Named("channel-" + fmt.Sprint(count)), - ErupeConfig: _config.ErupeConfig, + Logger: logger.Named("channel-" + fmt.Sprint(count+1)), + ErupeConfig: config, DB: db, DiscordBot: discordBot, }) @@ -221,28 +222,41 @@ func main() { } else { c.IP = ee.IP } - c.Port = ce.Port + if ce.Port == 0 { + for i := 0; ; i++ { + port := uint16(54001 + i) + if !slices.Contains(ports, port) { + ce.Port = port + break + } + } + } + if slices.Contains(ports, ce.Port) { + preventClose("Channel: Failed to start, duplicate port") + break + } else { + ports = append(ports, ce.Port) + c.Port = ce.Port + } c.GlobalID = fmt.Sprintf("%02d%02d", j+1, i+1) err = c.Start() if err != nil { preventClose(fmt.Sprintf("Channel: Failed to start, %s", err.Error())) } else { channelQuery += fmt.Sprintf(`INSERT INTO servers (server_id, current_players, world_name, world_description, land) VALUES (%d, 0, '%s', '%s', %d);`, sid, ee.Name, ee.Description, i+1) - channels = append(channels, &c) - logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, ce.Port)) - ci++ + lands = append(lands, &c) + logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, c.Port)) count++ } } - ci = 0 - si++ + worlds = append(worlds, lands) } // Register all servers in DB _ = db.MustExec(channelQuery) - for _, c := range channels { - c.Channels = channels + if config.Entrance.Enabled { + entranceServer.SetWorlds(worlds) } } @@ -256,8 +270,10 @@ func main() { if !config.DisableSoftCrash { for i := 0; i < 10; i++ { message := fmt.Sprintf("Shutting down in %d...", 10-i) - for _, c := range channels { - c.BroadcastChatMessage(message) + for _, w := range worlds { + for _, l := range w { + l.BroadcastChatMessage(message) + } } logger.Info(message) time.Sleep(time.Second) @@ -265,8 +281,10 @@ func main() { } if config.Channel.Enabled { - for _, c := range channels { - c.Shutdown() + for _, w := range worlds { + for _, l := range w { + l.Shutdown() + } } } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 70f52e461..58c51d0e9 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -419,3 +419,7 @@ func (s *Server) Season() uint8 { sid := int64(((s.ID & 0xFF00) - 4096) / 256) return uint8(((TimeAdjusted().Unix() / 86400) + sid) % 3) } + +func (s *Server) Players() uint16 { + return uint16(len(s.sessions)) +} diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go index 8b06be0e0..01695207e 100644 --- a/server/entranceserver/entrance_server.go +++ b/server/entranceserver/entrance_server.go @@ -2,6 +2,7 @@ package entranceserver import ( "encoding/hex" + "erupe-ce/server/channelserver" "fmt" "io" "net" @@ -19,6 +20,7 @@ type Server struct { sync.Mutex logger *zap.Logger erupeConfig *_config.Config + worlds [][]*channelserver.Server db *sqlx.DB listener net.Listener isShuttingDown bool @@ -68,6 +70,10 @@ func (s *Server) Shutdown() { s.listener.Close() } +func (s *Server) SetWorlds(c [][]*channelserver.Server) { + s.worlds = c +} + // acceptClients handles accepting new clients in a loop. func (s *Server) acceptClients() { for { diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 4b478fa24..2d63f781d 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/hex" "erupe-ce/common/stringsupport" + "erupe-ce/common/token" _config "erupe-ce/config" "fmt" "net" @@ -13,10 +14,8 @@ import ( ) func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { - serverInfos := config.Entrance.Entries bf := byteframe.NewByteFrame() - - for serverIdx, si := range serverInfos { + for i, si := range config.Channel.Worlds { // Prevent MezFes Worlds displaying on Z1 if config.RealClientMode <= _config.Z1 { if si.Type == 6 { @@ -29,7 +28,7 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } } - sid := (4096 + serverIdx*256) * 6000 + sid := (4096 + i*256) * 6000 if si.IP == "" { si.IP = config.Host } @@ -38,11 +37,11 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } else { bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) } - bf.WriteUint16(16 + uint16(serverIdx)) + bf.WriteUint16(16 + uint16(i)) bf.WriteUint16(0x0000) - bf.WriteUint16(uint16(len(si.Channels))) + bf.WriteUint16(uint16(len(si.Lands))) bf.WriteUint8(si.Type) - bf.WriteUint8(uint8(((channelserver.TimeAdjusted().Unix() / 86400) + int64(serverIdx)) % 3)) + bf.WriteUint8(uint8(((channelserver.TimeAdjusted().Unix() / 86400) + int64(i)) % 3)) if s.erupeConfig.RealClientMode >= _config.G1 { bf.WriteUint8(si.Recommended) } @@ -67,15 +66,15 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { bf.WriteUint32(si.AllowedClientFlags) } - for channelIdx, ci := range si.Channels { - sid = (4096 + serverIdx*256) + (16 + channelIdx) + for j, land := range si.Lands { + sid = (4096 + i*256) + (16 + j) if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 { bf.WriteUint16(_config.ErupeConfig.ProxyPort) } else { - bf.WriteUint16(ci.Port) + bf.WriteUint16(land.Port) } - bf.WriteUint16(16 + uint16(channelIdx)) - bf.WriteUint16(ci.MaxPlayers) + bf.WriteUint16(16 + uint16(j)) + bf.WriteUint16(land.MaxPlayers) var currentPlayers uint16 s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(¤tPlayers) bf.WriteUint16(currentPlayers) @@ -115,11 +114,11 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt } func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte { - serverInfos := config.Entrance.Entries + worlds := config.Channel.Worlds // Decrease by the number of MezFes Worlds var mf int if config.RealClientMode <= _config.Z1 { - for _, si := range serverInfos { + for _, si := range worlds { if si.Type == 6 { mf++ } @@ -128,7 +127,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte { // and Return Worlds var ret int if config.RealClientMode <= _config.G6 { - for _, si := range serverInfos { + for _, si := range worlds { if si.Type == 5 { ret++ } @@ -146,7 +145,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte { } bf := byteframe.NewByteFrame() - bf.WriteBytes(makeHeader(rawServerData, respType, uint16(len(serverInfos)-(mf+ret)), 0x00)) + bf.WriteBytes(makeHeader(rawServerData, respType, uint16(len(worlds)-(mf+ret)), token.RandBytes(1)[0])) return bf.Data() } @@ -172,5 +171,5 @@ func makeUsrResp(pkt []byte, s *Server) []byte { fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data())) } - return makeHeader(resp.Data(), "USR", userEntries, 0x00) + return makeHeader(resp.Data(), "USR", userEntries, token.RandBytes(1)[0]) }