diff --git a/bundled-schema/NetcafeDefaults.sql b/bundled-schema/NetcafeDefaults.sql new file mode 100644 index 000000000..458cfa7b9 --- /dev/null +++ b/bundled-schema/NetcafeDefaults.sql @@ -0,0 +1,13 @@ +BEGIN; + +INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity) +VALUES + (1800, 17, 0, 250), + (3600, 17, 0, 500), + (7200, 17, 0, 1000), + (10800, 17, 0, 1500), + (18000, 17, 0, 1750), + (28800, 17, 0, 3000), + (43200, 17, 0, 4000); + +END; \ No newline at end of file diff --git a/config.json b/config.json index ba9af8b77..31ecd8416 100644 --- a/config.json +++ b/config.json @@ -2,13 +2,17 @@ "Host": "127.0.0.1", "BinPath": "bin", "DisableSoftCrash": false, - "devmode": true, - "devmodeoptions": { + "FeaturedWeapons": 1, + "DevMode": true, + "DevModeOptions": { + "PatchServerManifest": "", + "PatchServerFile": "", + "AutoCreateAccount": true, "EnableLauncherServer": false, - "hideLoginNotice": false, - "loginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", - "cleandb": false, - "maxlauncherhr": false, + "HideLoginNotice": false, + "LoginNotice": "
Welcome to Erupe SU9.1 Beta!
Erupe is experimental software, we are not liable for any
issues caused by installing the software!

■Report bugs on Discord!

■Test everything!

■Don't talk to softlocking NPCs!

■Fork the code on GitHub!

Thank you to all of the contributors,

this wouldn't exist without you.", + "CleanDB": false, + "MaxLauncherHR": false, "LogInboundMessages": false, "LogOutboundMessages": false, "MaxHexdumpLength": 256, @@ -19,93 +23,108 @@ "MezFesAlt": false, "DisableMailItems": true, "DisableTokenCheck": false, + "QuestDebugTools": false, "SaveDumps": { "Enabled": true, "OutputDir": "savedata" } }, - "discord": { - "enabled": false, - "bottoken": "", - "realtimeChannelID": "" + "Discord": { + "Enabled": false, + "BotToken": "", + "RealtimeChannelID": "" }, "Commands": [ { - "name": "Rights", - "enabled": true, - "prefix": "!rights" + "Name": "Rights", + "Enabled": false, + "Prefix": "!rights" }, { - "name": "Raviente", - "enabled": true, - "prefix": "!ravi" + "Name": "Raviente", + "Enabled": true, + "Prefix": "!ravi" }, { - "name": "Teleport", - "enabled": false, - "prefix": "!tele" + "Name": "Teleport", + "Enabled": false, + "Prefix": "!tele" }, { - "name": "Reload", - "enabled": true, - "prefix": "!reload" + "Name": "Reload", + "Enabled": true, + "Prefix": "!reload" }, { - "name": "KeyQuest", - "enabled": false, - "prefix": "!kqf" + "Name": "KeyQuest", + "Enabled": false, + "Prefix": "!kqf" + }, { + "Name": "Course", + "Enabled": true, + "Prefix": "!course" } ], - "database": { - "host": "localhost", - "port": 5432, - "user": "postgres", - "password": "", - "database": "erupe" + "Courses": [ + {"Name": "HunterLife", "Enabled": true}, + {"Name": "ExtraA", "Enabled": true}, + {"Name": "Premium", "Enabled": true}, + {"Name": "Assist", "Enabled": false}, + {"Name": "Netcafe", "Enabled": false}, + {"Name": "Hiden", "Enabled": false}, + {"Name": "HunterSupport", "Enabled": false}, + {"Name": "NetcafeBoost", "Enabled": false} + ], + "Database": { + "Host": "localhost", + "Port": 5432, + "User": "postgres", + "Password": "", + "Database": "erupe" }, - "launcher": { - "enabled": true, - "port": 80, + "Launcher": { + "Enabled": false, + "Port": 80, "UseOriginalLauncherFiles": false }, - "sign": { - "enabled": true, - "port": 53312 + "Sign": { + "Enabled": true, + "Port": 53312 }, - "channel": { - "enabled": true + "Channel": { + "Enabled": true }, - "entrance": { - "enabled": true, - "port": 53310, - "entries": [ + "Entrance": { + "Enabled": true, + "Port": 53310, + "Entries": [ { - "name": "Newbie", "description": "", "ip": "", "type": 3, "recommended": 2, "allowedclientflags": 0, - "channels": [ - { "port": 54001, "MaxPlayers": 100 }, - { "port": 54002, "MaxPlayers": 100 } + "Name": "Newbie", "Description": "", "IP": "", "Type": 3, "Recommended": 2, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54001, "MaxPlayers": 100 }, + { "Port": 54002, "MaxPlayers": 100 } ] }, { - "name": "Normal", "description": "", "ip": "", "type": 1, "recommended": 0, "allowedclientflags": 0, - "channels": [ - { "port": 54003, "MaxPlayers": 100 }, - { "port": 54004, "MaxPlayers": 100 } + "Name": "Normal", "Description": "", "IP": "", "Type": 1, "Recommended": 0, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54003, "MaxPlayers": 100 }, + { "Port": 54004, "MaxPlayers": 100 } ] }, { - "name": "Cities", "description": "", "ip": "", "type": 2, "recommended": 0, "allowedclientflags": 0, - "channels": [ - { "port": 54005, "MaxPlayers": 100 } + "Name": "Cities", "Description": "", "IP": "", "Type": 2, "Recommended": 0, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54005, "MaxPlayers": 100 } ] }, { - "name": "Tavern", "description": "", "ip": "", "type": 4, "recommended": 0, "allowedclientflags": 0, - "channels": [ - { "port": 54006, "MaxPlayers": 100 } + "Name": "Tavern", "Description": "", "IP": "", "Type": 4, "Recommended": 0, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54006, "MaxPlayers": 100 } ] }, { - "name": "Return", "description": "", "ip": "", "type": 5, "recommended": 0, "allowedclientflags": 0, - "channels": [ - { "port": 54007, "MaxPlayers": 100 } + "Name": "Return", "Description": "", "IP": "", "Type": 5, "Recommended": 0, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54007, "MaxPlayers": 100 } ] }, { - "name": "MezFes", "description": "", "ip": "", "type": 6, "recommended": 6, "allowedclientflags": 0, - "channels": [ - { "port": 54008, "MaxPlayers": 100 } + "Name": "MezFes", "Description": "", "IP": "", "Type": 6, "Recommended": 6, "AllowedClientFlags": 0, + "Channels": [ + { "Port": 54008, "MaxPlayers": 100 } ] } ] diff --git a/config/config.go b/config/config.go index c90550f53..d2804b9fc 100644 --- a/config/config.go +++ b/config/config.go @@ -15,11 +15,13 @@ type Config struct { Host string `mapstructure:"Host"` BinPath string `mapstructure:"BinPath"` 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 DevMode bool DevModeOptions DevModeOptions Discord Discord Commands []Command + Courses []Course Database Database Launcher Launcher Sign Sign @@ -29,22 +31,25 @@ type Config struct { // DevModeOptions holds various debug/temporary options for use while developing Erupe. type DevModeOptions struct { - EnableLauncherServer bool // Enables the launcher server to be served on port 80 - HideLoginNotice bool // Hide the Erupe notice on login - LoginNotice string // MHFML string of the login notice displayed - CleanDB bool // Automatically wipes the DB on server reset. - MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. - LogInboundMessages bool // Log all messages sent to the server - LogOutboundMessages bool // Log all messages sent to the clients - MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled - DivaEvent int // Diva Defense event status - FestaEvent int // Hunter's Festa event status - TournamentEvent int // VS Tournament event status - MezFesEvent bool // MezFes status - MezFesAlt bool // Swaps out Volpakkun for Tokotoko - DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) - DisableMailItems bool // Hack to prevent english versions of MHF from crashing - SaveDumps SaveDumpOptions + PatchServerManifest string // Manifest patch server override + PatchServerFile string // File patch server override + AutoCreateAccount bool // Automatically create accounts if they don't exist + HideLoginNotice bool // Hide the Erupe notice on login + LoginNotice string // MHFML string of the login notice displayed + CleanDB bool // Automatically wipes the DB on server reset. + MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds. + LogInboundMessages bool // Log all messages sent to the server + LogOutboundMessages bool // Log all messages sent to the clients + MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled + DivaEvent int // Diva Defense event status + FestaEvent int // Hunter's Festa event status + TournamentEvent int // VS Tournament event status + MezFesEvent bool // MezFes status + MezFesAlt bool // Swaps out Volpakkun for Tokotoko + DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) + DisableMailItems bool // Hack to prevent english versions of MHF from crashing + QuestDebugTools bool // Enable various quest debug logs + SaveDumps SaveDumpOptions } type SaveDumpOptions struct { @@ -66,6 +71,12 @@ type Command struct { Prefix string } +// Course represents a course within MHF +type Course struct { + Name string + Enabled bool +} + // Database holds the postgres database config. type Database struct { Host string diff --git a/go.mod b/go.mod index f4e7f1033..1d7f83e7b 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,38 @@ module erupe-ce -go 1.16 +go 1.19 require ( github.com/bwmarrin/discordgo v0.23.2 - github.com/golang/mock v1.6.0 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 - github.com/gorilla/websocket v1.4.2 // indirect github.com/jmoiron/sqlx v1.3.4 github.com/lib/pq v1.10.4 - github.com/mitchellh/mapstructure v1.4.3 // indirect 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 +) + +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 + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/pelletier/go-toml v1.9.3 // indirect + github.com/spf13/afero v1.6.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.2.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect - go.uber.org/zap v1.18.1 - 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.11-0.20220513221640-090b14e8501f // indirect + golang.org/x/sys v0.1.0 // indirect + gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 1e7747306..ca2bd44a9 100644 --- a/go.sum +++ b/go.sum @@ -261,7 +261,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -291,9 +290,8 @@ 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/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -304,6 +302,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f h1:Al51T6tzvuh3oiwX11vex3QgJ2XTedFPGmbEVh8cdoc= +golang.org/x/exp v0.0.0-20221028150844-83b7d23a625f/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -330,7 +330,6 @@ 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.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= @@ -367,8 +366,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -432,12 +429,9 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/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/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= @@ -446,9 +440,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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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/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= @@ -505,8 +498,7 @@ 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.11-0.20220513221640-090b14e8501f h1:OKYpQQVE3DKSc3r3zHVzq46vq5YH7x8xpR3/k9ixmUg= -golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= +golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= 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= diff --git a/main.go b/main.go index 3964ff315..dcb3a14b4 100644 --- a/main.go +++ b/main.go @@ -105,8 +105,6 @@ func main() { // Clear stale data _ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM servers") - _ = db.MustExec("DELETE FROM cafe_accepted") - _ = db.MustExec("UPDATE characters SET cafe_time=0") // Clean the DB if the option is on. if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.CleanDB { @@ -119,7 +117,7 @@ func main() { // Launcher HTTP server. var launcherServer *launcherserver.Server - if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.EnableLauncherServer { + if config.ErupeConfig.Launcher.Enabled { launcherServer = launcherserver.NewServer( &launcherserver.Config{ Logger: logger.Named("launcher"), @@ -235,7 +233,7 @@ func main() { entranceServer.Shutdown() } - if config.ErupeConfig.DevModeOptions.EnableLauncherServer { + if config.ErupeConfig.Launcher.Enabled { launcherServer.Shutdown() } diff --git a/migrations/000001_initial_db.down.sql b/migrations/000001_initial_db.down.sql deleted file mode 100644 index 04f7e46f8..000000000 --- a/migrations/000001_initial_db.down.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN; - -DROP TABLE IF EXISTS sign_sessions; -DROP TABLE IF EXISTS characters; -DROP TABLE IF EXISTS users; - -DROP DOMAIN IF EXISTS uint8; -DROP DOMAIN IF EXISTS uint16; - -END; \ No newline at end of file diff --git a/migrations/000001_initial_db.up.sql b/migrations/000001_initial_db.up.sql deleted file mode 100644 index eeac64a33..000000000 --- a/migrations/000001_initial_db.up.sql +++ /dev/null @@ -1,37 +0,0 @@ -BEGIN; - -CREATE DOMAIN uint8 AS smallint - CHECK(VALUE >= 0 AND VALUE <= 255); - -CREATE DOMAIN uint16 AS integer - CHECK(VALUE >= 0 AND VALUE <= 65536); - -CREATE TABLE users ( - id serial NOT NULL PRIMARY KEY, - username text UNIQUE NOT NULL, - password text NOT NULL, - item_box bytea -); - -CREATE TABLE characters ( - id serial NOT NULL PRIMARY KEY, - user_id bigint REFERENCES users(id), - is_female boolean, - is_new_character boolean, - small_gr_level uint8, - gr_override_mode boolean, - name varchar(15), - unk_desc_string varchar(31), - gr_override_level uint16, - gr_override_unk0 uint8, - gr_override_unk1 uint8 -); - -CREATE TABLE sign_sessions ( - id serial NOT NULL PRIMARY KEY, - user_id bigint REFERENCES users(id), - auth_token_num bigint, - auth_token_str text -); - -END; \ No newline at end of file diff --git a/migrations/000002_alter_characters.down.sql b/migrations/000002_alter_characters.down.sql deleted file mode 100644 index c5eeb425b..000000000 --- a/migrations/000002_alter_characters.down.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN exp, - DROP COLUMN weapon, - DROP COLUMN last_login; - -END; \ No newline at end of file diff --git a/migrations/000002_alter_characters.up.sql b/migrations/000002_alter_characters.up.sql deleted file mode 100644 index 8e92154dc..000000000 --- a/migrations/000002_alter_characters.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -ALTER TABLE characters - ADD COLUMN exp uint16, - ADD COLUMN weapon uint16, - ADD COLUMN last_login integer; - -END; \ No newline at end of file diff --git a/migrations/000003_character_savedata.down.sql b/migrations/000003_character_savedata.down.sql deleted file mode 100644 index d8d1ca8c6..000000000 --- a/migrations/000003_character_savedata.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN savedata; - -END; \ No newline at end of file diff --git a/migrations/000003_character_savedata.up.sql b/migrations/000003_character_savedata.up.sql deleted file mode 100644 index d0f39a223..000000000 --- a/migrations/000003_character_savedata.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - ADD COLUMN savedata bytea; - -END; \ No newline at end of file diff --git a/migrations/000004_character_additional.down.sql b/migrations/000004_character_additional.down.sql deleted file mode 100644 index 5229381cb..000000000 --- a/migrations/000004_character_additional.down.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN decomyset, - DROP COLUMN hunternavi, - DROP COLUMN otomoairou, - DROP COLUMN partner, - DROP COLUMN platebox, - DROP COLUMN platedata, - DROP COLUMN platemyset, - DROP COLUMN rengokudata; - -END; \ No newline at end of file diff --git a/migrations/000004_character_additional.up.sql b/migrations/000004_character_additional.up.sql deleted file mode 100644 index 699351796..000000000 --- a/migrations/000004_character_additional.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -BEGIN; - -ALTER TABLE characters - ADD COLUMN decomyset bytea, - ADD COLUMN hunternavi bytea, - ADD COLUMN otomoairou bytea, - ADD COLUMN partner bytea, - ADD COLUMN platebox bytea, - ADD COLUMN platedata bytea, - ADD COLUMN platemyset bytea, - ADD COLUMN trophy bytea, - ADD COLUMN rengokudata bytea; - -END; diff --git a/migrations/000005_quests.down.sql b/migrations/000005_quests.down.sql deleted file mode 100644 index 60eff8f22..000000000 --- a/migrations/000005_quests.down.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -DROP TABLE IF EXISTS questlists; - -END; \ No newline at end of file diff --git a/migrations/000005_quests.up.sql b/migrations/000005_quests.up.sql deleted file mode 100644 index 26a02f8d1..000000000 --- a/migrations/000005_quests.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -BEGIN; - -CREATE TABLE questlists ( - ind int NOT NULL PRIMARY KEY, - questlist bytea -); - -END; \ No newline at end of file diff --git a/migrations/000006_mercenary.down.sql b/migrations/000006_mercenary.down.sql deleted file mode 100644 index 54cbd957b..000000000 --- a/migrations/000006_mercenary.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN savemercenary; - -END; \ No newline at end of file diff --git a/migrations/000006_mercenary.up.sql b/migrations/000006_mercenary.up.sql deleted file mode 100644 index af025dff0..000000000 --- a/migrations/000006_mercenary.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - ADD COLUMN savemercenary bytea; - -END; \ No newline at end of file diff --git a/migrations/000007_guilds.down.sql b/migrations/000007_guilds.down.sql deleted file mode 100644 index 344ab854e..000000000 --- a/migrations/000007_guilds.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -DROP TABLE guild_characters; -DROP TABLE guilds; - -END; \ No newline at end of file diff --git a/migrations/000007_guilds.up.sql b/migrations/000007_guilds.up.sql deleted file mode 100644 index e327c7db3..000000000 --- a/migrations/000007_guilds.up.sql +++ /dev/null @@ -1,22 +0,0 @@ -BEGIN; - -CREATE TABLE guilds -( - id serial NOT NULL PRIMARY KEY, - name varchar(24), - created_at timestamp DEFAULT NOW(), - leader_id int NOT NULL, - main_motto varchar(255) DEFAULT '' -); - -CREATE TABLE guild_characters -( - id serial NOT NULL PRIMARY KEY, - guild_id bigint REFERENCES guilds (id), - character_id bigint REFERENCES characters (id), - joined_at timestamp DEFAULT NOW() -); - -CREATE UNIQUE INDEX guild_character_unique_index ON guild_characters (character_id); - -END; \ No newline at end of file diff --git a/migrations/000008_guild_additional.down.sql b/migrations/000008_guild_additional.down.sql deleted file mode 100644 index 9252a98f2..000000000 --- a/migrations/000008_guild_additional.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -ALTER TABLE guilds - DROP COLUMN rp; - -ALTER TABLE guild_characters - DROP COLUMN is_applicant, - DROP COLUMN is_sub_leader, - DROP COLUMN order_index; - -END; \ No newline at end of file diff --git a/migrations/000008_guild_additional.up.sql b/migrations/000008_guild_additional.up.sql deleted file mode 100644 index 7e16984d3..000000000 --- a/migrations/000008_guild_additional.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -ALTER TABLE guild_characters - ADD COLUMN is_applicant bool NOT NULL DEFAULT false, - ADD COLUMN is_sub_leader bool NOT NULL DEFAULT false, - ADD COLUMN order_index int NOT NULL DEFAULT 1; - -ALTER TABLE guilds - ADD COLUMN rp uint16 NOT NULL DEFAULT 0; - -END; diff --git a/migrations/000009_character_social.down.sql b/migrations/000009_character_social.down.sql deleted file mode 100644 index 243ea6712..000000000 --- a/migrations/000009_character_social.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN restrict_guild_scout; - -END; \ No newline at end of file diff --git a/migrations/000009_character_social.up.sql b/migrations/000009_character_social.up.sql deleted file mode 100644 index f94ce2043..000000000 --- a/migrations/000009_character_social.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; - -ALTER TABLE characters - ADD COLUMN restrict_guild_scout bool NOT NULL DEFAULT false; - -END; \ No newline at end of file diff --git a/migrations/000010_guild_comments_festival_hall.down.sql b/migrations/000010_guild_comments_festival_hall.down.sql deleted file mode 100644 index c86d1ee5e..000000000 --- a/migrations/000010_guild_comments_festival_hall.down.sql +++ /dev/null @@ -1,10 +0,0 @@ -BEGIN; - -ALTER TABLE guilds - DROP COLUMN comment, - DROP COLUMN festival_colour, - DROP COLUMN guild_hall; - -DROP TYPE festival_colour; - -END; \ No newline at end of file diff --git a/migrations/000010_guild_comments_festival_hall.up.sql b/migrations/000010_guild_comments_festival_hall.up.sql deleted file mode 100644 index 3caec4149..000000000 --- a/migrations/000010_guild_comments_festival_hall.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -CREATE TYPE festival_colour AS ENUM ('none', 'red', 'blue'); - -ALTER TABLE guilds - ADD COLUMN comment varchar(255) NOT NULL DEFAULT '', - ADD COLUMN festival_colour festival_colour DEFAULT 'none', - ADD COLUMN guild_hall int DEFAULT 0; - - -END; \ No newline at end of file diff --git a/migrations/000011_character_points_minidata.down.sql b/migrations/000011_character_points_minidata.down.sql deleted file mode 100644 index 557608be9..000000000 --- a/migrations/000011_character_points_minidata.down.sql +++ /dev/null @@ -1,24 +0,0 @@ -BEGIN; - -ALTER TABLE characters - DROP COLUMN minidata, - DROP COLUMN gacha_trial, - DROP COLUMN gacha_prem, - DROP COLUMN gacha_items, - DROP COLUMN daily_time, - DROP COLUMN frontier_points, - DROP COLUMN netcafe_points, - DROP COLUMN house_info, - DROP COLUMN login_boost, - DROP COLUMN skin_hist, - DROP COLUMN gcp; - -DROP TABLE fpoint_items; -DROP TABLE gacha_shop; -DROP TABLE gacha_shop_items; -DROP TABLE lucky_box_state; -DROP TABLE stepup_state; -DROP TABLE normal_shop_items; -DROP TABLE shop_item_state; - -END; \ No newline at end of file diff --git a/migrations/000011_character_points_minidata.up.sql b/migrations/000011_character_points_minidata.up.sql deleted file mode 100644 index 29baa0790..000000000 --- a/migrations/000011_character_points_minidata.up.sql +++ /dev/null @@ -1,100 +0,0 @@ -BEGIN; -ALTER TABLE characters - ADD COLUMN minidata bytea, - ADD COLUMN gacha_trial int, - ADD COLUMN gacha_prem int, - ADD COLUMN gacha_items bytea, - ADD COLUMN daily_time timestamp, - ADD COLUMN frontier_points int, - ADD COLUMN netcafe_points int, - ADD COLUMN house_info bytea, - ADD COLUMN login_boost bytea, - ADD COLUMN skin_hist bytea, - ADD COLUMN kouryou_point int, - ADD COLUMN gcp int; - -CREATE TABLE fpoint_items -( - hash int, - itemType uint8, - itemID uint16, - quant uint16, - itemValue uint16, - tradeType uint8 -); - - -CREATE TABLE gacha_shop -( - hash bigint, - reqGR int, - reqHR int, - gachaName varchar(255), - gachaLink0 varchar(255), - gachaLink1 varchar(255), - gachaLink2 varchar(255), - extraIcon int, - gachaType int, - hideFlag bool -); - -CREATE TABLE gacha_shop_items -( - shophash int, - entryType uint8, - itemhash int UNIQUE NOT NULL, - currType uint8, - currNumber uint16, - currQuant uint16, - percentage uint16, - rarityIcon uint8, - rollsCount uint8, - itemCount uint8, - dailyLimit uint8, - itemType int[], - itemId int[], - quantity int[] -); - -CREATE TABLE lucky_box_state -( - char_id bigint REFERENCES characters (id), - shophash int UNIQUE NOT NULL, - used_itemhash int[] -); - - -CREATE TABLE stepup_state -( - char_id bigint REFERENCES characters (id), - shophash int UNIQUE NOT NULL, - step_progression int, - step_time timestamp -); - -CREATE TABLE normal_shop_items -( - shoptype int, - shopid int, - itemhash int UNIQUE NOT NULL, - itemID uint16, - Points uint16, - TradeQuantity uint16, - rankReqLow uint16, - rankReqHigh uint16, - rankReqG uint16, - storeLevelReq uint16, - maximumQuantity uint16, - boughtQuantity uint16, - roadFloorsRequired uint16, - weeklyFatalisKills uint16 -); - -CREATE TABLE shop_item_state -( - char_id bigint REFERENCES characters (id), - itemhash int UNIQUE NOT NULL, - usedquantity int -); - -END; \ No newline at end of file diff --git a/migrations/000012_loginboost_etc.down.sql b/migrations/000012_loginboost_etc.down.sql deleted file mode 100644 index 13102b0b0..000000000 --- a/migrations/000012_loginboost_etc.down.sql +++ /dev/null @@ -1,5 +0,0 @@ -BEGIN; - -DROP TABLE login_boost_state; - -END; \ No newline at end of file diff --git a/migrations/000012_loginboost_etc.up.sql b/migrations/000012_loginboost_etc.up.sql deleted file mode 100644 index 4feff1790..000000000 --- a/migrations/000012_loginboost_etc.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE login_boost_state -( - char_id bigint REFERENCES characters (id), - week_req uint8, - week_count uint8, - available bool, - end_time int, - CONSTRAINT id_week UNIQUE(char_id, week_req) -); - -END; \ No newline at end of file diff --git a/migrations/000013_shop_constraints.down.sql b/migrations/000013_shop_constraints.down.sql deleted file mode 100644 index 7b5ac1242..000000000 --- a/migrations/000013_shop_constraints.down.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -ALTER TABLE shop_item_state DROP CONSTRAINT shop_item_state_id_itemhash; -ALTER TABLE shop_item_state ADD CONSTRAINT shop_item_state_itemhash_key UNIQUE (itemhash); - -ALTER TABLE stepup_state DROP CONSTRAINT stepup_state_id_shophash; -ALTER TABLE stepup_state ADD CONSTRAINT stepup_state_shophash_key UNIQUE (shophash); - -ALTER TABLE lucky_box_state DROP CONSTRAINT lucky_box_state_id_shophash; -ALTER TABLE lucky_box_state ADD CONSTRAINT lucky_box_state_shophash_key UNIQUE (shophash); - -END; \ No newline at end of file diff --git a/migrations/000013_shop_constraints.up.sql b/migrations/000013_shop_constraints.up.sql deleted file mode 100644 index a48e81ac2..000000000 --- a/migrations/000013_shop_constraints.up.sql +++ /dev/null @@ -1,12 +0,0 @@ -BEGIN; - -ALTER TABLE shop_item_state DROP CONSTRAINT shop_item_state_itemhash_key; -ALTER TABLE shop_item_state ADD CONSTRAINT shop_item_state_id_itemhash UNIQUE(char_id, itemhash); - -ALTER TABLE stepup_state DROP CONSTRAINT stepup_state_shophash_key; -ALTER TABLE stepup_state ADD CONSTRAINT stepup_state_id_shophash UNIQUE(char_id, shophash); - -ALTER TABLE lucky_box_state DROP CONSTRAINT lucky_box_state_shophash_key; -ALTER TABLE lucky_box_state ADD CONSTRAINT lucky_box_state_id_shophash UNIQUE(char_id, shophash); - -END; \ No newline at end of file diff --git a/migrations/000014_guild_flags_applications.down.sql b/migrations/000014_guild_flags_applications.down.sql deleted file mode 100644 index 6cfe86288..000000000 --- a/migrations/000014_guild_flags_applications.down.sql +++ /dev/null @@ -1,18 +0,0 @@ -BEGIN; -ALTER TABLE guild_characters - RENAME COLUMN avoid_leadership TO is_sub_leader; - -ALTER TABLE guild_characters - ADD COLUMN is_applicant bool NOT NULL DEFAULT false; - -ALTER TABLE guilds - DROP COLUMN icon, - ALTER COLUMN main_motto TYPE varchar USING '', - DROP COLUMN sub_motto; - -ALTER TABLE guilds - ALTER COLUMN main_motto SET DEFAULT ''; - -DROP TABLE guild_applications; -DROP TYPE guild_application_type; -END; \ No newline at end of file diff --git a/migrations/000014_guild_flags_applications.up.sql b/migrations/000014_guild_flags_applications.up.sql deleted file mode 100644 index 4bc09668a..000000000 --- a/migrations/000014_guild_flags_applications.up.sql +++ /dev/null @@ -1,30 +0,0 @@ -BEGIN; -CREATE TYPE guild_application_type AS ENUM ('applied', 'invited'); - -CREATE TABLE guild_applications -( - id serial NOT NULL PRIMARY KEY, - guild_id int NOT NULL REFERENCES guilds (id), - character_id int NOT NULL REFERENCES characters (id), - actor_id int NOT NULL REFERENCES characters (id), - application_type guild_application_type NOT NULL, - created_at timestamp NOT NULL DEFAULT now(), - CONSTRAINT guild_application_character_id UNIQUE (guild_id, character_id) -); - -CREATE INDEX guild_application_type_index ON guild_applications (application_type); - -ALTER TABLE guild_characters - DROP COLUMN is_applicant; - -ALTER TABLE guild_characters - RENAME COLUMN is_sub_leader TO avoid_leadership; - -ALTER TABLE guilds - ALTER COLUMN main_motto SET DEFAULT 0; - -ALTER TABLE guilds - ADD COLUMN icon bytea, - ADD COLUMN sub_motto int DEFAULT 0, - ALTER COLUMN main_motto TYPE int USING 0; -END; \ No newline at end of file diff --git a/migrations/000015_mail.down.sql b/migrations/000015_mail.down.sql deleted file mode 100644 index 8ed5b834f..000000000 --- a/migrations/000015_mail.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -DROP TABLE mail; -END; \ No newline at end of file diff --git a/migrations/000015_mail.up.sql b/migrations/000015_mail.up.sql deleted file mode 100644 index ba0dc4edd..000000000 --- a/migrations/000015_mail.up.sql +++ /dev/null @@ -1,19 +0,0 @@ -BEGIN; -CREATE TABLE mail -( - id SERIAL NOT NULL PRIMARY KEY, - sender_id INT NOT NULL REFERENCES characters (id), - recipient_id INT NOT NULL REFERENCES characters (id), - subject VARCHAR NOT NULL DEFAULT '', - body VARCHAR NOT NULL DEFAULT '', - read BOOL NOT NULL DEFAULT FALSE, - attached_item_received BOOL NOT NULL DEFAULT FALSE, - attached_item INT DEFAULT NULL, - attached_item_amount INT NOT NULL DEFAULT 1, - is_guild_invite BOOL NOT NULL DEFAULT FALSE, - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - deleted BOOL NOT NULL DEFAULT FALSE -); - -CREATE INDEX mail_recipient_deleted_created_id_index ON mail (recipient_id, deleted, created_at DESC, id DESC); -END; \ No newline at end of file diff --git a/migrations/000016_server.down.sql b/migrations/000016_server.down.sql deleted file mode 100644 index a5fb2d6b0..000000000 --- a/migrations/000016_server.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -DROP TABLE public.servers; -END; \ No newline at end of file diff --git a/migrations/000016_server.up.sql b/migrations/000016_server.up.sql deleted file mode 100644 index f6a7798e7..000000000 --- a/migrations/000016_server.up.sql +++ /dev/null @@ -1,21 +0,0 @@ -BEGIN; --- Table: public.servers - --- DROP TABLE IF EXISTS public.servers; - -CREATE TABLE IF NOT EXISTS public.servers -( - server_id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), - server_name text COLLATE pg_catalog."default", - season integer, - current_players integer, - event_id integer, - event_expiration integer, - CONSTRAINT servers_pkey PRIMARY KEY (server_id) -) - -TABLESPACE pg_default; - -ALTER TABLE IF EXISTS public.servers - OWNER to postgres; -END; \ No newline at end of file diff --git a/migrations/000017_account.down.sql b/migrations/000017_account.down.sql deleted file mode 100644 index c52957af0..000000000 --- a/migrations/000017_account.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -BEGIN; -DROP TABLE public.account_ban; -DROP TABLE public.account_history; -DROP TABLE public.account_moderation; -DROP TABLE public.account_sub; -END; \ No newline at end of file diff --git a/migrations/000017_account.up.sql b/migrations/000017_account.up.sql deleted file mode 100644 index f636d619f..000000000 --- a/migrations/000017_account.up.sql +++ /dev/null @@ -1,45 +0,0 @@ -BEGIN; - -CREATE TABLE IF NOT EXISTS public.account_ban -( - user_id integer NOT NULL, - title text COLLATE pg_catalog."default", - reason text COLLATE pg_catalog."default", - date text COLLATE pg_catalog."default", - pass_origin text COLLATE pg_catalog."default", - pass_block text COLLATE pg_catalog."default", - CONSTRAINT ban_pkey PRIMARY KEY (user_id) -); - -CREATE TABLE IF NOT EXISTS public.account_history -( - report_id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), - user_id integer, - title text COLLATE pg_catalog."default", - reason text COLLATE pg_catalog."default", - date date, - CONSTRAINT account_history_pkey PRIMARY KEY (report_id) -); - -CREATE TABLE IF NOT EXISTS public.account_moderation -( - id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), - username text COLLATE pg_catalog."default", - password text COLLATE pg_catalog."default", - type text COLLATE pg_catalog."default", - CONSTRAINT account_moderation_pkey PRIMARY KEY (id) -); - -CREATE TABLE IF NOT EXISTS public.account_sub -( - id integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), - discord_id text COLLATE pg_catalog."default", - erupe_account text COLLATE pg_catalog."default", - erupe_password text COLLATE pg_catalog."default", - date_inscription date, - country text COLLATE pg_catalog."default", - presentation text COLLATE pg_catalog."default", - CONSTRAINT account_auth_pkey PRIMARY KEY (id) -); - -END; \ No newline at end of file diff --git a/migrations/000018_event_week.down.sql b/migrations/000018_event_week.down.sql deleted file mode 100644 index 756df1f22..000000000 --- a/migrations/000018_event_week.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -DROP TABLE public.event_week; -END; \ No newline at end of file diff --git a/migrations/000018_event_week.up.sql b/migrations/000018_event_week.up.sql deleted file mode 100644 index f536871f2..000000000 --- a/migrations/000018_event_week.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -BEGIN; - -CREATE TABLE IF NOT EXISTS public.event_week -( - id integer NOT NULL, - event_id integer NOT NULL, - date_expiration integer NOT NULL, - CONSTRAINT event_week_pkey PRIMARY KEY (id) -); - -END; \ No newline at end of file diff --git a/migrations/000019_gook.down.sql b/migrations/000019_gook.down.sql deleted file mode 100644 index a4b544e9a..000000000 --- a/migrations/000019_gook.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -DROP TABLE public.gook; -END; \ No newline at end of file diff --git a/migrations/000019_gook.up.sql b/migrations/000019_gook.up.sql deleted file mode 100644 index fe5c051c0..000000000 --- a/migrations/000019_gook.up.sql +++ /dev/null @@ -1,20 +0,0 @@ -BEGIN; - -CREATE TABLE IF NOT EXISTS public.gook -( - id serial NOT NULL PRIMARY KEY, - gook0 bytea, - gook1 bytea, - gook2 bytea, - gook3 bytea, - gook4 bytea, - gook5 bytea, - gook0status boolean, - gook1status boolean, - gook2status boolean, - gook3status boolean, - gook4status boolean, - gook5status boolean -); - -END; \ No newline at end of file diff --git a/migrations/000020_history.down.sql b/migrations/000020_history.down.sql deleted file mode 100644 index 623bd53c8..000000000 --- a/migrations/000020_history.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -BEGIN; -DROP TABLE public.history; -END; \ No newline at end of file diff --git a/migrations/000020_history.up.sql b/migrations/000020_history.up.sql deleted file mode 100644 index 96d6b36c8..000000000 --- a/migrations/000020_history.up.sql +++ /dev/null @@ -1,13 +0,0 @@ -BEGIN; - -CREATE TABLE IF NOT EXISTS public.history -( - user_id integer, - admin_id integer, - report_id integer NOT NULL, - title text COLLATE pg_catalog."default", - reason text COLLATE pg_catalog."default", - CONSTRAINT history_pkey PRIMARY KEY (report_id) -); - -END; \ No newline at end of file diff --git a/network/clientctx/clientcontext.go b/network/clientctx/clientcontext.go index 0f890f6d7..021ae3299 100644 --- a/network/clientctx/clientcontext.go +++ b/network/clientctx/clientcontext.go @@ -1,8 +1,4 @@ package clientctx -import "erupe-ce/common/stringsupport" - // ClientContext holds contextual data required for packet encoding/decoding. -type ClientContext struct { - StrConv *stringsupport.StringConverter -} +type ClientContext struct{} // Unused diff --git a/network/mhfpacket/msg_mhf_enumerate_guild.go b/network/mhfpacket/msg_mhf_enumerate_guild.go index 65edbc555..691f8241f 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild.go @@ -32,6 +32,7 @@ type MsgMhfEnumerateGuild struct { AckHandle uint32 Type EnumerateGuildType Page uint8 + Sorting bool RawDataPayload []byte } @@ -45,6 +46,8 @@ func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli m.AckHandle = bf.ReadUint32() m.Type = EnumerateGuildType(bf.ReadUint8()) m.Page = bf.ReadUint8() + m.Sorting = bf.ReadBool() + _ = bf.ReadUint8() m.RawDataPayload = bf.DataFromCurrent() bf.Seek(-2, io.SeekEnd) return nil diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index b343dd0c4..0702a5b3e 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -2,6 +2,8 @@ package mhfpacket import ( "errors" + ps "erupe-ce/common/pascalstring" + "golang.org/x/exp/slices" "erupe-ce/common/byteframe" "erupe-ce/network" @@ -34,6 +36,12 @@ type ClientRight struct { Timestamp uint32 } +type Course struct { + Aliases []string + ID uint16 + Value uint32 +} + // MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT type MsgSysUpdateRight struct { ClientRespAckHandle uint32 // If non-0, requests the client to send back a MSG_SYS_ACK packet with this value. @@ -63,9 +71,40 @@ func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame, ctx *clientctx.Client bf.WriteUint16(v.Unk0) bf.WriteUint32(v.Timestamp) } - - bf.WriteUint16(m.UnkSize) // String of upto 0x800 bytes, update client login token / password in the game's launcherstate struct. - //bf.WriteBytes(m.UpdatedClientLoginToken) - + ps.Uint16(bf, "", false) // update client login token / password in the game's launcherstate struct return nil } + +func Courses() []Course { + var courses = []Course{ + {[]string{"Trial", "TL"}, 1, 0x00000002}, + {[]string{"HunterLife", "HL"}, 2, 0x00000004}, + {[]string{"ExtraA", "Extra", "EX"}, 3, 0x00000008}, + {[]string{"ExtraB"}, 4, 0x00000010}, + {[]string{"Mobile"}, 5, 0x00000020}, + {[]string{"Premium"}, 6, 0x00000040}, + {[]string{"Pallone"}, 7, 0x00000080}, + {[]string{"Assist", "Legend", "Rasta"}, 8, 0x00000100}, // Legend + {[]string{"Netcafe", "N", "Cafe"}, 9, 0x00000200}, + {[]string{"Hiden", "Secret"}, 10, 0x00000400}, // Secret + {[]string{"HunterSupport", "HunterAid", "Support", "Royal", "Aid"}, 11, 0x00000800}, // Royal + {[]string{"NetcafeBoost", "NBoost", "Boost"}, 12, 0x00001000}, + } + return courses +} + +// GetCourseStruct returns a slice of Course(s) from a rights integer +func GetCourseStruct(rights uint32) []Course { + var resp []Course + s := Courses() + slices.SortStableFunc(s, func(i, j Course) bool { + return i.ID > j.ID + }) + for _, course := range s { + if rights-course.Value < 0x80000000 { + resp = append(resp, course) + rights -= course.Value + } + } + return resp +} diff --git a/patch-schema/active-feature.sql b/patch-schema/active-feature.sql new file mode 100644 index 000000000..f8b835100 --- /dev/null +++ b/patch-schema/active-feature.sql @@ -0,0 +1,9 @@ +BEGIN; + +CREATE TABLE IF NOT EXISTS public.feature_weapon +( + start_time timestamp without time zone NOT NULL, + featured integer NOT NULL +); + +END; \ No newline at end of file diff --git a/patch-schema/netcafe-2.sql b/patch-schema/netcafe-2.sql new file mode 100644 index 000000000..d2a1f0763 --- /dev/null +++ b/patch-schema/netcafe-2.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE IF EXISTS public.characters + ADD COLUMN cafe_reset timestamp without time zone; + +END; \ No newline at end of file diff --git a/patch-schema/netcafe.sql b/patch-schema/netcafe.sql index e284742ce..563e10fb0 100644 --- a/patch-schema/netcafe.sql +++ b/patch-schema/netcafe.sql @@ -27,14 +27,4 @@ CREATE TABLE IF NOT EXISTS public.cafe_accepted character_id integer NOT NULL ); -INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity) -VALUES - (1800, 17, 0, 250), - (3600, 17, 0, 500), - (7200, 17, 0, 1000), - (10800, 17, 0, 1500), - (18000, 17, 0, 1750), - (28800, 17, 0, 3000), - (43200, 17, 0, 4000); - END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1eb014145..5d19d93f4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,7 +6,6 @@ import ( "erupe-ce/common/stringsupport" "fmt" "io" - "math" "net" "strings" "time" @@ -74,26 +73,16 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) { } func updateRights(s *Session) { - s.rights = uint32(0x0E) - s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&s.rights) - - rights := make([]mhfpacket.ClientRight, 0) - tempRights := s.rights - for i := 30; i > 0; i-- { - right := uint32(math.Pow(2, float64(i))) - if tempRights-right < 0x80000000 { - if i == 1 { - continue - } - rights = append(rights, mhfpacket.ClientRight{ID: uint16(i), Timestamp: 0x70DB59F0}) - tempRights -= right - } + rightsInt := uint32(0x0E) + s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.charID).Scan(&rightsInt) + s.courses = mhfpacket.GetCourseStruct(rightsInt) + rights := []mhfpacket.ClientRight{{1, 0, 0}} + for _, course := range s.courses { + rights = append(rights, mhfpacket.ClientRight{ID: course.ID, Timestamp: 0x70DB59F0}) } - rights = append(rights, mhfpacket.ClientRight{ID: 1, Timestamp: 0}) - update := &mhfpacket.MsgSysUpdateRight{ ClientRespAckHandle: 0, - Bitfield: s.rights, + Bitfield: rightsInt, Rights: rights, UnkSize: 0, } @@ -190,6 +179,16 @@ func logoutPlayer(s *Session) { s.server.Unlock() for _, stage := range s.server.stages { + // Tell sessions registered to disconnecting players quest to unregister + if stage.host != nil && stage.host.charID == s.charID { + for _, sess := range s.server.sessions { + for rSlot := range stage.reservedClientSlots { + if sess.charID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" { + sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + } + } + } + } for session := range stage.clients { if session.charID == s.charID { delete(stage.clients, session) @@ -214,7 +213,7 @@ func logoutPlayer(s *Session) { timePlayed += sessionTime var rpGained int - if s.rights >= 0x40000000 { // N Course + if s.FindCourse("Netcafe").ID != 0 { rpGained = timePlayed / 900 timePlayed = timePlayed % 900 } else { diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index c7d826429..15b01f510 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -4,6 +4,7 @@ import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" + "fmt" "go.uber.org/zap" "io" "time" @@ -71,15 +72,22 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetCafeDuration) bf := byteframe.NewByteFrame() + var cafeReset time.Time + err := s.server.db.QueryRow(`SELECT cafe_reset FROM characters WHERE id=$1`, s.charID).Scan(&cafeReset) + if Time_Current_Adjusted().After(cafeReset) { + cafeReset = TimeWeekNext() + s.server.db.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2; DELETE FROM cafe_accepted WHERE character_id=$2`, cafeReset, s.charID) + } + var cafeTime uint32 - err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime) + err = s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime) if err != nil { panic(err) } cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime bf.WriteUint32(cafeTime) // Total cafe time bf.WriteUint16(0) - ps.Uint16(bf, "Resets at next maintenance", true) + ps.Uint16(bf, fmt.Sprintf("Resets on %s %d", cafeReset.Month().String(), cafeReset.Day()), true) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 3791ea241..64666d9af 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -7,6 +7,7 @@ import ( "erupe-ce/network/binpacket" "erupe-ce/network/mhfpacket" "fmt" + "golang.org/x/exp/slices" "math" "math/rand" "strings" @@ -19,6 +20,7 @@ import ( const ( BinaryMessageTypeState = 0 BinaryMessageTypeChat = 1 + BinaryMessageTypeQuest = 2 BinaryMessageTypeData = 3 BinaryMessageTypeMailNotify = 4 BinaryMessageTypeEmote = 6 @@ -89,6 +91,18 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } } + if s.server.erupeConfig.DevModeOptions.QuestDebugTools == true && s.server.erupeConfig.DevMode { + if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 { + // This is only correct most of the time + tmp.ReadBytes(20) + tmp.SetLE() + x := tmp.ReadFloat32() + y := tmp.ReadFloat32() + z := tmp.ReadFloat32() + s.logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z})) + } + } + // Parse out the real casted binary payload var msgBinTargeted *binpacket.MsgBinTargeted var authorLen, msgLen uint16 @@ -298,6 +312,53 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } } + if strings.HasPrefix(chatMessage.Message, commands["Course"].Prefix) { + if commands["Course"].Enabled { + var name string + n, err := fmt.Sscanf(chatMessage.Message, "!course %s", &name) + if err != nil || n != 1 { + sendServerChatMessage(s, "Error in command. Format: !course ") + } else { + name = strings.ToLower(name) + for _, course := range mhfpacket.Courses() { + for _, alias := range course.Aliases { + if strings.ToLower(name) == strings.ToLower(alias) { + if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases[0], Enabled: true}) { + if s.FindCourse(name).Value != 0 { + ei := slices.IndexFunc(s.courses, func(c mhfpacket.Course) bool { + for _, alias := range c.Aliases { + if strings.ToLower(name) == strings.ToLower(alias) { + return true + } + } + return false + }) + if ei != -1 { + s.courses = append(s.courses[:ei], s.courses[ei+1:]...) + sendServerChatMessage(s, fmt.Sprintf(`%s Course disabled.`, course.Aliases[0])) + } + } else { + s.courses = append(s.courses, course) + sendServerChatMessage(s, fmt.Sprintf(`%s Course enabled.`, course.Aliases[0])) + } + var newInt uint32 + for _, course := range s.courses { + newInt += course.Value + } + s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", newInt, s.charID) + updateRights(s) + } else { + sendServerChatMessage(s, fmt.Sprintf(`%s Course is locked.`, course.Aliases[0])) + } + } + } + } + } + } else { + sendDisabledCommandMessage(s, commands["Course"]) + } + } + if strings.HasPrefix(chatMessage.Message, commands["Raviente"].Prefix) { if commands["Raviente"].Enabled { if getRaviSemaphore(s) != "" { diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index b641d3973..28521d10d 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -2,6 +2,7 @@ package channelserver import ( "encoding/hex" + "erupe-ce/common/bfutil" "erupe-ce/common/stringsupport" "fmt" "io" @@ -9,7 +10,6 @@ import ( "os" "path/filepath" - "erupe-ce/common/bfutil" "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" "erupe-ce/server/channelserver/compression/deltacomp" @@ -49,7 +49,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { characterSaveData.Save(s) s.logger.Info("Wrote recompressed savedata back to DB.") - characterSaveData.Name = s.clientContext.StrConv.MustDecode(bfutil.UpToNull(characterSaveData.decompSave[88:100])) + characterSaveData.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(characterSaveData.decompSave[88:100])) _, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID) if err != nil { s.logger.Fatal("Failed to update character name in db", zap.Error(err)) diff --git a/server/channelserver/handlers_event.go b/server/channelserver/handlers_event.go index b6765f998..95227709f 100644 --- a/server/channelserver/handlers_event.go +++ b/server/channelserver/handlers_event.go @@ -53,38 +53,46 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) { } type activeFeature struct { - StartTime time.Time - ActiveFeatures uint32 - Unk1 uint16 + StartTime time.Time `db:"start_time"` + ActiveFeatures uint32 `db:"featured"` } func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule) - persistentEventSchedule := make([]activeFeature, 8) // generate day after weekly restart - for x := -1; x < 7; x++ { - feat := generateActiveWeapons(14) // number of active weapons - // TODO: only generate this once per restart (server should be restarted weekly) - // then load data from db instead of regenerating - persistentEventSchedule[x+1] = activeFeature{ - StartTime: Time_Current_Midnight().Add(time.Duration(24*x) * time.Hour), - ActiveFeatures: uint32(feat), - Unk1: 0, + + 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) + } + + 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) } + 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) } - resp := byteframe.NewByteFrame() - resp.WriteUint8(uint8(len(persistentEventSchedule))) // Entry count, client only parses the first 7 or 8. - resp.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) // 5 minutes ago server time - - for _, es := range persistentEventSchedule { - resp.WriteUint32(uint32(es.StartTime.Unix())) - resp.WriteUint32(es.ActiveFeatures) - resp.WriteUint16(es.Unk1) + bf := byteframe.NewByteFrame() + bf.WriteUint8(2) + bf.WriteUint32(uint32(Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) + for _, feature := range features { + bf.WriteUint32(uint32(feature.StartTime.Unix())) + bf.WriteUint32(feature.ActiveFeatures) + bf.WriteUint16(0) } - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } -func generateActiveWeapons(count int) int { +func generateFeatureWeapons(count int) activeFeature { nums := make([]int, 0) var result int r := rand.New(rand.NewSource(time.Now().UnixNano())) @@ -104,7 +112,7 @@ func generateActiveWeapons(count int) int { for _, num := range nums { result += int(math.Pow(2, float64(num))) } - return result + return activeFeature{ActiveFeatures: uint32(result)} } type loginBoost struct { diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index 6e5080ac3..9f925f3dc 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -6,6 +6,7 @@ import ( ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" "math/rand" + "sort" "time" ) @@ -253,11 +254,19 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - var souls uint32 + var souls, exists uint32 s.server.db.QueryRow("SELECT souls FROM guild_characters WHERE character_id=$1", s.charID).Scan(&souls) + err = s.server.db.QueryRow("SELECT prize_id FROM festa_prizes_accepted WHERE prize_id=0 AND character_id=$1", s.charID).Scan(&exists) bf := byteframe.NewByteFrame() bf.WriteUint32(souls) - bf.WriteUint32(0) // unk + if err != nil { + bf.WriteBool(true) + bf.WriteBool(false) + } else { + bf.WriteBool(false) + bf.WriteBool(true) + } + bf.WriteUint16(0) // Unk doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } @@ -280,10 +289,10 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { return } resp.WriteUint32(guild.Souls) - resp.WriteUint32(0) // unk - resp.WriteUint32(0) // unk, rank? - resp.WriteUint32(0) // unk - resp.WriteUint32(0) // unk + resp.WriteUint32(1) // unk + resp.WriteUint32(1) // unk + resp.WriteUint32(1) // unk, rank? + resp.WriteUint32(1) // unk doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } @@ -302,6 +311,9 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) { bf := byteframe.NewByteFrame() bf.WriteUint16(uint16(len(members))) bf.WriteUint16(0) // Unk + sort.Slice(members, func(i, j int) bool { + return members[i].Souls > members[j].Souls + }) for _, member := range members { bf.WriteUint32(member.CharID) bf.WriteUint32(member.Souls) @@ -342,6 +354,7 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfAcquireFesta) + s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.charID) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } @@ -368,7 +381,7 @@ type Prize struct { func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaPersonalPrize) - rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='personal'") + rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='personal'`, s.charID) var count uint32 prizeData := byteframe.NewByteFrame() for rows.Next() { @@ -394,7 +407,7 @@ func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateFestaIntermediatePrize) - rows, _ := s.server.db.Queryx("SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = 4) AS claimed FROM festa_prizes fp WHERE type='guild'") + rows, _ := s.server.db.Queryx(`SELECT id, tier, souls_req, item_id, num_item, (SELECT count(*) FROM festa_prizes_accepted fpa WHERE fp.id = fpa.prize_id AND fpa.character_id = $1) AS claimed FROM festa_prizes fp WHERE type='guild'`, s.charID) var count uint32 prizeData := byteframe.NewByteFrame() for rows.Next() { diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 3a4b047fc..a6129bd1e 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -1034,7 +1034,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { } applicants, err := GetGuildMembers(s, guild.ID, true) - if err != nil { + if err != nil || (characterGuildData != nil && !characterGuildData.CanRecruit()) { bf.WriteUint16(0) } else { bf.WriteUint16(uint16(len(applicants))) @@ -1101,9 +1101,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { switch pkt.Type { case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME: - bf.ReadBytes(10) + bf.ReadBytes(8) searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())) - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10) if err == nil { for rows.Next() { guild, _ := buildGuildObjectFromDbResult(rows, err, s) @@ -1111,9 +1111,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME: - bf.ReadBytes(10) + bf.ReadBytes(8) searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())) - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2 LIMIT 11`, guildInfoSelectQuery), searchTerm, pkt.Page*10) if err == nil { for rows.Next() { guild, _ := buildGuildObjectFromDbResult(rows, err, s) @@ -1121,7 +1121,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID: - bf.ReadBytes(2) ID := bf.ReadUint32() rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID) if err == nil { @@ -1131,11 +1130,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS: - sorting := bf.ReadUint8() - if sorting == 1 { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + if pkt.Sorting { + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } else { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } if err == nil { for rows.Next() { @@ -1144,11 +1142,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION: - sorting := bf.ReadUint8() - if sorting == 1 { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + if pkt.Sorting { + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } else { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } if err == nil { for rows.Next() { @@ -1157,11 +1154,10 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK: - sorting := bf.ReadUint8() - if sorting == 1 { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + if pkt.Sorting { + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } else { - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) } if err == nil { for rows.Next() { @@ -1170,10 +1166,9 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO: - bf.ReadBytes(2) mainMotto := bf.ReadUint16() subMotto := bf.ReadUint16() - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3 LIMIT 11`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10) if err == nil { for rows.Next() { guild, _ := buildGuildObjectFromDbResult(rows, err, s) @@ -1182,7 +1177,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING: // Assume the player wants the newest guilds with open recruitment - rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10) + rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1 LIMIT 11`, guildInfoSelectQuery), pkt.Page*10) if err == nil { for rows.Next() { guild, _ := buildGuildObjectFromDbResult(rows, err, s) @@ -1202,7 +1197,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } switch pkt.Type { case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME: - bf.ReadBytes(10) + bf.ReadBytes(8) searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) for _, alliance := range tempAlliances { if strings.Contains(alliance.Name, searchTerm) { @@ -1210,7 +1205,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME: - bf.ReadBytes(10) + bf.ReadBytes(8) searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) for _, alliance := range tempAlliances { if strings.Contains(alliance.ParentGuild.LeaderName, searchTerm) { @@ -1218,7 +1213,6 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_ID: - bf.ReadBytes(2) ID := bf.ReadUint32() for _, alliance := range tempAlliances { if alliance.ParentGuild.LeaderCharID == ID { @@ -1226,8 +1220,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } } case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_MEMBERS: - sorting := bf.ReadBool() - if sorting { + if pkt.Sorting { sort.Slice(tempAlliances, func(i, j int) bool { return tempAlliances[i].TotalMembers > tempAlliances[j].TotalMembers }) @@ -1238,8 +1231,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { } alliances = tempAlliances case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ORDER_REGISTRATION: - sorting := bf.ReadBool() - if sorting { + if pkt.Sorting { sort.Slice(tempAlliances, func(i, j int) bool { return tempAlliances[i].CreatedAt.Unix() > tempAlliances[j].CreatedAt.Unix() }) @@ -1260,16 +1252,14 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { bf = byteframe.NewByteFrame() if pkt.Type > 8 { - if len(guilds) > 10 { - bf.WriteUint16(10) - } else { - bf.WriteUint16(uint16(len(alliances))) + hasNextPage := false + if len(alliances) > 10 { + hasNextPage = true + alliances = alliances[:10] } - bf.WriteUint8(0x00) // Unk - for i, alliance := range alliances { - if i == 10 { - break - } + bf.WriteUint16(uint16(len(alliances))) + bf.WriteBool(hasNextPage) + for _, alliance := range alliances { bf.WriteUint32(alliance.ID) bf.WriteUint32(alliance.ParentGuild.LeaderCharID) bf.WriteUint16(alliance.TotalMembers) @@ -1288,16 +1278,14 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteBool(true) // TODO: Enable GuildAlliance applications } } else { + hasNextPage := false if len(guilds) > 10 { - bf.WriteUint16(10) - } else { - bf.WriteUint16(uint16(len(guilds))) + hasNextPage = true + guilds = guilds[:10] } - bf.WriteUint8(0x01) // Unk - for i, guild := range guilds { - if i == 10 { - break - } + bf.WriteUint16(uint16(len(guilds))) + bf.WriteBool(hasNextPage) + for _, guild := range guilds { bf.WriteUint32(guild.ID) bf.WriteUint32(guild.LeaderCharID) bf.WriteUint16(guild.MemberCount) diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index c01b0e006..10c60d04f 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -2,7 +2,7 @@ package channelserver import ( "fmt" - "io" + "go.uber.org/zap" "io/ioutil" "os" "path/filepath" @@ -14,14 +14,24 @@ import ( func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetFile) - // Debug print the request. if pkt.IsScenario { - fmt.Printf("%+v\n", pkt.ScenarioIdentifer) + if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode { + s.logger.Debug( + "Scenario", + zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID), + zap.Uint32("MainID", pkt.ScenarioIdentifer.MainID), + zap.Uint8("ChapterID", pkt.ScenarioIdentifer.ChapterID), + zap.Uint8("Flags", pkt.ScenarioIdentifer.Flags), + ) + } filename := fmt.Sprintf("%d_0_0_0_S%d_T%d_C%d", pkt.ScenarioIdentifer.CategoryID, pkt.ScenarioIdentifer.MainID, pkt.ScenarioIdentifer.Flags, pkt.ScenarioIdentifer.ChapterID) // Read the scenario file. data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("scenarios/%s.bin", filename))) if err != nil { - panic(err) + s.logger.Error(fmt.Sprintf("Failed to open file: %s/scenarios/%s.bin", s.server.erupeConfig.BinPath, filename)) + // This will crash the game. + doAckBufSucceed(s, pkt.AckHandle, data) + return } doAckBufSucceed(s, pkt.AckHandle, data) } else { @@ -32,10 +42,19 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { } doAckBufSucceed(s, pkt.AckHandle, data) } else { + if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode { + s.logger.Debug( + "Quest", + zap.String("Filename", pkt.Filename), + ) + } // Get quest file. data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename))) if err != nil { - s.logger.Fatal(fmt.Sprintf("Failed to open quest file: quests/%s.bin", pkt.Filename)) + s.logger.Error(fmt.Sprintf("Failed to open file: %s/quests/%s.bin", s.server.erupeConfig.BinPath, pkt.Filename)) + // This will crash the game. + doAckBufSucceed(s, pkt.AckHandle, data) + return } doAckBufSucceed(s, pkt.AckHandle, data) } diff --git a/server/channelserver/handlers_rengoku.go b/server/channelserver/handlers_rengoku.go index d9f58df57..c1e8d2dbd 100644 --- a/server/channelserver/handlers_rengoku.go +++ b/server/channelserver/handlers_rengoku.go @@ -3,6 +3,7 @@ package channelserver import ( ps "erupe-ce/common/pascalstring" "fmt" + "github.com/jmoiron/sqlx" "io/ioutil" "path/filepath" @@ -96,20 +97,13 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, data) } -const rengokuScoreQuery = ` -SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id -FROM rengoku_score rs +const rengokuScoreQuery = `, c.name FROM rengoku_score rs LEFT JOIN characters c ON c.id = rs.character_id -LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id -` +LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id ` type RengokuScore struct { - Name string `db:"name"` - GuildID int `db:"guild_id"` - MaxStagesMP uint32 `db:"max_stages_mp"` - MaxPointsMP uint32 `db:"max_points_mp"` - MaxStagesSP uint32 `db:"max_stages_sp"` - MaxPointsSP uint32 `db:"max_points_sp"` + Name string `db:"name"` + Score uint32 `db:"score"` } func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { @@ -121,170 +115,64 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) { guild = nil } + if pkt.Leaderboard == 2 || pkt.Leaderboard == 3 || pkt.Leaderboard == 6 || pkt.Leaderboard == 7 { + if guild == nil { + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 11)) + return + } + } + var score RengokuScore + var selfExist bool i := uint32(1) bf := byteframe.NewByteFrame() scoreData := byteframe.NewByteFrame() + + var rows *sqlx.Rows switch pkt.Leaderboard { - case 0: // Max stage overall MP - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery)) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxStagesMP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxStagesMP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - case 1: // Max RdP overall MP - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery)) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxPointsMP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxPointsMP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - case 2: // Max stage guild MP - if guild != nil { - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxStagesMP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxStagesMP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - } else { - bf.WriteBytes(make([]byte, 11)) - } - case 3: // Max RdP guild MP - if guild != nil { - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxPointsMP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxPointsMP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - } else { - bf.WriteBytes(make([]byte, 11)) - } - case 4: // Max stage overall SP - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery)) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxStagesSP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxStagesSP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - case 5: // Max RdP overall SP - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery)) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxPointsSP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxPointsSP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - case 6: // Max stage guild SP - if guild != nil { - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxStagesSP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxStagesSP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - } else { - bf.WriteBytes(make([]byte, 11)) - } - case 7: // Max RdP guild SP - if guild != nil { - rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID) - for rows.Next() { - rows.StructScan(&score) - if score.Name == s.Name { - bf.WriteUint32(i) - bf.WriteUint32(score.MaxPointsSP) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } - scoreData.WriteUint32(i) - scoreData.WriteUint32(score.MaxPointsSP) - ps.Uint8(scoreData, score.Name, true) - ps.Uint8(scoreData, "", false) - i++ - } - } else { - bf.WriteBytes(make([]byte, 11)) - } + case 0: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s ORDER BY max_stages_mp DESC", rengokuScoreQuery)) + case 1: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s ORDER BY max_points_mp DESC", rengokuScoreQuery)) + case 2: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID) + case 3: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_mp AS score %s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID) + case 4: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s ORDER BY max_stages_sp DESC", rengokuScoreQuery)) + case 5: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s ORDER BY max_points_sp DESC", rengokuScoreQuery)) + case 6: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_sp AS score %s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID) + case 7: + rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID) } - if i == 1 { - bf.WriteUint32(1) - bf.WriteUint32(0) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - bf.WriteUint8(1) - bf.WriteUint32(1) - bf.WriteUint32(0) - ps.Uint8(bf, s.Name, true) - ps.Uint8(bf, "", false) - } else { - bf.WriteUint8(uint8(i) - 1) - bf.WriteBytes(scoreData.Data()) + + for rows.Next() { + rows.StructScan(&score) + if score.Name == s.Name { + bf.WriteUint32(i) + bf.WriteUint32(score.Score) + ps.Uint8(bf, s.Name, true) + ps.Uint8(bf, "", false) + selfExist = true + } + if i > 100 { + i++ + continue + } + scoreData.WriteUint32(i) + scoreData.WriteUint32(score.Score) + ps.Uint8(scoreData, score.Name, true) + ps.Uint8(scoreData, "", false) + i++ } + + if !selfExist { + bf.WriteBytes(make([]byte, 10)) + } + bf.WriteUint8(uint8(i) - 1) + bf.WriteBytes(scoreData.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 906480be7..90ecdec99 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -19,6 +19,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } else { stage := NewStage(pkt.StageID) + stage.host = s stage.maxPlayers = uint16(pkt.PlayerCount) s.server.stages[stage.id] = stage doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) @@ -42,6 +43,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { stage = s.server.stages[stageID] s.server.Unlock() stage.Lock() + stage.host = s stage.clients[s] = s.charID stage.Unlock() } diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 90430f4da..39a96f40f 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -6,17 +6,16 @@ import ( "fmt" "io" "net" + "strings" "sync" "time" "erupe-ce/common/byteframe" "erupe-ce/common/stringstack" - "erupe-ce/common/stringsupport" "erupe-ce/network" "erupe-ce/network/clientctx" "erupe-ce/network/mhfpacket" "go.uber.org/zap" - "golang.org/x/text/encoding/japanese" ) type packet struct { @@ -43,7 +42,7 @@ type Session struct { charID uint32 logKey []byte sessionStart int64 - rights uint32 + courses []mhfpacket.Course token string kqf []byte kqfOverride bool @@ -68,16 +67,12 @@ type Session struct { // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{ - logger: server.logger.Named(conn.RemoteAddr().String()), - server: server, - rawConn: conn, - cryptConn: network.NewCryptConn(conn), - sendPackets: make(chan packet, 20), - clientContext: &clientctx.ClientContext{ - StrConv: &stringsupport.StringConverter{ - Encoding: japanese.ShiftJIS, - }, - }, + logger: server.logger.Named(conn.RemoteAddr().String()), + server: server, + rawConn: conn, + cryptConn: network.NewCryptConn(conn), + sendPackets: make(chan packet, 20), + clientContext: &clientctx.ClientContext{}, // Unused sessionStart: Time_Current_Adjusted().Unix(), stageMoveStack: stringstack.New(), } @@ -268,3 +263,14 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data)) } } + +func (s *Session) FindCourse(name string) mhfpacket.Course { + for _, course := range s.courses { + for _, alias := range course.Aliases { + if strings.ToLower(name) == strings.ToLower(alias) { + return course + } + } + } + return mhfpacket.Course{} +} diff --git a/server/channelserver/sys_stage.go b/server/channelserver/sys_stage.go index 827b2d6be..4cd96a7f8 100644 --- a/server/channelserver/sys_stage.go +++ b/server/channelserver/sys_stage.go @@ -46,6 +46,7 @@ type Stage struct { // other clients expect the server to echo them back in the exact same format. rawBinaryData map[stageBinaryKey][]byte + host *Session maxPlayers uint16 password string createdAt string diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go index 56f300c27..706aac8b3 100644 --- a/server/entranceserver/entrance_server.go +++ b/server/entranceserver/entrance_server.go @@ -90,6 +90,7 @@ func (s *Server) acceptClients() { } func (s *Server) handleEntranceServerConnection(conn net.Conn) { + defer conn.Close() // Client initalizes the connection with a one-time buffer of 8 NULL bytes. nullInit := make([]byte, 8) n, err := io.ReadFull(conn, nullInit) @@ -118,5 +119,4 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) { cc.SendPacket(data) // Close because we only need to send the response once. // Any further requests from the client will come from a new connection. - conn.Close() } diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go index 5e99d4c85..213a74a14 100644 --- a/server/signserver/dbutils.go +++ b/server/signserver/dbutils.go @@ -148,7 +148,7 @@ func (s *Server) getFriendsForCharacters(chars []character) []members { if err != nil { continue } - for i, _ := range charFriends { + for i := range charFriends { charFriends[i].CID = char.ID } friends = append(friends, charFriends...) diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index cbad6405c..3966af8d6 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -42,13 +42,23 @@ func (s *Session) makeSignInResp(uid int) []byte { bf := byteframe.NewByteFrame() - bf.WriteUint8(1) // resp_code - bf.WriteUint8(0) // file/patch server count + bf.WriteUint8(1) // resp_code + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" { + bf.WriteUint8(2) + } else { + bf.WriteUint8(0) + } bf.WriteUint8(1) // entrance server count bf.WriteUint8(uint8(len(chars))) // character count bf.WriteUint32(0xFFFFFFFF) // login_token_number bf.WriteBytes([]byte(token)) // login_token bf.WriteUint32(uint32(time.Now().Unix())) // current time + if s.server.erupeConfig.DevMode { + if s.server.erupeConfig.DevModeOptions.PatchServerManifest != "" && s.server.erupeConfig.DevModeOptions.PatchServerFile != "" { + ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerManifest, false) + ps.Uint8(bf, s.server.erupeConfig.DevModeOptions.PatchServerFile, false) + } + } ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false) lastPlayed := uint32(0) diff --git a/server/signserver/session.go b/server/signserver/session.go index aef6508b1..6b2e38b8c 100644 --- a/server/signserver/session.go +++ b/server/signserver/session.go @@ -16,32 +16,19 @@ import ( type Session struct { sync.Mutex logger *zap.Logger - sid int server *Server - rawConn *net.Conn + rawConn net.Conn cryptConn *network.CryptConn } -func (s *Session) fail() { - s.server.Lock() - delete(s.server.sessions, s.sid) - s.server.Unlock() - -} - func (s *Session) work() { - for { - pkt, err := s.cryptConn.ReadPacket() - if err != nil { - s.fail() - return - } - - err = s.handlePacket(pkt) - if err != nil { - s.fail() - return - } + pkt, err := s.cryptConn.ReadPacket() + if err != nil { + return + } + err = s.handlePacket(pkt) + if err != nil { + return } } @@ -61,6 +48,7 @@ func (s *Session) handlePacket(pkt []byte) error { case "DELETE:100": loginTokenString := string(bf.ReadNullTerminatedBytes()) characterID := int(bf.ReadUint32()) + _ = int(bf.ReadUint32()) // login_token_number s.server.deleteCharacter(characterID, loginTokenString) sugar.Infof("Deleted character ID: %v\n", characterID) err := s.cryptConn.SendPacket([]byte{0x01}) // DEL_SUCCESS @@ -78,13 +66,13 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { reqUsername := string(bf.ReadNullTerminatedBytes()) reqPassword := string(bf.ReadNullTerminatedBytes()) - reqUnk := 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("reqUnk", reqUnk), + zap.String("reqSkey", reqSkey), ) newCharaReq := false @@ -105,12 +93,15 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { s.logger.Info("Account not found", zap.String("reqUsername", reqUsername)) serverRespBytes = makeSignInFailureResp(SIGN_EAUTH) - // HACK(Andoryuuta): Create a new account if it doesn't exit. - s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword)) - err = s.server.registerDBAccount(reqUsername, reqPassword) - if err != nil { - s.logger.Info("Error on creating new account", zap.Error(err)) - serverRespBytes = makeSignInFailureResp(SIGN_EABORT) + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.AutoCreateAccount { + s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword)) + err = s.server.registerDBAccount(reqUsername, reqPassword) + if err != nil { + s.logger.Info("Error on creating new account", zap.Error(err)) + serverRespBytes = makeSignInFailureResp(SIGN_EABORT) + break + } + } else { break } diff --git a/server/signserver/sign_server.go b/server/signserver/sign_server.go index 02bafb10d..6d567e126 100644 --- a/server/signserver/sign_server.go +++ b/server/signserver/sign_server.go @@ -24,7 +24,6 @@ type Server struct { sync.Mutex logger *zap.Logger erupeConfig *config.Config - sid int sessions map[int]*Session db *sqlx.DB listener net.Listener @@ -36,8 +35,6 @@ func NewServer(config *Config) *Server { s := &Server{ logger: config.Logger, erupeConfig: config.ErupeConfig, - sid: 0, - sessions: make(map[int]*Session), db: config.DB, } return s @@ -84,20 +81,19 @@ func (s *Server) acceptClients() { } } - go s.handleConnection(s.sid, conn) - s.sid++ + go s.handleConnection(conn) } } -func (s *Server) handleConnection(sid int, conn net.Conn) { +func (s *Server) handleConnection(conn net.Conn) { s.logger.Info("Got connection to sign server", 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 { - fmt.Println(err) - conn.Close() + s.logger.Error("Error initialising sign server connection", zap.Error(err)) return } @@ -105,15 +101,10 @@ func (s *Server) handleConnection(sid int, conn net.Conn) { session := &Session{ logger: s.logger, server: s, - rawConn: &conn, + rawConn: conn, cryptConn: network.NewCryptConn(conn), } - // Add the session to the server's sessions map. - s.Lock() - s.sessions[sid] = session - s.Unlock() - // Do the session's work. session.work() }