Merge branch 'main' into feature/diva

This commit is contained in:
wish
2023-03-10 01:14:22 +11:00
36 changed files with 505 additions and 403 deletions

View File

@@ -1,3 +1,5 @@
BEGIN;
INSERT INTO fpoint_items (item_type, item_id, quantity, fpoints, trade_type) VALUES
(7,8895,1,500,0),
(7,8891,1,300,0),
@@ -384,4 +386,6 @@ INSERT INTO fpoint_items (item_type, item_id, quantity, fpoints, trade_type) VAL
(7,16448,1,3,0),
(7,16449,1,3,0),
(7,16348,1,3,0),
(7,16349,1,3,0)
(7,16349,1,3,0);
END;

View File

@@ -3,9 +3,10 @@
"BinPath": "bin",
"Language": "en",
"DisableSoftCrash": false,
"FeaturedWeapons": 1,
"HideLoginNotice": true,
"LoginNotice": "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9.1!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you.",
"LoginNotices": [
"<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9.2!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you."
],
"PatchServerManifest": "",
"PatchServerFile": "",
"ScreenshotAPIURL": "",
@@ -31,6 +32,17 @@
"OutputDir": "savedata"
}
},
"GameplayOptions": {
"FeaturedWeapons": 1,
"MaximumNP": 100000,
"MaximumRP": 50000,
"DisableLoginBoost": false,
"DisableBoostTime": false,
"BoostTimeDuration": 120,
"GuildMealDuration": 60,
"BonusQuestAllowance": 3,
"DailyQuestAllowance": 1
},
"Discord": {
"Enabled": false,
"BotToken": "",

View File

@@ -15,25 +15,25 @@ type Config struct {
Host string `mapstructure:"Host"`
BinPath string `mapstructure:"BinPath"`
Language string
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
FeaturedWeapons int // Number of Active Feature weapons to generate daily
HideLoginNotice bool // Hide the Erupe notice on login
LoginNotice string // MHFML string of the login notice displayed
PatchServerManifest string // Manifest patch server override
PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
HideLoginNotice bool // Hide the Erupe notice on login
LoginNotices []string // MHFML string of the login notices displayed
PatchServerManifest string // Manifest patch server override
PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
DevMode bool
DevModeOptions DevModeOptions
Discord Discord
Commands []Command
Courses []Course
Database Database
Sign Sign
SignV2 SignV2
Channel Channel
Entrance Entrance
DevModeOptions DevModeOptions
GameplayOptions GameplayOptions
Discord Discord
Commands []Command
Courses []Course
Database Database
Sign Sign
SignV2 SignV2
Channel Channel
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
@@ -60,6 +60,19 @@ type SaveDumpOptions struct {
OutputDir string
}
// GameplayOptions has various gameplay modifiers
type GameplayOptions struct {
FeaturedWeapons int // Number of Active Feature weapons to generate daily
MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player
DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time
BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for
GuildMealDuration int // The number of minutes a Guild Meal can be activated for after cooking
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
}
// Discord holds the discord integration config.
type Discord struct {
Enabled bool

6
go.mod
View File

@@ -8,18 +8,16 @@ require (
github.com/gorilla/mux v1.8.0
github.com/jmoiron/sqlx v1.3.4
github.com/lib/pq v1.10.4
github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96
github.com/spf13/viper v1.8.1
go.uber.org/zap v1.18.1
golang.org/x/crypto v0.1.0
golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f
golang.org/x/text v0.4.0
golang.org/x/text v0.7.0
)
require (
github.com/felixge/httpsnoop v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
@@ -32,7 +30,7 @@ require (
github.com/subosito/gotenv v1.2.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/sys v0.1.0 // indirect
golang.org/x/sys v0.5.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

13
go.sum
View File

@@ -94,8 +94,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -229,8 +227,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96 h1:BanNeULiV7hOXjHPUQt3tgF6qVHGZ0uLMnCr0WZ5CTk=
github.com/sachaos/lottery v0.0.0-20180520074626-61949d99bd96/go.mod h1:NuxNqEW5jNyYkZ5WSBB70WQXtRKY1jUPMzX74wr5JFo=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
@@ -430,8 +426,8 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -440,8 +436,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -496,7 +492,6 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

81
main.go
View File

@@ -5,6 +5,7 @@ import (
"net"
"os"
"os/signal"
"runtime/debug"
"syscall"
"time"
@@ -29,13 +30,31 @@ func cleanDB(db *sqlx.DB) {
_ = db.MustExec("DELETE FROM users")
}
var Commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
return setting.Value[:7]
}
}
}
return "unknown"
}
func main() {
var err error
zapLogger, _ := zap.NewDevelopment()
var zapLogger *zap.Logger
if config.ErupeConfig.DevMode {
zapLogger, _ = zap.NewDevelopment()
} else {
zapLogger, _ = zap.NewProduction()
}
defer zapLogger.Sync()
logger := zapLogger.Named("main")
logger.Info("Starting Erupe (9.2b)")
logger.Info(fmt.Sprintf("Starting Erupe (9.2b-%s)", Commit()))
if config.ErupeConfig.Database.Password == "" {
preventClose("Database password is blank")
@@ -64,20 +83,20 @@ func main() {
})
if err != nil {
preventClose(fmt.Sprintf("Failed to create Discord bot: %s", err.Error()))
preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error()))
}
// Discord bot
err = bot.Start()
if err != nil {
preventClose(fmt.Sprintf("Failed to start Discord bot: %s", err.Error()))
preventClose(fmt.Sprintf("Discord: Failed to start, %s", err.Error()))
}
discordBot = bot
logger.Info("Discord bot is enabled")
logger.Info("Discord: Started successfully")
} else {
logger.Info("Discord bot is disabled")
logger.Info("Discord: Disabled")
}
// Create the postgres DB pool.
@@ -92,15 +111,15 @@ func main() {
db, err := sqlx.Open("postgres", connectString)
if err != nil {
preventClose(fmt.Sprintf("Failed to open SQL database: %s", err.Error()))
preventClose(fmt.Sprintf("Database: Failed to open, %s", err.Error()))
}
// Test the DB connection.
err = db.Ping()
if err != nil {
preventClose(fmt.Sprintf("Failed to ping database: %s", err.Error()))
preventClose(fmt.Sprintf("Database: Failed to ping, %s", err.Error()))
}
logger.Info("Connected to database")
logger.Info("Database: Started successfully")
// Clear stale data
_ = db.MustExec("DELETE FROM sign_sessions")
@@ -108,11 +127,13 @@ func main() {
// Clean the DB if the option is on.
if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.CleanDB {
logger.Info("Cleaning DB")
logger.Info("Database: Started clearing...")
cleanDB(db)
logger.Info("Done cleaning DB")
logger.Info("Database: Finished clearing")
}
logger.Info(fmt.Sprintf("Server Time: %s", channelserver.TimeAdjusted().String()))
// Now start our server(s).
// Entrance server.
@@ -127,9 +148,11 @@ func main() {
})
err = entranceServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Failed to start entrance server: %s", err.Error()))
preventClose(fmt.Sprintf("Entrance: Failed to start, %s", err.Error()))
}
logger.Info("Started entrance server")
logger.Info("Entrance: Started successfully")
} else {
logger.Info("Entrance: Disabled")
}
// Sign server.
@@ -144,9 +167,11 @@ func main() {
})
err = signServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Failed to start sign server: %s", err.Error()))
preventClose(fmt.Sprintf("Sign: Failed to start, %s", err.Error()))
}
logger.Info("Started sign server")
logger.Info("Sign: Started successfully")
} else {
logger.Info("Sign: Disabled")
}
// New Sign server
@@ -160,9 +185,11 @@ func main() {
})
err = newSignServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Failed to start sign-v2 server: %s", err.Error()))
preventClose(fmt.Sprintf("SignV2: Failed to start, %s", err.Error()))
}
logger.Info("Started new sign server")
logger.Info("SignV2: Started successfully")
} else {
logger.Info("SignV2: Disabled")
}
var channels []*channelserver.Server
@@ -172,7 +199,7 @@ func main() {
si := 0
ci := 0
count := 1
for _, ee := range config.ErupeConfig.Entrance.Entries {
for j, ee := range config.ErupeConfig.Entrance.Entries {
for i, ce := range ee.Channels {
sid := (4096 + si*256) + (16 + ci)
c := *channelserver.NewServer(&channelserver.Config{
@@ -188,13 +215,14 @@ func main() {
c.IP = ee.IP
}
c.Port = ce.Port
c.GlobalID = fmt.Sprintf("%02d%02d", j+1, i+1)
err = c.Start()
if err != nil {
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
preventClose(fmt.Sprintf("Channel: Failed to start, %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))
logger.Info(fmt.Sprintf("Channel %d (%d): Started successfully", count, ce.Port))
ci++
count++
}
@@ -211,12 +239,23 @@ func main() {
}
}
logger.Info("Finished starting Erupe")
// Wait for exit or interrupt with ctrl+C.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c
logger.Info("Trying to shutdown gracefully")
if !config.ErupeConfig.DisableSoftCrash {
for i := 0; i < 10; i++ {
message := fmt.Sprintf("Shutting down in %d...", 10-i)
for _, c := range channels {
c.BroadcastChatMessage(message)
}
logger.Info(message)
time.Sleep(time.Second)
}
}
if config.ErupeConfig.Channel.Enabled {
for _, c := range channels {

View File

@@ -1,18 +1,18 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgSysLockGlobalSema represents the MSG_SYS_LOCK_GLOBAL_SEMA
type MsgSysLockGlobalSema struct {
AckHandle uint32
UserIDLength uint16
ServerChannelIDLength uint16
UserIDLength uint16
ServerChannelIDLength uint16
UserIDString string
ServerChannelIDString string
}

View File

@@ -0,0 +1,12 @@
BEGIN;
DROP TABLE IF EXISTS public.login_boost_state;
CREATE TABLE IF NOT EXISTS public.login_boost (
char_id INTEGER,
week_req INTEGER,
expiration TIMESTAMP WITH TIME ZONE,
reset TIMESTAMP WITH TIME ZONE
);
END;

View File

@@ -0,0 +1,6 @@
BEGIN;
ALTER TABLE public.characters
ADD COLUMN mezfes BYTEA;
END;

69
patch-schema/time-fix.sql Normal file
View File

@@ -0,0 +1,69 @@
BEGIN;
ALTER TABLE IF EXISTS public.characters
ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.characters
ALTER COLUMN guild_post_checked TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.characters
ALTER COLUMN boost_time TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.characters
ALTER COLUMN cafe_reset TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.distribution
ALTER COLUMN deadline TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.events
ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.feature_weapon
ALTER COLUMN start_time TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guild_alliances
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guild_applications
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guild_characters
ALTER COLUMN joined_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guild_posts
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.characters
ALTER COLUMN daily_time TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guilds
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.mail
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.stamps
ALTER COLUMN hl_next TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.stamps
ALTER COLUMN ex_next TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.titles
ALTER COLUMN unlocked_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.titles
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.users
ALTER COLUMN last_login TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.users
ALTER COLUMN return_expires TYPE TIMESTAMP WITH TIME ZONE;
ALTER TABLE IF EXISTS public.guild_meals
DROP COLUMN expires;
ALTER TABLE IF EXISTS public.guild_meals
ADD COLUMN created_at TIMESTAMP WITH TIME ZONE;
END;

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"encoding/binary"
"encoding/hex"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
"fmt"
"io"
@@ -144,7 +145,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
updateRights(s)
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // Unix timestamp
_, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
if err != nil {
@@ -156,7 +157,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
panic(err)
}
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", Time_Current().Unix(), s.charID)
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", TimeAdjusted().Unix(), s.charID)
if err != nil {
panic(err)
}
@@ -216,7 +217,7 @@ func logoutPlayer(s *Session) {
var timePlayed int
var sessionTime int
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)
sessionTime = int(TimeAdjusted().Unix()) - int(s.sessionStart)
timePlayed += sessionTime
var rpGained int
@@ -258,8 +259,8 @@ func logoutPlayer(s *Session) {
return
}
saveData.RP += uint16(rpGained)
if saveData.RP >= 50000 {
saveData.RP = 50000
if saveData.RP >= s.server.erupeConfig.GameplayOptions.MaximumRP {
saveData.RP = s.server.erupeConfig.GameplayOptions.MaximumRP
}
saveData.Save(s)
}
@@ -276,7 +277,7 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
resp := &mhfpacket.MsgSysTime{
GetRemoteTime: false,
Timestamp: uint32(Time_Current_Adjusted().Unix()), // JP timezone
Timestamp: uint32(TimeAdjusted().Unix()), // JP timezone
}
s.QueueSendMHF(resp)
}
@@ -314,25 +315,30 @@ func handleMsgSysEcho(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysLockGlobalSema(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLockGlobalSema)
var sgid string
for _, channel := range s.server.Channels {
for id := range channel.stages {
if strings.HasSuffix(id, pkt.UserIDString) {
sgid = channel.GlobalID
}
}
}
bf := byteframe.NewByteFrame()
// Unk
// 0x00 when no ID sent
// 0x02 when ID sent
if pkt.ServerChannelIDLength == 1 {
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00})
if len(sgid) > 0 && sgid != s.server.GlobalID {
bf.WriteUint8(0)
bf.WriteUint8(0)
ps.Uint16(bf, sgid, false)
} else {
bf.WriteUint8(0x02)
bf.WriteUint8(0x00) // Unk
bf.WriteUint16(uint16(pkt.ServerChannelIDLength))
bf.WriteBytes([]byte(pkt.ServerChannelIDString))
bf.WriteUint8(2)
bf.WriteUint8(0)
ps.Uint16(bf, pkt.ServerChannelIDString, false)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgSysUnlockGlobalSema(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysUnlockGlobalSema)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 8))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgSysUpdateRight(s *Session, p mhfpacket.MHFPacket) {}
@@ -1505,7 +1511,7 @@ func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) {
var dailyTime time.Time
_ = s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
if Time_Current_Adjusted().After(dailyTime) {
if TimeAdjusted().After(dailyTime) {
s.server.db.Exec("UPDATE characters SET bonus_quests = 0, daily_quests = 0 WHERE id=$1", s.charID)
}

View File

@@ -37,11 +37,8 @@ func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
// get next midday
var t = Time_static()
year, month, day := t.Date()
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
if t.After(midday) {
midday := TimeMidnight().Add(12 * time.Hour)
if TimeAdjusted().After(midday) {
midday = midday.Add(24 * time.Hour)
}
@@ -54,11 +51,11 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
var bondBonus, bonusQuests, dailyQuests uint32
bf := byteframe.NewByteFrame()
if t.After(dailyTime) {
if midday.After(dailyTime) {
addPointNetcafe(s, 5)
bondBonus = 5 // Bond point bonus quests
bonusQuests = 3 // HRP bonus quests?
dailyQuests = 1 // Daily quests
bondBonus = 5 // Bond point bonus quests
bonusQuests = s.server.erupeConfig.GameplayOptions.BonusQuestAllowance
dailyQuests = s.server.erupeConfig.GameplayOptions.DailyQuestAllowance
s.server.db.Exec("UPDATE characters SET daily_time=$1, bonus_quests = $2, daily_quests = $3 WHERE id=$4", midday, bonusQuests, dailyQuests, s.charID)
bf.WriteBool(true) // Success?
} else {
@@ -80,7 +77,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
cafeReset = TimeWeekNext()
s.server.db.Exec(`UPDATE characters SET cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID)
}
if Time_Current_Adjusted().After(cafeReset) {
if TimeAdjusted().After(cafeReset) {
cafeReset = TimeWeekNext()
s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID)
s.server.db.Exec(`DELETE FROM cafe_accepted WHERE character_id=$1`, s.charID)
@@ -92,7 +89,7 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
panic(err)
}
if s.FindCourse("NetCafe").ID != 0 || s.FindCourse("N").ID != 0 {
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
}
bf.WriteUint32(cafeTime) // Total cafe time
bf.WriteUint16(0)
@@ -142,7 +139,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(0)
resp.WriteUint32(uint32(time.Now().Unix()))
resp.WriteUint32(uint32(TimeAdjusted().Unix()))
resp.WriteUint32(count)
resp.WriteBytes(bf.Data())
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
@@ -165,7 +162,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
SELECT ch.cafe_time + $2
FROM characters ch
WHERE ch.id = $1
) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart)
) >= time_req`, s.charID, TimeAdjusted().Unix()-s.sessionStart)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} else {
@@ -210,8 +207,8 @@ func addPointNetcafe(s *Session, p int) error {
if err != nil {
return err
}
if points+p > 100000 {
points = 100000
if points+p > s.server.erupeConfig.GameplayOptions.MaximumNP {
points = s.server.erupeConfig.GameplayOptions.MaximumNP
} else {
points += p
}
@@ -222,7 +219,12 @@ func addPointNetcafe(s *Session, p int) error {
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
bf := byteframe.NewByteFrame()
boostLimit := Time_Current_Adjusted().Add(100 * time.Minute)
boostLimit := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Minute)
if s.server.erupeConfig.GameplayOptions.DisableBoostTime {
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID)
bf.WriteUint32(uint32(boostLimit.Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
@@ -255,7 +257,7 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
if boostLimit.Unix() > Time_Current_Adjusted().Unix() {
if boostLimit.After(TimeAdjusted()) {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
} else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})

View File

@@ -38,16 +38,18 @@ var commands map[string]config.Command
func init() {
commands = make(map[string]config.Command)
zapLogger, _ := zap.NewDevelopment()
zapConfig := zap.NewDevelopmentConfig()
zapConfig.DisableCaller = true
zapLogger, _ := zapConfig.Build()
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))
logger.Info(fmt.Sprintf("Command %s: Enabled, prefix: %s", cmd.Name, cmd.Prefix))
} else {
logger.Info(fmt.Sprintf("%s command is disabled", cmd.Name))
logger.Info(fmt.Sprintf("Command %s: Disabled", cmd.Name))
}
}
}

View File

@@ -22,7 +22,7 @@ func cleanupDiva(s *Session) {
func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
timestamps := make([]uint32, 6)
midnight := Time_Current_Midnight()
midnight := TimeMidnight()
if debug && start <= 3 {
midnight := uint32(midnight.Unix())
switch start {
@@ -50,7 +50,7 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
}
return timestamps
}
if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 {
if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 {
cleanupDiva(s)
// Generate a new diva defense, starting midnight tomorrow
start = uint32(midnight.Add(24 * time.Hour).Unix())

View File

@@ -61,29 +61,26 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
var features []activeFeature
rows, _ := s.server.db.Queryx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1 OR start_time=$2`, Time_Current_Midnight().Add(-24*time.Hour), Time_Current_Midnight())
for rows.Next() {
var feature activeFeature
rows.StructScan(&feature)
features = append(features, feature)
times := []time.Time{
TimeMidnight().Add(-24 * time.Hour),
TimeMidnight(),
TimeMidnight().Add(24 * time.Hour),
}
if len(features) < 2 {
if len(features) == 0 {
feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
feature.StartTime = Time_Current_Midnight().Add(-24 * time.Hour)
features = append(features, feature)
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
for _, t := range times {
var temp activeFeature
err := s.server.db.QueryRowx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1`, t).StructScan(&temp)
if err != nil || temp.StartTime.IsZero() {
temp = generateFeatureWeapons(s.server.erupeConfig.GameplayOptions.FeaturedWeapons)
temp.StartTime = t
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
}
feature := generateFeatureWeapons(s.server.erupeConfig.FeaturedWeapons)
feature.StartTime = Time_Current_Midnight()
features = append(features, feature)
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, feature.StartTime, feature.ActiveFeatures)
features = append(features, temp)
}
bf := byteframe.NewByteFrame()
bf.WriteUint8(2)
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
bf.WriteUint8(uint8(len(features)))
bf.WriteUint32(uint32(TimeAdjusted().Add(-5 * time.Minute).Unix()))
for _, feature := range features {
bf.WriteUint32(uint32(feature.StartTime.Unix()))
bf.WriteUint32(feature.ActiveFeatures)
@@ -119,129 +116,97 @@ func generateFeatureWeapons(count int) activeFeature {
}
type loginBoost struct {
WeekReq, WeekCount uint8
Available bool
Expiration uint32
WeekReq uint8 `db:"week_req"`
WeekCount uint8
Active bool
Expiration time.Time `db:"expiration"`
Reset time.Time `db:"reset"`
}
func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetKeepLoginBoostStatus)
var loginBoostStatus []loginBoost
insert := false
boostState, err := s.server.db.Query("SELECT week_req, week_count, available, end_time FROM login_boost_state WHERE char_id=$1 ORDER BY week_req ASC", s.charID)
if err != nil {
panic(err)
bf := byteframe.NewByteFrame()
var loginBoosts []loginBoost
rows, err := s.server.db.Queryx("SELECT week_req, expiration, reset FROM login_boost WHERE char_id=$1 ORDER BY week_req", s.charID)
if err != nil || s.server.erupeConfig.GameplayOptions.DisableLoginBoost {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 35))
return
}
for boostState.Next() {
var boost loginBoost
err = boostState.Scan(&boost.WeekReq, &boost.WeekCount, &boost.Available, &boost.Expiration)
if err != nil {
panic(err)
for rows.Next() {
var temp loginBoost
rows.StructScan(&temp)
loginBoosts = append(loginBoosts, temp)
}
if len(loginBoosts) == 0 {
temp := TimeWeekStart()
loginBoosts = []loginBoost{
{WeekReq: 1, Expiration: temp},
{WeekReq: 2, Expiration: temp},
{WeekReq: 3, Expiration: temp},
{WeekReq: 4, Expiration: temp},
{WeekReq: 5, Expiration: temp},
}
loginBoostStatus = append(loginBoostStatus, boost)
}
if len(loginBoostStatus) == 0 {
// create default Entries (should only been week 1 with )
insert = true
loginBoostStatus = []loginBoost{
{
WeekReq: 1, // weeks needed
WeekCount: 0, // weeks passed
Available: true, // available
Expiration: 0, //uint32(t.Add(120 * time.Minute).Unix()), // uncomment to enable permanently
},
{
WeekReq: 2,
WeekCount: 0,
Available: true,
Expiration: 0,
},
{
WeekReq: 3,
WeekCount: 0,
Available: true,
Expiration: 0,
},
{
WeekReq: 4,
WeekCount: 0,
Available: true,
Expiration: 0,
},
{
WeekReq: 5,
WeekCount: 0,
Available: true,
Expiration: 0,
},
for _, boost := range loginBoosts {
s.server.db.Exec(`INSERT INTO login_boost VALUES ($1, $2, $3, $4)`, s.charID, boost.WeekReq, boost.Expiration, time.Time{})
}
}
resp := byteframe.NewByteFrame()
CurrentWeek := Time_Current_Week_uint8()
for d := range loginBoostStatus {
if CurrentWeek == 1 && loginBoostStatus[d].WeekCount <= 5 {
loginBoostStatus[d].WeekCount = 0
for _, boost := range loginBoosts {
// Reset if next week
if !boost.Reset.IsZero() && boost.Reset.Before(TimeAdjusted()) {
boost.Expiration = TimeWeekStart()
boost.Reset = time.Time{}
s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, boost.Expiration, boost.Reset, s.charID, boost.WeekReq)
}
if loginBoostStatus[d].WeekReq == CurrentWeek || loginBoostStatus[d].WeekCount != 0 {
loginBoostStatus[d].WeekCount = CurrentWeek
boost.WeekCount = uint8((TimeAdjusted().Unix()-boost.Expiration.Unix())/604800 + 1)
if boost.WeekCount >= boost.WeekReq {
boost.Active = true
boost.WeekCount = boost.WeekReq
}
if !loginBoostStatus[d].Available && loginBoostStatus[d].WeekCount >= loginBoostStatus[d].WeekReq && uint32(time.Now().In(time.FixedZone("UTC+1", 1*60*60)).Unix()) >= loginBoostStatus[d].Expiration {
loginBoostStatus[d].Expiration = 1
// Show reset timer on expired boosts
if boost.Reset.After(TimeAdjusted()) {
boost.Active = true
boost.WeekCount = 0
}
if !insert {
_, err := s.server.db.Exec(`UPDATE login_boost_state SET week_count=$1, end_time=$2 WHERE char_id=$3 AND week_req=$4`, loginBoostStatus[d].WeekCount, loginBoostStatus[d].Expiration, s.charID, loginBoostStatus[d].WeekReq)
if err != nil {
panic(err)
}
bf.WriteUint8(boost.WeekReq)
bf.WriteBool(boost.Active)
bf.WriteUint8(boost.WeekCount)
if !boost.Reset.IsZero() {
bf.WriteUint32(uint32(boost.Expiration.Unix()))
} else {
bf.WriteUint32(0)
}
}
for _, v := range loginBoostStatus {
if insert {
_, err := s.server.db.Exec(`INSERT INTO login_boost_state (char_id, week_req, week_count, available, end_time) VALUES ($1,$2,$3,$4,$5)`, s.charID, v.WeekReq, v.WeekCount, v.Available, v.Expiration)
if err != nil {
panic(err)
}
}
resp.WriteUint8(v.WeekReq)
resp.WriteUint8(v.WeekCount)
resp.WriteBool(v.Available)
resp.WriteUint32(v.Expiration)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {
// Directly interacts with MhfGetKeepLoginBoostStatus
// TODO: make these states persistent on a per character basis
pkt := p.(*mhfpacket.MsgMhfUseKeepLoginBoost)
var t = time.Now().In(time.FixedZone("UTC+1", 1*60*60))
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
// response is end timestamp based on input
var expiration time.Time
bf := byteframe.NewByteFrame()
bf.WriteUint8(0)
switch pkt.BoostWeekUsed {
case 1:
t = t.Add(120 * time.Minute)
resp.WriteUint32(uint32(t.Unix()))
case 2:
t = t.Add(240 * time.Minute)
resp.WriteUint32(uint32(t.Unix()))
fallthrough
case 3:
t = t.Add(120 * time.Minute)
resp.WriteUint32(uint32(t.Unix()))
expiration = TimeAdjusted().Add(120 * time.Minute)
case 4:
t = t.Add(180 * time.Minute)
resp.WriteUint32(uint32(t.Unix()))
expiration = TimeAdjusted().Add(180 * time.Minute)
case 2:
fallthrough
case 5:
t = t.Add(240 * time.Minute)
resp.WriteUint32(uint32(t.Unix()))
expiration = TimeAdjusted().Add(240 * time.Minute)
}
_, err := s.server.db.Exec(`UPDATE login_boost_state SET available='false', end_time=$1 WHERE char_id=$2 AND week_req=$3`, uint32(t.Unix()), s.charID, pkt.BoostWeekUsed)
if err != nil {
panic(err)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
bf.WriteUint32(uint32(expiration.Unix()))
s.server.db.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, expiration, TimeWeekNext(), s.charID, pkt.BoostWeekUsed)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -12,22 +12,25 @@ import (
func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveMezfesData)
s.server.db.Exec(`UPDATE characters SET mezfes=$1 WHERE id=$2`, pkt.RawDataPayload, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadMezfesData)
resp := byteframe.NewByteFrame()
resp.WriteUint32(0) // Unk
resp.WriteUint8(2) // Count of the next 2 uint32s
resp.WriteUint32(0)
resp.WriteUint32(0)
resp.WriteUint32(0) // Unk
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
var data []byte
s.server.db.QueryRow(`SELECT mezfes FROM characters WHERE id=$1`, s.charID).Scan(&data)
bf := byteframe.NewByteFrame()
if len(data) > 0 {
bf.WriteBytes(data)
} else {
bf.WriteUint32(0)
bf.WriteUint8(2)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
@@ -38,7 +41,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
// Unk
// Start?
// End?
midnight := Time_Current_Midnight()
midnight := TimeMidnight()
switch state {
case 1:
bf.WriteUint32(uint32(midnight.Unix()))
@@ -57,13 +60,13 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(uint32(midnight.Add(7 * 24 * time.Hour).Unix()))
default:
bf.WriteBytes(make([]byte, 16))
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time
bf.WriteUint8(3)
bf.WriteBytes(make([]byte, 4))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // TS Current Time
bf.WriteUint32(uint32(TimeAdjusted().Unix())) // TS Current Time
bf.WriteUint8(3)
ps.Uint8(bf, "", false)
bf.WriteUint16(0) // numEvents
@@ -98,7 +101,7 @@ func cleanupFesta(s *Session) {
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
timestamps := make([]uint32, 5)
midnight := Time_Current_Midnight()
midnight := TimeMidnight()
if debug && start <= 3 {
midnight := uint32(midnight.Unix())
switch start {
@@ -123,7 +126,7 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
}
return timestamps
}
if start == 0 || Time_Current_Adjusted().Unix() > int64(start)+2977200 {
if start == 0 || TimeAdjusted().Unix() > int64(start)+2977200 {
cleanupFesta(s)
// Generate a new festa, starting midnight tomorrow
start = uint32(midnight.Add(24 * time.Hour).Unix())
@@ -167,7 +170,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
timestamps = generateFestaTimestamps(s, start, false)
}
if timestamps[0] > uint32(Time_Current_Adjusted().Unix()) {
if timestamps[0] > uint32(TimeAdjusted().Unix()) {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
@@ -180,7 +183,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
for _, timestamp := range timestamps {
bf.WriteUint32(timestamp)
}
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
bf.WriteUint8(4)
ps.Uint8(bf, "", false)
bf.WriteUint32(0)

View File

@@ -1724,57 +1724,59 @@ func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {
}
type GuildMeal struct {
ID uint32 `db:"id"`
MealID uint32 `db:"meal_id"`
Level uint32 `db:"level"`
Expires uint32 `db:"expires"`
ID uint32 `db:"id"`
MealID uint32 `db:"meal_id"`
Level uint32 `db:"level"`
CreatedAt time.Time `db:"created_at"`
}
func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadGuildCooking)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
data, err := s.server.db.Queryx("SELECT id, meal_id, level, expires FROM guild_meals WHERE guild_id = $1", guild.ID)
data, err := s.server.db.Queryx("SELECT id, meal_id, level, created_at FROM guild_meals WHERE guild_id = $1", guild.ID)
if err != nil {
s.logger.Error("Failed to get guild meals from db", zap.Error(err))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
return
}
temp := byteframe.NewByteFrame()
count := 0
var meals []GuildMeal
var temp GuildMeal
for data.Next() {
mealData := &GuildMeal{}
err = data.StructScan(&mealData)
err = data.StructScan(&temp)
if err != nil {
continue
}
if mealData.Expires > uint32(Time_Current_Adjusted().Add(-60*time.Minute).Unix()) {
count++
temp.WriteUint32(mealData.ID)
temp.WriteUint32(mealData.MealID)
temp.WriteUint32(mealData.Level)
temp.WriteUint32(mealData.Expires)
if temp.CreatedAt.Add(60 * time.Minute).After(TimeAdjusted()) {
meals = append(meals, temp)
}
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(count))
bf.WriteBytes(temp.Data())
bf.WriteUint16(uint16(len(meals)))
for _, meal := range meals {
bf.WriteUint32(meal.ID)
bf.WriteUint32(meal.MealID)
bf.WriteUint32(meal.Level)
bf.WriteUint32(uint32(meal.CreatedAt.Unix()))
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
currentTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.GuildMealDuration-60) * time.Minute)
if pkt.OverwriteID != 0 {
_, err := s.server.db.Exec("DELETE FROM guild_meals WHERE id = $1", pkt.OverwriteID)
if err != nil {
s.logger.Error("Failed to delete meal in db", zap.Error(err))
}
s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, currentTime, pkt.OverwriteID)
} else {
s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, currentTime).Scan(&pkt.OverwriteID)
}
_, err := s.server.db.Exec("INSERT INTO guild_meals (guild_id, meal_id, level, expires) VALUES ($1, $2, $3, $4)", guild.ID, pkt.MealID, pkt.Success, Time_Current_Adjusted().Add(30*time.Minute).Unix())
if err != nil {
s.logger.Error("Failed to register meal in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x00})
bf := byteframe.NewByteFrame()
bf.WriteUint16(1)
bf.WriteUint32(pkt.OverwriteID)
bf.WriteUint32(uint32(pkt.MealID))
bf.WriteUint32(uint32(pkt.Success))
bf.WriteUint32(uint32(currentTime.Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetGuildWeeklyBonusMaster(s *Session, p mhfpacket.MHFPacket) {
@@ -1832,7 +1834,7 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
s.server.db.Exec("UPDATE characters SET guild_post_checked = $1 WHERE id = $2", time.Now(), s.charID)
s.server.db.Exec("UPDATE characters SET guild_post_checked = now() WHERE id = $1", s.charID)
bf := byteframe.NewByteFrame()
var postCount uint32
for msgs.Next() {
@@ -1861,13 +1863,13 @@ func handleMsgMhfEnumerateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfUpdateGuildMessageBoard(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuildMessageBoard)
bf := byteframe.NewByteFrameFromBytes(pkt.Request)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
if guild == nil {
if pkt.MessageOp == 5 {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
guild, err := GetGuildInfoByCharacterId(s, s.charID)
applicant := false
if guild != nil {
applicant, _ = guild.HasApplicationForCharID(s, s.charID)
}
if err != nil || guild == nil || applicant {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
switch pkt.MessageOp {

View File

@@ -52,7 +52,7 @@ func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfRegistGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventure)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(6*time.Hour).Unix())
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, TimeAdjusted().Unix(), TimeAdjusted().Add(6*time.Hour).Unix())
if err != nil {
s.logger.Error("Failed to register guild adventure", zap.Error(err))
}
@@ -87,7 +87,7 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfRegistGuildAdventureDiva(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildAdventureDiva)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, Time_Current_Adjusted().Unix(), Time_Current_Adjusted().Add(1*time.Hour).Unix())
_, err := s.server.db.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, TimeAdjusted().Unix(), TimeAdjusted().Add(1*time.Hour).Unix())
if err != nil {
s.logger.Error("Failed to register guild adventure", zap.Error(err))
}

View File

@@ -65,8 +65,8 @@ SELECT
g.id as guild_id,
joined_at,
coalesce(souls, 0) as souls,
rp_today,
rp_yesterday,
COALESCE(rp_today, 0) AS rp_today,
COALESCE(rp_yesterday, 0) AS rp_yesterday,
c.name,
character.character_id,
coalesce(gc.order_index, 0) as order_index,

View File

@@ -27,7 +27,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
}
bf := byteframe.NewByteFrame()
hunts := 0
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, Time_Current_Adjusted().Unix())
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, TimeAdjusted().Unix())
for rows.Next() {
hunt := &TreasureHunt{}
err = rows.StructScan(&hunt)
@@ -101,7 +101,7 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
}
}
_, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6, $7)",
guild.ID, s.charID, destination, level, Time_Current_Adjusted().Unix(), huntData.Data(), catsUsed)
guild.ID, s.charID, destination, level, TimeAdjusted().Unix(), huntData.Data(), catsUsed)
if err != nil {
panic(err)
}

View File

@@ -43,9 +43,9 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
Unk uint32
Timestamp uint32
}{
{0, uint32(Time_Current_Midnight().Add(-12 * time.Hour).Unix())},
{0, uint32(Time_Current_Midnight().Add(12 * time.Hour).Unix())},
{0, uint32(Time_Current_Midnight().Add(36 * time.Hour).Unix())},
{0, uint32(TimeMidnight().Add(-12 * time.Hour).Unix())},
{0, uint32(TimeMidnight().Add(12 * time.Hour).Unix())},
{0, uint32(TimeMidnight().Add(36 * time.Hour).Unix())},
}
bf.WriteUint8(uint8(len(legendDispatch)))
for _, dispatch := range legendDispatch {
@@ -164,8 +164,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(pactID)
bf.WriteUint32(cid)
bf.WriteBool(false) // ?
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix()))
bf.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix()))
bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix()))
bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix()))
bf.WriteBytes(stringsupport.PaddedString(name, 18, true))
} else {
bf.WriteUint8(0)
@@ -180,8 +180,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
rows.Scan(&name, &cid, &pactID)
temp.WriteUint32(pactID)
temp.WriteUint32(cid)
temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -8).Unix()))
temp.WriteUint32(uint32(Time_Current_Adjusted().Add(time.Hour * 24 * -1).Unix()))
temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix()))
temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix()))
temp.WriteBytes(stringsupport.PaddedString(name, 18, true))
}
bf.WriteUint8(loans)
@@ -346,7 +346,7 @@ func getGuildAirouList(s *Session) []CatDefinition {
FROM guild_hunts gh
INNER JOIN characters c
ON gh.host_id = c.id
WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, Time_Current_Adjusted().Unix())
WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, TimeAdjusted().Unix())
if err != nil {
s.logger.Warn("Failed to get recently used airous", zap.Error(err))
}

View File

@@ -36,8 +36,9 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(*ref)
}
} else {
resp.WriteUint32(*ref + data*damageMultiplier)
*ref += data * damageMultiplier
data = uint32(float64(data) * damageMultiplier)
resp.WriteUint32(*ref + data)
*ref += data
}
case 13:
fallthrough

