1 Commits

Author SHA1 Message Date
wish
4f889b1bf1 initial refactor-servers commit 2023-12-04 00:00:40 +11:00
7 changed files with 122 additions and 101 deletions

View File

@@ -1,6 +1,7 @@
package token package token
import ( import (
crand "crypto/rand"
"math/rand" "math/rand"
"time" "time"
) )
@@ -20,3 +21,10 @@ func Generate(length int) string {
func RNG() *rand.Rand { func RNG() *rand.Rand {
return rand.New(rand.NewSource(time.Now().UnixNano())) 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
}

View File

@@ -156,45 +156,37 @@
"Links": [] "Links": []
}, },
"Channel": { "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": { "Entrance": {
"Enabled": true, "Enabled": true,
"Port": 53310, "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 }
]
}
]
} }
} }

View File

@@ -228,35 +228,29 @@ type SignV2Link struct {
type Channel struct { type Channel struct {
Enabled bool 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. // Entrance holds the entrance server config.
type Entrance struct { type Entrance struct {
Enabled bool Enabled bool
Port uint16 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 var ErupeConfig *Config

66
main.go
View File

@@ -7,6 +7,7 @@ import (
"os" "os"
"os/signal" "os/signal"
"runtime/debug" "runtime/debug"
"slices"
"syscall" "syscall"
"time" "time"
@@ -150,7 +151,7 @@ func main() {
entranceServer = entranceserver.NewServer( entranceServer = entranceserver.NewServer(
&entranceserver.Config{ &entranceserver.Config{
Logger: logger.Named("entrance"), Logger: logger.Named("entrance"),
ErupeConfig: _config.ErupeConfig, ErupeConfig: config,
DB: db, DB: db,
}) })
err = entranceServer.Start() err = entranceServer.Start()
@@ -169,7 +170,7 @@ func main() {
signServer = signserver.NewServer( signServer = signserver.NewServer(
&signserver.Config{ &signserver.Config{
Logger: logger.Named("sign"), Logger: logger.Named("sign"),
ErupeConfig: _config.ErupeConfig, ErupeConfig: config,
DB: db, DB: db,
}) })
err = signServer.Start() err = signServer.Start()
@@ -187,7 +188,7 @@ func main() {
newSignServer = signv2server.NewServer( newSignServer = signv2server.NewServer(
&signv2server.Config{ &signv2server.Config{
Logger: logger.Named("sign"), Logger: logger.Named("sign"),
ErupeConfig: _config.ErupeConfig, ErupeConfig: config,
DB: db, DB: db,
}) })
err = newSignServer.Start() err = newSignServer.Start()
@@ -199,20 +200,20 @@ func main() {
logger.Info("SignV2: Disabled") logger.Info("SignV2: Disabled")
} }
var channels []*channelserver.Server var worlds [][]*channelserver.Server
var ports []uint16
if config.Channel.Enabled { if config.Channel.Enabled {
channelQuery := "" channelQuery := ""
si := 0 var count int
ci := 0 for j, ee := range config.Channel.Worlds {
count := 1 var lands []*channelserver.Server
for j, ee := range config.Entrance.Entries { for i, ce := range ee.Lands {
for i, ce := range ee.Channels { sid := (4096 + j*256) + (16 + i)
sid := (4096 + si*256) + (16 + ci)
c := *channelserver.NewServer(&channelserver.Config{ c := *channelserver.NewServer(&channelserver.Config{
ID: uint16(sid), ID: uint16(sid),
Logger: logger.Named("channel-" + fmt.Sprint(count)), Logger: logger.Named("channel-" + fmt.Sprint(count+1)),
ErupeConfig: _config.ErupeConfig, ErupeConfig: config,
DB: db, DB: db,
DiscordBot: discordBot, DiscordBot: discordBot,
}) })
@@ -221,28 +222,41 @@ func main() {
} else { } else {
c.IP = ee.IP 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) c.GlobalID = fmt.Sprintf("%02d%02d", j+1, i+1)
err = c.Start() err = c.Start()
if err != nil { if err != nil {
preventClose(fmt.Sprintf("Channel: Failed to start, %s", err.Error())) preventClose(fmt.Sprintf("Channel: Failed to start, %s", err.Error()))
} else { } 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) 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) lands = append(lands, &c)
logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, ce.Port)) logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, c.Port))
ci++
count++ count++
} }
} }
ci = 0 worlds = append(worlds, lands)
si++
} }
// Register all servers in DB // Register all servers in DB
_ = db.MustExec(channelQuery) _ = db.MustExec(channelQuery)
for _, c := range channels { if config.Entrance.Enabled {
c.Channels = channels entranceServer.SetWorlds(worlds)
} }
} }
@@ -256,8 +270,10 @@ func main() {
if !config.DisableSoftCrash { if !config.DisableSoftCrash {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
message := fmt.Sprintf("Shutting down in %d...", 10-i) message := fmt.Sprintf("Shutting down in %d...", 10-i)
for _, c := range channels { for _, w := range worlds {
c.BroadcastChatMessage(message) for _, l := range w {
l.BroadcastChatMessage(message)
}
} }
logger.Info(message) logger.Info(message)
time.Sleep(time.Second) time.Sleep(time.Second)
@@ -265,8 +281,10 @@ func main() {
} }
if config.Channel.Enabled { if config.Channel.Enabled {
for _, c := range channels { for _, w := range worlds {
c.Shutdown() for _, l := range w {
l.Shutdown()
}
} }
} }

View File

@@ -419,3 +419,7 @@ func (s *Server) Season() uint8 {
sid := int64(((s.ID & 0xFF00) - 4096) / 256) sid := int64(((s.ID & 0xFF00) - 4096) / 256)
return uint8(((TimeAdjusted().Unix() / 86400) + sid) % 3) return uint8(((TimeAdjusted().Unix() / 86400) + sid) % 3)
} }
func (s *Server) Players() uint16 {
return uint16(len(s.sessions))
}

View File

@@ -2,6 +2,7 @@ package entranceserver
import ( import (
"encoding/hex" "encoding/hex"
"erupe-ce/server/channelserver"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -19,6 +20,7 @@ type Server struct {
sync.Mutex sync.Mutex
logger *zap.Logger logger *zap.Logger
erupeConfig *_config.Config erupeConfig *_config.Config
worlds [][]*channelserver.Server
db *sqlx.DB db *sqlx.DB
listener net.Listener listener net.Listener
isShuttingDown bool isShuttingDown bool
@@ -68,6 +70,10 @@ func (s *Server) Shutdown() {
s.listener.Close() s.listener.Close()
} }
func (s *Server) SetWorlds(c [][]*channelserver.Server) {
s.worlds = c
}
// acceptClients handles accepting new clients in a loop. // acceptClients handles accepting new clients in a loop.
func (s *Server) acceptClients() { func (s *Server) acceptClients() {
for { for {

View File

@@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
"erupe-ce/common/token"
_config "erupe-ce/config" _config "erupe-ce/config"
"fmt" "fmt"
"net" "net"
@@ -13,10 +14,8 @@ import (
) )
func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
serverInfos := config.Entrance.Entries
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
for i, si := range config.Channel.Worlds {
for serverIdx, si := range serverInfos {
// Prevent MezFes Worlds displaying on Z1 // Prevent MezFes Worlds displaying on Z1
if config.RealClientMode <= _config.Z1 { if config.RealClientMode <= _config.Z1 {
if si.Type == 6 { 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 == "" { if si.IP == "" {
si.IP = config.Host si.IP = config.Host
} }
@@ -38,11 +37,11 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
} else { } else {
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) 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(0x0000)
bf.WriteUint16(uint16(len(si.Channels))) bf.WriteUint16(uint16(len(si.Lands)))
bf.WriteUint8(si.Type) 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 { if s.erupeConfig.RealClientMode >= _config.G1 {
bf.WriteUint8(si.Recommended) bf.WriteUint8(si.Recommended)
} }
@@ -67,15 +66,15 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
bf.WriteUint32(si.AllowedClientFlags) bf.WriteUint32(si.AllowedClientFlags)
} }
for channelIdx, ci := range si.Channels { for j, land := range si.Lands {
sid = (4096 + serverIdx*256) + (16 + channelIdx) sid = (4096 + i*256) + (16 + j)
if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 { if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 {
bf.WriteUint16(_config.ErupeConfig.ProxyPort) bf.WriteUint16(_config.ErupeConfig.ProxyPort)
} else { } else {
bf.WriteUint16(ci.Port) bf.WriteUint16(land.Port)
} }
bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(16 + uint16(j))
bf.WriteUint16(ci.MaxPlayers) bf.WriteUint16(land.MaxPlayers)
var currentPlayers uint16 var currentPlayers uint16
s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(&currentPlayers) s.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(&currentPlayers)
bf.WriteUint16(currentPlayers) 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 { 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 // Decrease by the number of MezFes Worlds
var mf int var mf int
if config.RealClientMode <= _config.Z1 { if config.RealClientMode <= _config.Z1 {
for _, si := range serverInfos { for _, si := range worlds {
if si.Type == 6 { if si.Type == 6 {
mf++ mf++
} }
@@ -128,7 +127,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte {
// and Return Worlds // and Return Worlds
var ret int var ret int
if config.RealClientMode <= _config.G6 { if config.RealClientMode <= _config.G6 {
for _, si := range serverInfos { for _, si := range worlds {
if si.Type == 5 { if si.Type == 5 {
ret++ ret++
} }
@@ -146,7 +145,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte {
} }
bf := byteframe.NewByteFrame() 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() 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())) 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])
} }