diff --git a/config.json b/config.json index 6c3b4ecd6..ba952620b 100644 --- a/config.json +++ b/config.json @@ -29,6 +29,25 @@ "bottoken": "", "realtimeChannelID": "" }, + "Commands": [ + { + "name": "Rights", + "enabled": true, + "prefix": "!rights" + }, { + "name": "Raviente", + "enabled": true, + "prefix": "!ravi" + }, { + "name": "Teleport", + "enabled": true, + "prefix": "!tele" + }, { + "name": "Reload", + "enabled": true, + "prefix": "!reload" + } + ], "database": { "host": "localhost", "port": 5432, @@ -37,13 +56,19 @@ "database": "erupe" }, "launcher": { + "enabled": true, "port": 80, "UseOriginalLauncherFiles": false }, "sign": { + "enabled": true, "port": 53312 }, + "channel": { + "enabled": true + }, "entrance": { + "enabled": true, "port": 53310, "entries": [ { diff --git a/config/config.go b/config/config.go index 33bf22abe..c90550f53 100644 --- a/config/config.go +++ b/config/config.go @@ -1,8 +1,11 @@ package config import ( + "fmt" "log" "net" + "os" + "time" "github.com/spf13/viper" ) @@ -16,9 +19,11 @@ type Config struct { DevModeOptions DevModeOptions Discord Discord + Commands []Command Database Database Launcher Launcher Sign Sign + Channel Channel Entrance Entrance } @@ -54,6 +59,13 @@ type Discord struct { RealtimeChannelID string } +// Command is a channelserver chat command +type Command struct { + Name string + Enabled bool + Prefix string +} + // Database holds the postgres database config. type Database struct { Host string @@ -65,17 +77,24 @@ type Database struct { // Launcher holds the launcher server config. type Launcher struct { + Enabled bool Port int UseOriginalLauncherFiles bool } // Sign holds the sign server config. type Sign struct { - Port int + Enabled bool + Port int +} + +type Channel struct { + Enabled bool } // Entrance holds the entrance server config. type Entrance struct { + Enabled bool Port uint16 Entries []EntranceServerInfo } @@ -102,6 +121,17 @@ type EntranceChannelInfo struct { CurrentPlayers uint16 } +var ErupeConfig *Config + +func init() { + var err error + ErupeConfig, err = LoadConfig() + if err != nil { + preventClose(fmt.Sprintf("Failed to load config: %s", err.Error())) + } + +} + // getOutboundIP4 gets the preferred outbound ip4 of this machine // From https://stackoverflow.com/a/37382208 func getOutboundIP4() net.IP { @@ -143,3 +173,20 @@ func LoadConfig() (*Config, error) { return c, nil } + +func preventClose(text string) { + if ErupeConfig.DisableSoftCrash { + os.Exit(0) + } + fmt.Println("\nFailed to start Erupe:\n" + text) + go wait() + fmt.Println("\nPress Enter/Return to exit...") + fmt.Scanln() + os.Exit(0) +} + +func wait() { + for { + time.Sleep(time.Millisecond * 100) + } +} diff --git a/go.mod b/go.mod index 8107625a2..f4e7f1033 100644 --- a/go.mod +++ b/go.mod @@ -19,5 +19,5 @@ require ( golang.org/x/crypto v0.0.0-20211202192323-5770296d904e golang.org/x/sys v0.0.0-20211205182925-97ca703d548d // indirect golang.org/x/text v0.3.7 - golang.org/x/tools v0.1.8 // indirect + golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f // indirect ) diff --git a/go.sum b/go.sum index 9750b4732..1e7747306 100644 --- a/go.sum +++ b/go.sum @@ -78,9 +78,8 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -189,9 +188,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -202,9 +200,8 @@ github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaW github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -222,9 +219,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -295,6 +291,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e h1:MUP6MR3rJ7Gk9LEia0LP2ytiH6MuCfs7qYz+47jGdD8= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -333,7 +330,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -508,8 +505,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg= +golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -617,9 +614,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/main.go b/main.go index b9dc62538..60b64dac1 100644 --- a/main.go +++ b/main.go @@ -14,13 +14,12 @@ import ( "erupe-ce/server/entranceserver" "erupe-ce/server/launcherserver" "erupe-ce/server/signserver" + "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "go.uber.org/zap" ) -var erupeConfig *config.Config - // Temporary DB auto clean on startup for quick development & testing. func cleanDB(db *sqlx.DB) { _ = db.MustExec("DELETE FROM guild_characters") @@ -38,25 +37,19 @@ func main() { logger.Info("Starting Erupe") - // Load the configuration. - erupeConfig, err = config.LoadConfig() - if err != nil { - preventClose(fmt.Sprintf("Failed to load config: %s", err.Error())) - } - - if erupeConfig.Database.Password == "" { + if config.ErupeConfig.Database.Password == "" { preventClose("Database password is blank") } - if net.ParseIP(erupeConfig.Host) == nil { - ips, _ := net.LookupIP(erupeConfig.Host) + if net.ParseIP(config.ErupeConfig.Host) == nil { + ips, _ := net.LookupIP(config.ErupeConfig.Host) for _, ip := range ips { if ip != nil { - erupeConfig.Host = ip.String() + config.ErupeConfig.Host = ip.String() break } } - if net.ParseIP(erupeConfig.Host) == nil { + if net.ParseIP(config.ErupeConfig.Host) == nil { preventClose("Invalid host address") } } @@ -64,10 +57,10 @@ func main() { // Discord bot var discordBot *discordbot.DiscordBot = nil - if erupeConfig.Discord.Enabled { + if config.ErupeConfig.Discord.Enabled { bot, err := discordbot.NewDiscordBot(discordbot.Options{ Logger: logger, - Config: erupeConfig, + Config: config.ErupeConfig, }) if err != nil { @@ -90,11 +83,11 @@ func main() { // Create the postgres DB pool. connectString := fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname= %s sslmode=disable", - erupeConfig.Database.Host, - erupeConfig.Database.Port, - erupeConfig.Database.User, - erupeConfig.Database.Password, - erupeConfig.Database.Database, + config.ErupeConfig.Database.Host, + config.ErupeConfig.Database.Port, + config.ErupeConfig.Database.User, + config.ErupeConfig.Database.Password, + config.ErupeConfig.Database.Database, ) db, err := sqlx.Open("postgres", connectString) @@ -116,7 +109,7 @@ func main() { _ = db.MustExec("UPDATE characters SET cafe_time=0") // Clean the DB if the option is on. - if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB { + if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.CleanDB { logger.Info("Cleaning DB") cleanDB(db) logger.Info("Done cleaning DB") @@ -126,13 +119,13 @@ func main() { // Launcher HTTP server. var launcherServer *launcherserver.Server - if erupeConfig.DevMode && erupeConfig.DevModeOptions.EnableLauncherServer { + if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.EnableLauncherServer { launcherServer = launcherserver.NewServer( &launcherserver.Config{ Logger: logger.Named("launcher"), - ErupeConfig: erupeConfig, + ErupeConfig: config.ErupeConfig, DB: db, - UseOriginalLauncherFiles: erupeConfig.Launcher.UseOriginalLauncherFiles, + UseOriginalLauncherFiles: config.ErupeConfig.Launcher.UseOriginalLauncherFiles, }) err = launcherServer.Start() if err != nil { @@ -142,72 +135,83 @@ func main() { } // Entrance server. - entranceServer := entranceserver.NewServer( - &entranceserver.Config{ - Logger: logger.Named("entrance"), - ErupeConfig: erupeConfig, - DB: db, - }) - err = entranceServer.Start() - if err != nil { - preventClose(fmt.Sprintf("Failed to start entrance server: %s", err.Error())) + + var entranceServer *entranceserver.Server + if config.ErupeConfig.Entrance.Enabled { + entranceServer = entranceserver.NewServer( + &entranceserver.Config{ + Logger: logger.Named("entrance"), + ErupeConfig: config.ErupeConfig, + DB: db, + }) + err = entranceServer.Start() + if err != nil { + preventClose(fmt.Sprintf("Failed to start entrance server: %s", err.Error())) + } + logger.Info("Started entrance server") } - logger.Info("Started entrance server") // Sign server. - signServer := signserver.NewServer( - &signserver.Config{ - Logger: logger.Named("sign"), - ErupeConfig: erupeConfig, - DB: db, - }) - err = signServer.Start() - if err != nil { - preventClose(fmt.Sprintf("Failed to start sign server: %s", err.Error())) + + var signServer *signserver.Server + if config.ErupeConfig.Sign.Enabled { + signServer = signserver.NewServer( + &signserver.Config{ + Logger: logger.Named("sign"), + ErupeConfig: config.ErupeConfig, + DB: db, + }) + err = signServer.Start() + if err != nil { + preventClose(fmt.Sprintf("Failed to start sign server: %s", err.Error())) + } + logger.Info("Started sign server") } - logger.Info("Started sign server") var channels []*channelserver.Server - channelQuery := "" - si := 0 - ci := 0 - count := 1 - for _, ee := range erupeConfig.Entrance.Entries { - for _, ce := range ee.Channels { - sid := (4096 + si*256) + (16 + ci) - c := *channelserver.NewServer(&channelserver.Config{ - ID: uint16(sid), - Logger: logger.Named("channel-" + fmt.Sprint(count)), - ErupeConfig: erupeConfig, - DB: db, - DiscordBot: discordBot, - }) - if ee.IP == "" { - c.IP = erupeConfig.Host - } else { - c.IP = ee.IP - } - c.Port = ce.Port - err = c.Start() - if err != nil { - preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error())) - } else { - channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, si%3) - channels = append(channels, &c) - logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port)) - ci++ - count++ + + if config.ErupeConfig.Channel.Enabled { + channelQuery := "" + si := 0 + ci := 0 + count := 1 + for _, ee := range config.ErupeConfig.Entrance.Entries { + for i, ce := range ee.Channels { + sid := (4096 + si*256) + (16 + ci) + c := *channelserver.NewServer(&channelserver.Config{ + ID: uint16(sid), + Logger: logger.Named("channel-" + fmt.Sprint(count)), + ErupeConfig: config.ErupeConfig, + DB: db, + DiscordBot: discordBot, + }) + if ee.IP == "" { + c.IP = config.ErupeConfig.Host + } else { + c.IP = ee.IP + } + c.Port = ce.Port + err = c.Start() + if err != nil { + preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error())) + } else { + channelQuery += fmt.Sprintf(`INSERT INTO servers (server_id, season, current_players, world_name, world_description, land) VALUES (%d, %d, 0, '%s', '%s', %d);`, sid, si%3, ee.Name, ee.Description, i+1) + channels = append(channels, &c) + logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port)) + ci++ + count++ + } } + ci = 0 + si++ } - ci = 0 - si++ - } - // Register all servers in DB - _ = db.MustExec(channelQuery) + // Register all servers in DB + _ = db.MustExec(channelQuery) - for _, c := range channels { - c.Channels = channels + for _, c := range channels { + c.Channels = channels + } } // Wait for exit or interrupt with ctrl+C. @@ -217,12 +221,21 @@ func main() { logger.Info("Trying to shutdown gracefully") - for _, c := range channels { - c.Shutdown() + if config.ErupeConfig.Channel.Enabled { + for _, c := range channels { + c.Shutdown() + } } - signServer.Shutdown() - entranceServer.Shutdown() - if erupeConfig.DevModeOptions.EnableLauncherServer { + + if config.ErupeConfig.Sign.Enabled { + signServer.Shutdown() + } + + if config.ErupeConfig.Entrance.Enabled { + entranceServer.Shutdown() + } + + if config.ErupeConfig.DevModeOptions.EnableLauncherServer { launcherServer.Shutdown() } @@ -236,7 +249,7 @@ func wait() { } func preventClose(text string) { - if erupeConfig.DisableSoftCrash { + if config.ErupeConfig.DisableSoftCrash { os.Exit(0) } fmt.Println("\nFailed to start Erupe:\n" + text) diff --git a/patch-schema/servers_info.sql b/patch-schema/servers_info.sql new file mode 100644 index 000000000..06389b6ed --- /dev/null +++ b/patch-schema/servers_info.sql @@ -0,0 +1,23 @@ +--adds world_name and land columns +BEGIN; + +CREATE TABLE IF NOT EXISTS public.servers +( + server_id integer NOT NULL, + season integer NOT NULL, + current_players integer NOT NULL, + world_name text COLLATE pg_catalog."default", + world_description text, + land integer +); + +ALTER TABLE public.servers + ADD COLUMN IF NOT EXISTS land integer; + +ALTER TABLE public.servers + ADD COLUMN IF NOT EXISTS world_name text COLLATE pg_catalog."default"; + +ALTER TABLE public.servers + ADD COLUMN IF NOT EXISTS world_description text; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 35538e0fb..6910a01ae 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -8,8 +8,11 @@ import ( "time" "erupe-ce/common/byteframe" + "erupe-ce/config" "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" + + "go.uber.org/zap" ) // MSG_SYS_CAST[ED]_BINARY types enum @@ -29,6 +32,28 @@ const ( BroadcastTypeWorld = 0x0a ) +var commands map[string]config.Command + +func init() { + commands = make(map[string]config.Command) + zapLogger, _ := zap.NewDevelopment() + defer zapLogger.Sync() + logger := zapLogger.Named("commands") + cmds := config.ErupeConfig.Commands + for _, cmd := range cmds { + commands[cmd.Name] = cmd + if cmd.Enabled { + logger.Info(fmt.Sprintf("%s command is enabled, prefix: %s", cmd.Name, cmd.Prefix)) + } else { + logger.Info(fmt.Sprintf("%s command is disabled", cmd.Name)) + } + } +} + +func sendDisabledCommandMessage(s *Session, cmd config.Command) { + sendServerChatMessage(s, fmt.Sprintf("%s command is disabled", cmd.Name)) +} + func sendServerChatMessage(s *Session, message string) { // Make the inside of the casted binary bf := byteframe.NewByteFrame() @@ -165,167 +190,181 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got chat message: %+v\n", chatMessage) - // Flush all objects and users and reload - if strings.HasPrefix(chatMessage.Message, "!reload") { - sendServerChatMessage(s, "Reloading players...") - var temp mhfpacket.MHFPacket - deleteNotif := byteframe.NewByteFrame() - for _, object := range s.stage.objects { - if object.ownerCharID == s.charID { - continue - } - temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id} - deleteNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(deleteNotif, s.clientContext) - } - for _, session := range s.server.sessions { - if s == session { - continue - } - temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID} - deleteNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(deleteNotif, s.clientContext) - } - deleteNotif.WriteUint16(0x0010) - s.QueueSend(deleteNotif.Data()) - time.Sleep(500 * time.Millisecond) - reloadNotif := byteframe.NewByteFrame() - for _, session := range s.server.sessions { - if s == session { - continue - } - temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID} - reloadNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(reloadNotif, s.clientContext) - for i := 0; i < 3; i++ { - temp = &mhfpacket.MsgSysNotifyUserBinary{ - CharID: session.charID, - BinaryType: uint8(i + 1), - } - reloadNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(reloadNotif, s.clientContext) - } - } - for _, obj := range s.stage.objects { - if obj.ownerCharID == s.charID { - continue - } - temp = &mhfpacket.MsgSysDuplicateObject{ - ObjID: obj.id, - X: obj.x, - Y: obj.y, - Z: obj.z, - Unk0: 0, - OwnerCharID: obj.ownerCharID, - } - reloadNotif.WriteUint16(uint16(temp.Opcode())) - temp.Build(reloadNotif, s.clientContext) - } - reloadNotif.WriteUint16(0x0010) - s.QueueSend(reloadNotif.Data()) - } - - // Set account rights - if strings.HasPrefix(chatMessage.Message, "!rights") { - var v uint32 - n, err := fmt.Sscanf(chatMessage.Message, "!rights %d", &v) - if err != nil || n != 1 { - sendServerChatMessage(s, "Error in command. Format: !rights n") - } else { - _, err = s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID) - if err == nil { - sendServerChatMessage(s, fmt.Sprintf("Set rights integer: %d", v)) - } - } - } - // Discord integration if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld { s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message) } - // RAVI COMMANDS V2 - if strings.HasPrefix(chatMessage.Message, "!ravi") { - if getRaviSemaphore(s) != "" { - s.server.raviente.Lock() - if !strings.HasPrefix(chatMessage.Message, "!ravi ") { - sendServerChatMessage(s, "No Raviente command specified!") - } else { - if strings.HasPrefix(chatMessage.Message, "!ravi start") { - if s.server.raviente.register.startTime == 0 { - s.server.raviente.register.startTime = s.server.raviente.register.postTime - sendServerChatMessage(s, "The Great Slaying will begin in a moment") - s.notifyRavi() - } else { - sendServerChatMessage(s, "The Great Slaying has already begun!") + if strings.HasPrefix(chatMessage.Message, commands["Reload"].Prefix) { + // Flush all objects and users and reload + if commands["Reload"].Enabled { + sendServerChatMessage(s, "Reloading players...") + var temp mhfpacket.MHFPacket + deleteNotif := byteframe.NewByteFrame() + for _, object := range s.stage.objects { + if object.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID} + deleteNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(deleteNotif, s.clientContext) + } + deleteNotif.WriteUint16(0x0010) + s.QueueSend(deleteNotif.Data()) + time.Sleep(500 * time.Millisecond) + reloadNotif := byteframe.NewByteFrame() + for _, session := range s.server.sessions { + if s == session { + continue + } + temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID} + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + for i := 0; i < 3; i++ { + temp = &mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: uint8(i + 1), } - } else if strings.HasPrefix(chatMessage.Message, "!ravi sm") || strings.HasPrefix(chatMessage.Message, "!ravi setmultiplier") { - var num uint16 - n, numerr := fmt.Sscanf(chatMessage.Message, "!ravi sm %d", &num) - if numerr != nil || n != 1 { - sendServerChatMessage(s, "Error in command. Format: !ravi sm n") - } else if s.server.raviente.state.damageMultiplier == 1 { - if num > 32 { - sendServerChatMessage(s, "Raviente multiplier too high, defaulting to 32x") - s.server.raviente.state.damageMultiplier = 32 - } else { - sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier set to %dx", num)) - s.server.raviente.state.damageMultiplier = uint32(num) - } - } else { - sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is already set to %dx!", s.server.raviente.state.damageMultiplier)) - } - } else if strings.HasPrefix(chatMessage.Message, "!ravi cm") || strings.HasPrefix(chatMessage.Message, "!ravi checkmultiplier") { - sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is currently %dx", s.server.raviente.state.damageMultiplier)) - } else if strings.HasPrefix(chatMessage.Message, "!ravi sr") || strings.HasPrefix(chatMessage.Message, "!ravi sendres") { - if s.server.raviente.state.stateData[28] > 0 { - sendServerChatMessage(s, "Sending resurrection support!") - s.server.raviente.state.stateData[28] = 0 - } else { - sendServerChatMessage(s, "Resurrection support has not been requested!") - } - } else if strings.HasPrefix(chatMessage.Message, "!ravi ss") || strings.HasPrefix(chatMessage.Message, "!ravi sendsed") { - sendServerChatMessage(s, "Sending sedation support if requested!") - // Total BerRavi HP - HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] - s.server.raviente.support.supportData[1] = HP - } else if strings.HasPrefix(chatMessage.Message, "!ravi rs") || strings.HasPrefix(chatMessage.Message, "!ravi reqsed") { - sendServerChatMessage(s, "Requesting sedation support!") - // Total BerRavi HP - HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] - s.server.raviente.support.supportData[1] = HP + 12 - } else { - sendServerChatMessage(s, "Raviente command not recognised!") + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) } } - s.server.raviente.Unlock() + for _, obj := range s.stage.objects { + if obj.ownerCharID == s.charID { + continue + } + temp = &mhfpacket.MsgSysDuplicateObject{ + ObjID: obj.id, + X: obj.x, + Y: obj.y, + Z: obj.z, + Unk0: 0, + OwnerCharID: obj.ownerCharID, + } + reloadNotif.WriteUint16(uint16(temp.Opcode())) + temp.Build(reloadNotif, s.clientContext) + } + reloadNotif.WriteUint16(0x0010) + s.QueueSend(reloadNotif.Data()) } else { - sendServerChatMessage(s, "No one has joined the Great Slaying!") + sendDisabledCommandMessage(s, commands["Reload"]) } } - // END RAVI COMMANDS V2 - if strings.HasPrefix(chatMessage.Message, "!tele ") { - var x, y int16 - n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y) - if err != nil || n != 2 { - sendServerChatMessage(s, "Invalid command. Usage:\"!tele 500 500\"") + if strings.HasPrefix(chatMessage.Message, commands["Rights"].Prefix) { + // Set account rights + if commands["Rights"].Enabled { + var v uint32 + n, err := fmt.Sscanf(chatMessage.Message, "!rights %d", &v) + if err != nil || n != 1 { + sendServerChatMessage(s, "Error in command. Format: !rights n") + } else { + _, err = s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID) + if err == nil { + sendServerChatMessage(s, fmt.Sprintf("Set rights integer: %d", v)) + } + } } else { - sendServerChatMessage(s, fmt.Sprintf("Teleporting to %d %d", x, y)) + sendDisabledCommandMessage(s, commands["Rights"]) + } + } - // Make the inside of the casted binary - payload := byteframe.NewByteFrame() - payload.SetLE() - payload.WriteUint8(2) // SetState type(position == 2) - payload.WriteInt16(x) // X - payload.WriteInt16(y) // Y - payloadBytes := payload.Data() + if strings.HasPrefix(chatMessage.Message, commands["Raviente"].Prefix) { + if commands["Raviente"].Enabled { + if getRaviSemaphore(s) != "" { + s.server.raviente.Lock() + if !strings.HasPrefix(chatMessage.Message, "!ravi ") { + sendServerChatMessage(s, "No Raviente command specified!") + } else { + if strings.HasPrefix(chatMessage.Message, "!ravi start") { + if s.server.raviente.register.startTime == 0 { + s.server.raviente.register.startTime = s.server.raviente.register.postTime + sendServerChatMessage(s, "The Great Slaying will begin in a moment") + s.notifyRavi() + } else { + sendServerChatMessage(s, "The Great Slaying has already begun!") + } + } else if strings.HasPrefix(chatMessage.Message, "!ravi sm") || strings.HasPrefix(chatMessage.Message, "!ravi setmultiplier") { + var num uint16 + n, numerr := fmt.Sscanf(chatMessage.Message, "!ravi sm %d", &num) + if numerr != nil || n != 1 { + sendServerChatMessage(s, "Error in command. Format: !ravi sm n") + } else if s.server.raviente.state.damageMultiplier == 1 { + if num > 32 { + sendServerChatMessage(s, "Raviente multiplier too high, defaulting to 32x") + s.server.raviente.state.damageMultiplier = 32 + } else { + sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier set to %dx", num)) + s.server.raviente.state.damageMultiplier = uint32(num) + } + } else { + sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is already set to %dx!", s.server.raviente.state.damageMultiplier)) + } + } else if strings.HasPrefix(chatMessage.Message, "!ravi cm") || strings.HasPrefix(chatMessage.Message, "!ravi checkmultiplier") { + sendServerChatMessage(s, fmt.Sprintf("Raviente multiplier is currently %dx", s.server.raviente.state.damageMultiplier)) + } else if strings.HasPrefix(chatMessage.Message, "!ravi sr") || strings.HasPrefix(chatMessage.Message, "!ravi sendres") { + if s.server.raviente.state.stateData[28] > 0 { + sendServerChatMessage(s, "Sending resurrection support!") + s.server.raviente.state.stateData[28] = 0 + } else { + sendServerChatMessage(s, "Resurrection support has not been requested!") + } + } else if strings.HasPrefix(chatMessage.Message, "!ravi ss") || strings.HasPrefix(chatMessage.Message, "!ravi sendsed") { + sendServerChatMessage(s, "Sending sedation support if requested!") + // Total BerRavi HP + HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] + s.server.raviente.support.supportData[1] = HP + } else if strings.HasPrefix(chatMessage.Message, "!ravi rs") || strings.HasPrefix(chatMessage.Message, "!ravi reqsed") { + sendServerChatMessage(s, "Requesting sedation support!") + // Total BerRavi HP + HP := s.server.raviente.state.stateData[0] + s.server.raviente.state.stateData[1] + s.server.raviente.state.stateData[2] + s.server.raviente.state.stateData[3] + s.server.raviente.state.stateData[4] + s.server.raviente.support.supportData[1] = HP + 12 + } else { + sendServerChatMessage(s, "Raviente command not recognised!") + } + } + s.server.raviente.Unlock() + } else { + sendServerChatMessage(s, "No one has joined the Great Slaying!") + } + } else { + sendDisabledCommandMessage(s, commands["Raviente"]) + } + } - s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - MessageType: BinaryMessageTypeState, - RawDataPayload: payloadBytes, - }) + if strings.HasPrefix(chatMessage.Message, commands["Teleport"].Prefix) { + if commands["Teleport"].Enabled { + var x, y int16 + n, err := fmt.Sscanf(chatMessage.Message, "!tele %d %d", &x, &y) + if err != nil || n != 2 { + sendServerChatMessage(s, "Invalid command. Usage:\"!tele 500 500\"") + } else { + sendServerChatMessage(s, fmt.Sprintf("Teleporting to %d %d", x, y)) + + // Make the inside of the casted binary + payload := byteframe.NewByteFrame() + payload.SetLE() + payload.WriteUint8(2) // SetState type(position == 2) + payload.WriteInt16(x) // X + payload.WriteInt16(y) // Y + payloadBytes := payload.Data() + + s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + MessageType: BinaryMessageTypeState, + RawDataPayload: payloadBytes, + }) + } + } else { + sendDisabledCommandMessage(s, commands["Teleport"]) } } }