View File

@@ -521,7 +521,7 @@ func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) {
}
bf := byteframe.NewByteFrame()
bf.WriteUint8(step)
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -135,7 +135,6 @@ func destructEmptyStages(s *Session) {
func removeSessionFromStage(s *Session) {
// Remove client from old stage.
s.stage.Lock()
delete(s.stage.clients, s)
// Delete old stage objects owned by the client.
@@ -146,7 +145,6 @@ func removeSessionFromStage(s *Session) {
delete(s.stage.objects, object.ownerCharID)
}
}
s.stage.Unlock()
destructEmptyStages(s)
destructEmptySemaphores(s)
}

View File

@@ -13,7 +13,7 @@ func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) {
switch pkt.Unk0 {
case 0:
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix()))
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
bf.WriteUint32(0) // Tied to schedule ID?
case 1:

View File

@@ -38,6 +38,7 @@ type Server struct {
sync.Mutex
Channels []*Server
ID uint16
GlobalID string
IP string
Port uint16
logger *zap.Logger
@@ -124,19 +125,19 @@ func NewRaviente() *Raviente {
return raviente
}
func (r *Raviente) GetRaviMultiplier(s *Server) uint32 {
func (r *Raviente) GetRaviMultiplier(s *Server) float64 {
raviSema := getRaviSemaphore(s)
if raviSema != nil {
var minPlayers uint32
var minPlayers int
if r.register.maxPlayers > 8 {
minPlayers = 24
} else {
minPlayers = 4
}
if uint32(len(raviSema.clients)) > minPlayers {
if len(raviSema.clients) > minPlayers {
return 1
}
return minPlayers / uint32(len(raviSema.clients))
return float64(minPlayers / len(raviSema.clients))
}
return 0
}
@@ -269,6 +270,8 @@ func (s *Server) manageSessions() {
// BroadcastMHF queues a MHFPacket to be sent to all sessions.
func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
s.Lock()
defer s.Unlock()
for _, session := range s.sessions {
if session == ignoredSession {
continue
@@ -291,16 +294,7 @@ func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session,
if c == ignoredChannel {
continue
}
for _, session := range c.sessions {
if session == ignoredSession {
continue
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(bf, session.clientContext)
bf.WriteUint16(0x0010)
session.QueueSendNonBlocking(bf.Data())
}
c.BroadcastMHF(pkt, ignoredSession)
}
}

View File

@@ -61,7 +61,7 @@ func getLangStrings(s *Server) map[string]string {
strings["commandRaviNoCommand"] = "ラヴィコマンドが指定されていません"
strings["commandRaviStartSuccess"] = "大討伐を開始します"
strings["commandRaviStartError"] = "大討伐は既に開催されています"
strings["commandRaviMultiplier"] = "ラヴィダメージ倍率:x%d"
strings["commandRaviMultiplier"] = "ラヴィダメージ倍率:x%.2f"
strings["commandRaviResSuccess"] = "復活支援を実行します"
strings["commandRaviResError"] = "復活支援は実行されませんでした"
strings["commandRaviSedSuccess"] = "鎮静支援を実行します"
@@ -146,7 +146,7 @@ func getLangStrings(s *Server) map[string]string {
strings["commandRaviNoCommand"] = "No Raviente command specified!"
strings["commandRaviStartSuccess"] = "The Great Slaying will begin in a moment"
strings["commandRaviStartError"] = "The Great Slaying has already begun!"
strings["commandRaviMultiplier"] = "Raviente multiplier is currently %dx"
strings["commandRaviMultiplier"] = "Raviente multiplier is currently %.2fx"
strings["commandRaviResSuccess"] = "Sending resurrection support!"
strings["commandRaviResError"] = "Resurrection support has not been requested!"
strings["commandRaviSedSuccess"] = "Sending sedation support if requested!"

View File

@@ -73,7 +73,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
cryptConn: network.NewCryptConn(conn),
sendPackets: make(chan packet, 20),
clientContext: &clientctx.ClientContext{}, // Unused
sessionStart: Time_Current_Adjusted().Unix(),
sessionStart: TimeAdjusted().Unix(),
stageMoveStack: stringstack.New(),
}
return s
@@ -82,7 +82,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
// Start starts the session packet send and recv loop(s).
func (s *Session) Start() {
go func() {
s.logger.Info("Channel server got connection!", zap.String("remoteaddr", s.rawConn.RemoteAddr().String()))
s.logger.Debug("New connection", zap.String("RemoteAddr", s.rawConn.RemoteAddr().String()))
// Unlike the sign and entrance server,
// the client DOES NOT initalize the channel connection with 8 NULL bytes.
go s.sendLoop()

View File

@@ -69,7 +69,8 @@ func NewStage(ID string) *Stage {
// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage.
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
s.Lock()
defer s.Unlock()
for session := range s.clients {
if session == ignoredSession {
continue

View File

@@ -0,0 +1,25 @@
package channelserver
import (
"time"
)
func TimeAdjusted() time.Time {
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location())
}
func TimeMidnight() time.Time {
baseTime := time.Now().In(time.FixedZone("UTC+9", 9*60*60))
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
}
func TimeWeekStart() time.Time {
midnight := TimeMidnight()
offset := (int(midnight.Weekday()) - 1) * -24
return midnight.Add(time.Hour * time.Duration(offset))
}
func TimeWeekNext() time.Time {
return TimeWeekStart().Add(time.Hour * 24 * 7)
}

View File

@@ -1,58 +0,0 @@
package channelserver
import (
"fmt"
"time"
)
var (
Offset = 9
YearAdjust = -7
MonthAdjust = 0
DayAdjust = 0
)
var (
TimeStatic = time.Time{}
)
func Time_Current() time.Time {
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60))
return baseTime
}
func Time_Current_Adjusted() time.Time {
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), baseTime.Hour(), baseTime.Minute(), baseTime.Second(), baseTime.Nanosecond(), baseTime.Location())
}
func Time_Current_Midnight() time.Time {
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
}
func TimeWeekStart() time.Time {
midnight := Time_Current_Midnight()
offset := (int(midnight.Weekday()) - 1) * -24
return midnight.Add(time.Hour * time.Duration(offset))
}
func TimeWeekNext() time.Time {
return TimeWeekStart().Add(time.Hour * 24 * 7)
}
func Time_Current_Week_uint8() uint8 {
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
_, thisWeek := baseTime.ISOWeek()
_, beginningOfTheMonth := time.Date(baseTime.Year(), baseTime.Month(), 1, 0, 0, 0, 0, baseTime.Location()).ISOWeek()
return uint8(1 + thisWeek - beginningOfTheMonth)
}
func Time_static() time.Time {
if TimeStatic.IsZero() {
TimeStatic = Time_Current_Adjusted()
}
return TimeStatic
}

View File

@@ -58,7 +58,7 @@ func (s *Server) Start() error {
// Shutdown exits the server gracefully.
func (s *Server) Shutdown() {
s.logger.Debug("Shutting down")
s.logger.Debug("Shutting down...")
s.Lock()
s.isShuttingDown = true
@@ -111,7 +111,9 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
return
}
s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt)))
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogInboundMessages {
fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
local := false
if strings.Split(conn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {

View File

@@ -2,6 +2,8 @@ package entranceserver
import (
"encoding/binary"
"encoding/hex"
"fmt"
"net"
"erupe-ce/common/stringsupport"
@@ -66,7 +68,7 @@ func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
bf.WriteUint16(0x3039)
}
}
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Unix()))
bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix()))
bf.WriteUint32(0x0000003C)
return bf.Data()
}
@@ -92,6 +94,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
rawServerData := encodeServerInfo(config, s, local)
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
}
bf := byteframe.NewByteFrame()
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00))
return bf.Data()
@@ -109,11 +116,15 @@ func makeUsrResp(pkt []byte, s *Server) []byte {
err := s.db.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid)
if err != nil {
resp.WriteBytes(make([]byte, 4))
continue
} else {
resp.WriteUint16(sid)
resp.WriteUint16(0)
}
}
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data()))
}
return makeHeader(resp.Data(), "USR", userEntries, 0x00)
}

