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
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
}

View File

@@ -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
}
}

View File

@@ -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

66
main.go
View File

@@ -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()
}
}
}

View File

@@ -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))
}

View File

@@ -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 {

View File

@@ -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(&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 {
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])
}