View File

@@ -111,9 +111,10 @@ func (s *Session) makeSignInResp(uid int) []byte {
if s.server.erupeConfig.HideLoginNotice {
bf.WriteUint8(0)
} else {
bf.WriteUint8(1) // Notice count
noticeText := s.server.erupeConfig.LoginNotice
ps.Uint32(bf, noticeText, true)
bf.WriteUint8(uint8(len(s.server.erupeConfig.LoginNotices)))
for _, notice := range s.server.erupeConfig.LoginNotices {
ps.Uint32(bf, notice, true)
}
}
bf.WriteUint32(s.server.getLastCID(uid))
@@ -127,12 +128,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
bf.WriteUint16(0x0001)
bf.WriteUint16(0x4E20)
ps.Uint16(bf, "", false) // unk ipv4
if returnExpiry.Before(time.Now()) {
// Hack to make Return work while having a non-adjusted expiry
bf.WriteUint32(0)
} else {
bf.WriteUint32(uint32(returnExpiry.Unix()))
}
bf.WriteUint32(uint32(returnExpiry.Unix()))
bf.WriteUint32(0x00000000)
bf.WriteUint32(0x0A5197DF) // unk id
@@ -140,9 +136,9 @@ func (s *Session) makeSignInResp(uid int) []byte {
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
if mezfes {
// Start time
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
// End time
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix()))
bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix()))
bf.WriteUint8(2) // Unk
bf.WriteUint32(20) // Single tickets
bf.WriteUint32(10) // Group tickets

View File

@@ -3,6 +3,7 @@ package signserver
import (
"database/sql"
"encoding/hex"
"fmt"
"net"
"sync"
@@ -23,6 +24,11 @@ type Session struct {
func (s *Session) work() {
pkt, err := s.cryptConn.ReadPacket()
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
if err != nil {
return
}
@@ -33,8 +39,6 @@ func (s *Session) work() {
}
func (s *Session) handlePacket(pkt []byte) error {
sugar := s.logger.Sugar()
bf := byteframe.NewByteFrameFromBytes(pkt)
reqType := string(bf.ReadNullTerminatedBytes())
switch reqType {
@@ -50,13 +54,16 @@ func (s *Session) handlePacket(pkt []byte) error {
characterID := int(bf.ReadUint32())
_ = int(bf.ReadUint32()) // login_token_number
s.server.deleteCharacter(characterID, loginTokenString)
sugar.Infof("Deleted character ID: %v\n", characterID)
s.logger.Info("Deleted character", zap.Int("CharacterID", characterID))
err := s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS
if err != nil {
return nil
}
default:
sugar.Infof("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent()))
s.logger.Warn("Unknown sign request", zap.String("reqType", reqType))
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
}
return nil
@@ -66,14 +73,7 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
reqUsername := string(bf.ReadNullTerminatedBytes())
reqPassword := string(bf.ReadNullTerminatedBytes())
reqSkey := string(bf.ReadNullTerminatedBytes())
s.server.logger.Info(
"Got sign in request",
zap.String("reqUsername", reqUsername),
zap.String("reqPassword", reqPassword),
zap.String("reqSkey", reqSkey),
)
_ = string(bf.ReadNullTerminatedBytes()) // Unk
newCharaReq := false
@@ -90,14 +90,14 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
var serverRespBytes []byte
switch {
case err == sql.ErrNoRows:
s.logger.Info("Account not found", zap.String("reqUsername", reqUsername))
s.logger.Info("User not found", zap.String("Username", reqUsername))
serverRespBytes = makeSignInFailureResp(SIGN_EAUTH)
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount {
s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword))
s.logger.Info("Creating user", zap.String("Username", reqUsername))
err = s.server.registerDBAccount(reqUsername, reqPassword)
if err != nil {
s.logger.Info("Error on creating new account", zap.Error(err))
s.logger.Error("Error registering new user", zap.Error(err))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
break
}
@@ -108,7 +108,7 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
var id int
err = s.server.db.QueryRow("SELECT id FROM users WHERE username = $1", reqUsername).Scan(&id)
if err != nil {
s.logger.Info("Error on querying account id", zap.Error(err))
s.logger.Error("Error getting new user ID", zap.Error(err))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
break
}
@@ -117,15 +117,15 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
break
case err != nil:
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
s.logger.Warn("Got error on SQL query", zap.Error(err))
s.logger.Error("Error getting user details", zap.Error(err))
break
default:
if bcrypt.CompareHashAndPassword([]byte(password), []byte(reqPassword)) == nil {
s.logger.Info("Passwords match!")
s.logger.Debug("Passwords match!")
if newCharaReq {
err = s.server.newUserChara(reqUsername)
if err != nil {
s.logger.Info("Error on adding new character to account", zap.Error(err))
s.logger.Error("Error adding new character to user", zap.Error(err))
serverRespBytes = makeSignInFailureResp(SIGN_EABORT)
break
}
@@ -139,12 +139,16 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error {
// }
serverRespBytes = s.makeSignInResp(id)
} else {
s.logger.Info("Passwords don't match!")
s.logger.Warn("Incorrect password")
serverRespBytes = makeSignInFailureResp(SIGN_EPASS)
}
}
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(serverRespBytes), hex.Dump(serverRespBytes))
}
err = s.cryptConn.SendPacket(serverRespBytes)
if err != nil {
return err

View File

@@ -55,7 +55,7 @@ func (s *Server) Start() error {
// Shutdown exits the server gracefully.
func (s *Server) Shutdown() {
s.logger.Debug("Shutting down")
s.logger.Debug("Shutting down...")
s.Lock()
s.isShuttingDown = true
@@ -86,14 +86,14 @@ func (s *Server) acceptClients() {
}
func (s *Server) handleConnection(conn net.Conn) {
s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String()))
s.logger.Debug("New connection", zap.String("RemoteAddr", conn.RemoteAddr().String()))
defer conn.Close()
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
_, err := io.ReadFull(conn, nullInit)
if err != nil {
s.logger.Error("Error initialising sign server connection", zap.Error(err))
s.logger.Error("Error initializing connection", zap.Error(err))
return
}