Config / DB now its own package

This commit is contained in:
stratic-dev
2024-10-12 22:37:42 +01:00
parent 44692e986e
commit 5f975c97b5
76 changed files with 3249 additions and 2359 deletions

View File

@@ -1,4 +1,4 @@
package _config
package config
import (
"fmt"
@@ -6,6 +6,7 @@ import (
"net"
"os"
"strings"
"sync"
"time"
"github.com/spf13/viper"
@@ -299,15 +300,10 @@ type EntranceChannelInfo struct {
CurrentPlayers uint16
}
var ErupeConfig *Config
func init() {
var err error
ErupeConfig, err = LoadConfig()
if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
}
}
var (
once sync.Once
ErupeConfig *Config
)
// getOutboundIP4 gets the preferred outbound ip4 of this machine
// From https://stackoverflow.com/a/37382208
@@ -368,7 +364,16 @@ func LoadConfig() (*Config, error) {
return c, nil
}
func GetConfig() *Config {
once.Do(func() {
var err error
ErupeConfig, err = LoadConfig()
if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
}
})
return ErupeConfig
}
func preventClose(text string) {
if ErupeConfig.DisableSoftCrash {
os.Exit(0)

180
main.go
View File

@@ -1,7 +1,7 @@
package main
import (
_config "erupe-ce/config"
"erupe-ce/config"
"fmt"
"net"
"os"
@@ -13,27 +13,19 @@ import (
"erupe-ce/server/api"
"erupe-ce/server/channelserver"
"erupe-ce/server/discordbot"
"erupe-ce/server/entranceserver"
"erupe-ce/server/entrance"
"erupe-ce/server/sign"
"erupe-ce/utils/db"
"erupe-ce/utils/logger"
"erupe-ce/server/signserver"
"erupe-ce/utils/gametime"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
"go.uber.org/zap"
)
var mainLogger logger.Logger
// Temporary DB auto clean on startup for quick development & testing.
func cleanDB(db *sqlx.DB) {
_ = db.MustExec("DELETE FROM guild_characters")
_ = db.MustExec("DELETE FROM guilds")
_ = db.MustExec("DELETE FROM characters")
_ = db.MustExec("DELETE FROM users")
}
var Commit = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
@@ -54,96 +46,24 @@ func initLogger() {
mainLogger = logger.Get().Named("main")
}
func main() {
var err error
config := _config.ErupeConfig
config := config.GetConfig()
initLogger()
mainLogger.Info(fmt.Sprintf("Starting Erupe (9.3b-%s)", Commit()))
mainLogger.Info(fmt.Sprintf("Client Mode: %s (%d)", config.ClientMode, config.ClientID))
if config.Database.Password == "" {
preventClose("Database password is blank")
}
checkAndExitIf(config.Database.Password == "", "Database password is blank")
if net.ParseIP(config.Host) == nil {
ips, _ := net.LookupIP(config.Host)
for _, ip := range ips {
if ip != nil {
config.Host = ip.String()
break
}
}
if net.ParseIP(config.Host) == nil {
preventClose("Invalid host address")
}
}
resolveHostIP()
// Discord bot
var discordBot *discordbot.DiscordBot = nil
discordBot := initializeDiscordBot()
if config.Discord.Enabled {
bot, err := discordbot.NewDiscordBot(discordbot.Options{
Config: _config.ErupeConfig,
})
DiscordFailMsg := "Discord: Failed to start, %s"
if err != nil {
preventClose(fmt.Sprintf(DiscordFailMsg, err.Error()))
}
// Discord bot
err = bot.Start()
if err != nil {
preventClose(fmt.Sprintf(DiscordFailMsg, err.Error()))
}
discordBot = bot
_, err = discordBot.Session.ApplicationCommandBulkOverwrite(discordBot.Session.State.User.ID, "", discordbot.Commands)
if err != nil {
preventClose(fmt.Sprintf(DiscordFailMsg, err.Error()))
}
mainLogger.Info("Discord: Started successfully")
} else {
mainLogger.Info("Discord: Disabled")
}
// Create the postgres DB pool.
connectString := fmt.Sprintf(
"host='%s' port='%d' user='%s' password='%s' dbname='%s' sslmode=disable",
config.Database.Host,
config.Database.Port,
config.Database.User,
config.Database.Password,
config.Database.Database,
)
db, err := sqlx.Open("postgres", connectString)
database, err := db.InitDB(config)
if err != nil {
preventClose(fmt.Sprintf("Database: Failed to open, %s", err.Error()))
}
// Test the DB connection.
err = db.Ping()
if err != nil {
preventClose(fmt.Sprintf("Database: Failed to ping, %s", err.Error()))
}
mainLogger.Info("Database: Started successfully")
// Clear stale data
if config.DebugOptions.ProxyPort == 0 {
_ = db.MustExec("DELETE FROM sign_sessions")
}
_ = db.MustExec("DELETE FROM servers")
_ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`)
// Clean the DB if the option is on.
if config.DebugOptions.CleanDB {
mainLogger.Info("Database: Started clearing...")
cleanDB(db)
mainLogger.Info("Database: Finished clearing")
mainLogger.Fatal(fmt.Sprintf("Database initialization failed: %s", err))
}
mainLogger.Info(fmt.Sprintf("Server Time: %s", gametime.TimeAdjusted().String()))
@@ -151,16 +71,10 @@ func main() {
// Now start our server(s).
// Entrance server.
entranceLogger := logger.Get().Named("entrance")
var entranceServer *entranceserver.Server
var entranceServer *entrance.EntranceServer
if config.Entrance.Enabled {
entranceServer = entranceserver.NewServer(
&entranceserver.Config{
Logger: entranceLogger,
ErupeConfig: _config.ErupeConfig,
DB: db,
})
entranceServer = entrance.NewServer()
err = entranceServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Entrance: Failed to start, %s", err.Error()))
@@ -171,13 +85,9 @@ func main() {
}
// Sign server.
var signServer *signserver.Server
var signServer *sign.SignServer
if config.Sign.Enabled {
signServer = signserver.NewServer(
&signserver.Config{
ErupeConfig: _config.ErupeConfig,
DB: db,
})
signServer = sign.NewServer()
err = signServer.Start()
if err != nil {
preventClose(fmt.Sprintf("Sign: Failed to start, %s", err.Error()))
@@ -188,14 +98,10 @@ func main() {
}
// Api server
var ApiServer *api.APIServer
var apiServer *api.APIServer
if config.API.Enabled {
ApiServer = api.NewAPIServer(
&api.Config{
ErupeConfig: _config.ErupeConfig,
DB: db,
})
err = ApiServer.Start()
apiServer = api.NewAPIServer()
err = apiServer.Start()
if err != nil {
preventClose(fmt.Sprintf("API: Failed to start, %s", err.Error()))
}
@@ -203,8 +109,7 @@ func main() {
} else {
mainLogger.Info("API: Disabled")
}
var channelServers []*channelserver.Server
var channelServers []*channelserver.ChannelServer
if config.Channel.Enabled {
channelQuery := ""
si := 0
@@ -214,10 +119,8 @@ func main() {
for i, ce := range ee.Channels {
sid := (4096 + si*256) + (16 + ci)
c := *channelserver.NewServer(&channelserver.Config{
ID: uint16(sid),
ErupeConfig: _config.ErupeConfig,
DB: db,
DiscordBot: discordBot,
ID: uint16(sid),
DiscordBot: discordBot,
})
if ee.IP == "" {
c.IP = config.Host
@@ -242,7 +145,7 @@ func main() {
}
// Register all servers in DB
_ = db.MustExec(channelQuery)
_ = database.MustExec(channelQuery)
for _, c := range channelServers {
c.Channels = channelServers
@@ -278,7 +181,7 @@ func main() {
}
if config.API.Enabled {
ApiServer.Shutdown()
apiServer.Shutdown()
}
if config.Entrance.Enabled {
@@ -295,7 +198,7 @@ func wait() {
}
func preventClose(text string) {
if _config.ErupeConfig.DisableSoftCrash {
if config.GetConfig().DisableSoftCrash {
os.Exit(0)
}
mainLogger.Error(fmt.Sprintf(("\nFailed to start Erupe:\n" + text)))
@@ -303,3 +206,38 @@ func preventClose(text string) {
mainLogger.Error(fmt.Sprintf(("\nPress Enter/Return to exit...")))
os.Exit(0)
}
func checkAndExitIf(condition bool, message string) {
if condition {
preventClose(message)
}
}
func resolveHostIP() {
if net.ParseIP(config.GetConfig().Host) == nil {
ips, err := net.LookupIP(config.GetConfig().Host)
if err == nil && len(ips) > 0 {
config.GetConfig().Host = ips[0].String()
}
checkAndExitIf(net.ParseIP(config.GetConfig().Host) == nil, "Invalid host address")
}
}
func initializeDiscordBot() *discordbot.DiscordBot {
if !config.GetConfig().Discord.Enabled {
mainLogger.Info("Discord: Disabled")
return nil
}
bot, err := discordbot.NewDiscordBot()
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to start, %s", err))
err = bot.Start()
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to start, %s", err))
_, err = bot.Session.ApplicationCommandBulkOverwrite(bot.Session.State.User.ID, "", discordbot.Commands)
checkAndExitIf(err != nil, fmt.Sprintf("Discord: Failed to register commands, %s", err))
mainLogger.Info("Discord: Started successfully")
return bot
}

View File

@@ -3,7 +3,7 @@ package network
import (
"encoding/hex"
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/crypto"
"fmt"
"io"
@@ -51,7 +51,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
var encryptedPacketBody []byte
// Don't know when support for this was added, works in Forward.4, doesn't work in Season 6.0
if _config.ErupeConfig.ClientID < _config.F1 {
if config.GetConfig().ClientID < config.F1 {
encryptedPacketBody = make([]byte, cph.DataSize)
} else {
encryptedPacketBody = make([]byte, uint32(cph.DataSize)+(uint32(cph.Pf0-0x03)*0x1000))

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -30,7 +30,7 @@ func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame) error {
m.ItemType = bf.ReadUint16()
m.ItemID = bf.ReadUint16()
m.Quant = bf.ReadUint16()
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
m.PointCost = bf.ReadUint32()
} else {
m.PointCost = uint32(bf.ReadUint16())

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
)
@@ -26,10 +26,10 @@ func (m *MsgMhfApplyDistItem) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.DistributionType = bf.ReadUint8()
m.DistributionID = bf.ReadUint32()
if _config.ErupeConfig.ClientID >= _config.G8 {
if config.GetConfig().ClientID >= config.G8 {
m.Unk2 = bf.ReadUint32()
}
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
m.Unk3 = bf.ReadUint32()
}
return nil

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
)
@@ -27,7 +27,7 @@ func (m *MsgMhfEnumerateDistItem) Parse(bf *byteframe.ByteFrame) error {
m.DistType = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16() // Maximum? Hardcoded to 256
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
m.Unk3 = bf.ReadBytes(uint(bf.ReadUint8()))
}
return nil

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -29,7 +29,7 @@ func (m *MsgMhfEnumerateQuest) Parse(bf *byteframe.ByteFrame) error {
m.Unk0 = bf.ReadUint8()
m.World = bf.ReadUint8()
m.Counter = bf.ReadUint16()
if _config.ErupeConfig.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
m.Offset = uint16(bf.ReadUint8())
} else {
m.Offset = bf.ReadUint16()

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -31,7 +31,7 @@ func (m *MsgMhfEnumerateShop) Parse(bf *byteframe.ByteFrame) error {
m.ShopID = bf.ReadUint32()
m.Limit = bf.ReadUint16()
m.Unk3 = bf.ReadUint8()
if _config.ErupeConfig.ClientID >= _config.G2 {
if config.GetConfig().ClientID >= config.G2 {
m.Unk4 = bf.ReadUint8()
m.Unk5 = bf.ReadUint32()
}

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -29,7 +29,7 @@ func (m *MsgMhfSavedata) Parse(bf *byteframe.ByteFrame) error {
m.AllocMemSize = bf.ReadUint32()
m.SaveType = bf.ReadUint8()
m.Unk1 = bf.ReadUint32()
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
m.DataSize = bf.ReadUint32()
}
if m.DataSize == 0 { // seems to be used when DataSize = 0 rather than on savetype?

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -31,12 +31,12 @@ func (m *MsgMhfStampcardStamp) Opcode() network.PacketID {
func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.HR = bf.ReadUint16()
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
m.GR = bf.ReadUint16()
}
m.Stamps = bf.ReadUint16()
bf.ReadUint16() // Zeroed
if _config.ErupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
m.Reward1 = uint16(bf.ReadUint32())
m.Reward2 = uint16(bf.ReadUint32())
m.Item1 = uint16(bf.ReadUint32())

View File

@@ -3,7 +3,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
)
@@ -22,11 +22,11 @@ func (m *MsgMhfUpdateMyhouseInfo) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfUpdateMyhouseInfo) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
m.Data = bf.ReadBytes(362)
} else if _config.ErupeConfig.ClientID >= _config.GG {
} else if config.GetConfig().ClientID >= config.GG {
m.Data = bf.ReadBytes(338)
} else if _config.ErupeConfig.ClientID >= _config.F5 {
} else if config.GetConfig().ClientID >= config.F5 {
// G1 is a guess
m.Data = bf.ReadBytes(314)
} else {

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
)
@@ -24,7 +24,7 @@ func (m *MsgSysCreateAcquireSemaphore) Opcode() network.PacketID {
func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
if _config.ErupeConfig.ClientID >= _config.S7 { // Assuming this was added with Ravi?
if config.GetConfig().ClientID >= config.S7 { // Assuming this was added with Ravi?
m.PlayerCount = bf.ReadUint8()
}
bf.ReadUint8() // SemaphoreID length

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -25,7 +25,7 @@ func (m *MsgSysCreateSemaphore) Opcode() network.PacketID {
func (m *MsgSysCreateSemaphore) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
if _config.ErupeConfig.ClientID >= _config.S7 { // Assuming this was added with Ravi?
if config.GetConfig().ClientID >= config.S7 { // Assuming this was added with Ravi?
m.PlayerCount = bf.ReadUint8()
}
bf.ReadUint8() // SemaphoreID length

View File

@@ -2,7 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/byteframe"
@@ -48,7 +48,7 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame) error {
e.Unk1 = bf.ReadInt32()
e.Unk2 = bf.ReadInt32()
e.Unk3 = bf.ReadInt32()
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
for j := 0; j < 4; j++ {
e.Unk4 = append(e.Unk4, bf.ReadInt32())
}

View File

@@ -2,7 +2,7 @@ package api
import (
"context"
_config "erupe-ce/config"
"erupe-ce/config"
"fmt"
"net/http"
"os"
@@ -13,33 +13,24 @@ import (
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
type Config struct {
DB *sqlx.DB
ErupeConfig *_config.Config
}
// APIServer is Erupes Standard API interface
type APIServer struct {
sync.Mutex
logger logger.Logger
erupeConfig *_config.Config
db *sqlx.DB
erupeConfig *config.Config
httpServer *http.Server
isShuttingDown bool
}
// NewAPIServer creates a new Server type.
func NewAPIServer(config *Config) *APIServer {
func NewAPIServer() *APIServer {
s := &APIServer{
logger: logger.Get().Named("API"),
erupeConfig: config.ErupeConfig,
db: config.DB,
httpServer: &http.Server{},
logger: logger.Get().Named("API"),
httpServer: &http.Server{},
}
return s
}
@@ -58,7 +49,7 @@ func (s *APIServer) Start() error {
r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet)
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.API.Port)
s.httpServer.Addr = fmt.Sprintf(":%d", config.GetConfig().API.Port)
serveError := make(chan error, 1)
go func() {

View File

@@ -3,6 +3,7 @@ package api
import (
"context"
"database/sql"
"erupe-ce/utils/db"
"erupe-ce/utils/token"
"fmt"
"time"
@@ -21,7 +22,11 @@ func (s *APIServer) createNewUser(ctx context.Context, username string, password
id uint32
rights uint32
)
err = s.db.QueryRowContext(
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRowContext(
ctx, `
INSERT INTO users (username, password, return_expires)
VALUES ($1, $2, $3)
@@ -35,7 +40,11 @@ func (s *APIServer) createNewUser(ctx context.Context, username string, password
func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
loginToken := token.Generate(16)
var tid uint32
err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
if err != nil {
return 0, "", err
}
@@ -44,7 +53,11 @@ func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, s
func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32, error) {
var userID uint32
err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID)
if err == sql.ErrNoRows {
return 0, fmt.Errorf("invalid login token")
} else if err != nil {
@@ -55,17 +68,21 @@ func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32,
func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Character, error) {
var character Character
err := s.db.GetContext(ctx, &character,
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.GetContext(ctx, &character,
"SELECT id, name, is_female, weapon_type, hr, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1",
userID,
)
if err == sql.ErrNoRows {
var count int
s.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM characters WHERE user_id = $1", userID).Scan(&count)
database.QueryRowContext(ctx, "SELECT COUNT(*) FROM characters WHERE user_id = $1", userID).Scan(&count)
if count >= 16 {
return character, fmt.Errorf("cannot have more than 16 characters")
}
err = s.db.GetContext(ctx, &character, `
err = database.GetContext(ctx, &character, `
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hr, gr, weapon_type, last_login
@@ -80,21 +97,29 @@ func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Charact
func (s *APIServer) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error {
var isNew bool
err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew)
if err != nil {
return err
}
if isNew {
_, err = s.db.Exec("DELETE FROM characters WHERE id = $1", charID)
_, err = database.Exec("DELETE FROM characters WHERE id = $1", charID)
} else {
_, err = s.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", charID)
_, err = database.Exec("UPDATE characters SET deleted = true WHERE id = $1", charID)
}
return err
}
func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) {
var characters []Character
err := s.db.SelectContext(
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.SelectContext(
ctx, &characters, `
SELECT id, name, is_female, weapon_type, hr, gr, last_login
FROM characters
@@ -109,25 +134,33 @@ func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Cha
func (s *APIServer) getReturnExpiry(uid uint32) time.Time {
var returnExpiry, lastLogin time.Time
s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
returnExpiry = time.Now().Add(time.Hour * 24 * 30)
s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
database.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
} else {
err := s.db.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid)
err := database.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid)
if err != nil {
returnExpiry = time.Now()
s.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
database.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
}
}
s.db.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid)
database.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid)
return returnExpiry
}
func (s *APIServer) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) {
row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := database.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid)
result := make(map[string]interface{})
err := row.MapScan(result)
err = row.MapScan(result)
if err != nil {
return nil, err
}

View File

@@ -5,7 +5,8 @@ import (
"encoding/json"
"encoding/xml"
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"fmt"
"image"
@@ -30,9 +31,9 @@ const (
)
type LauncherResponse struct {
Banners []_config.APISignBanner `json:"banners"`
Messages []_config.APISignMessage `json:"messages"`
Links []_config.APISignLink `json:"links"`
Banners []config.APISignBanner `json:"banners"`
Messages []config.APISignMessage `json:"messages"`
Links []config.APISignLink `json:"links"`
}
type User struct {
@@ -137,7 +138,11 @@ func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) {
userRights uint32
password string
)
err := s.db.QueryRow("SELECT id, password, rights FROM users WHERE username = $1", reqData.Username).Scan(&userID, &password, &userRights)
database, err := db.GetDB()
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT id, password, rights FROM users WHERE username = $1", reqData.Username).Scan(&userID, &password, &userRights)
if err == sql.ErrNoRows {
w.WriteHeader(400)
w.Write([]byte("username-error"))

View File

@@ -1,23 +1,24 @@
package channelserver
import (
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
)
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
func DoAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(s.server.erupeConfig.EarthID))
bf.WriteUint32(uint32(config.GetConfig().EarthID))
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(uint32(len(data)))
for i := range data {
bf.WriteBytes(data[i].Data())
}
doAckBufSucceed(s, ackHandle, bf.Data())
DoAckBufSucceed(s, ackHandle, bf.Data())
}
func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
func DoAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
s.QueueSendMHF(&mhfpacket.MsgSysAck{
AckHandle: ackHandle,
IsBufferResponse: true,
@@ -26,7 +27,7 @@ func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
})
}
func doAckBufFail(s *Session, ackHandle uint32, data []byte) {
func DoAckBufFail(s *Session, ackHandle uint32, data []byte) {
s.QueueSendMHF(&mhfpacket.MsgSysAck{
AckHandle: ackHandle,
IsBufferResponse: true,
@@ -35,7 +36,7 @@ func doAckBufFail(s *Session, ackHandle uint32, data []byte) {
})
}
func doAckSimpleSucceed(s *Session, ackHandle uint32, data []byte) {
func DoAckSimpleSucceed(s *Session, ackHandle uint32, data []byte) {
s.QueueSendMHF(&mhfpacket.MsgSysAck{
AckHandle: ackHandle,
IsBufferResponse: false,
@@ -44,7 +45,7 @@ func doAckSimpleSucceed(s *Session, ackHandle uint32, data []byte) {
})
}
func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
func DoAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
s.QueueSendMHF(&mhfpacket.MsgSysAck{
AckHandle: ackHandle,
IsBufferResponse: false,

View File

@@ -2,7 +2,8 @@ package channelserver
import (
"encoding/binary"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/mhfcourse"
"erupe-ce/utils/mhfitem"
@@ -28,12 +29,16 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
enumBf := byteframe.NewByteFrame()
enumBf.WriteUint32(0) // Entry count (count for quests, rankings, events, etc.)
doAckBufSucceed(s, ackHandle, enumBf.Data())
DoAckBufSucceed(s, ackHandle, enumBf.Data())
}
func updateRights(s *Session) {
rightsInt := uint32(2)
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)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.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, rightsInt = mhfcourse.GetCourseStruct(rightsInt)
update := &mhfpacket.MsgSysUpdateRight{
ClientRespAckHandle: 0,
@@ -63,7 +68,7 @@ func handleMsgSysAck(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysTerminalLog)
for i := range pkt.Entries {
s.server.logger.Info("SysTerminalLog",
s.Server.logger.Info("SysTerminalLog",
zap.Uint8("Type1", pkt.Entries[i].Type1),
zap.Uint8("Type2", pkt.Entries[i].Type2),
zap.Int16("Unk0", pkt.Entries[i].Unk0),
@@ -75,55 +80,58 @@ func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(pkt.LogID + 1) // LogID to use for requests after this.
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLogin)
if !s.server.erupeConfig.DebugOptions.DisableTokenCheck {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if !config.GetConfig().DebugOptions.DisableTokenCheck {
var token string
err := s.server.db.QueryRow("SELECT token FROM sign_sessions ss INNER JOIN public.users u on ss.user_id = u.id WHERE token=$1 AND ss.id=$2 AND u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.LoginTokenString, pkt.LoginTokenNumber, pkt.CharID0).Scan(&token)
err := database.QueryRow("SELECT token FROM sign_sessions ss INNER JOIN public.users u on ss.user_id = u.id WHERE token=$1 AND ss.id=$2 AND u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.LoginTokenString, pkt.LoginTokenNumber, pkt.CharID0).Scan(&token)
if err != nil {
s.rawConn.Close()
s.logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0))
s.Logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0))
return
}
}
s.Lock()
s.charID = pkt.CharID0
s.CharID = pkt.CharID0
s.token = pkt.LoginTokenString
s.Unlock()
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(gametime.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)
_, err = database.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.Server.sessions), s.Server.ID)
if err != nil {
panic(err)
}
_, err = s.server.db.Exec("UPDATE sign_sessions SET server_id=$1, char_id=$2 WHERE token=$3", s.server.ID, s.charID, s.token)
_, err = database.Exec("UPDATE sign_sessions SET server_id=$1, char_id=$2 WHERE token=$3", s.Server.ID, s.CharID, s.token)
if err != nil {
panic(err)
}
_, err = s.server.db.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", gametime.TimeAdjusted().Unix(), s.charID)
_, err = database.Exec("UPDATE characters SET last_login=$1 WHERE id=$2", gametime.TimeAdjusted().Unix(), s.CharID)
if err != nil {
panic(err)
}
_, err = s.server.db.Exec("UPDATE users u SET last_character=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.charID)
_, err = database.Exec("UPDATE users u SET last_character=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.CharID)
if err != nil {
panic(err)
}
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
updateRights(s)
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{CharID: s.charID}, s)
s.Server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{CharID: s.CharID}, s)
}
func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
@@ -131,45 +139,49 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
}
func logoutPlayer(s *Session) {
s.server.Lock()
if _, exists := s.server.sessions[s.rawConn]; exists {
delete(s.server.sessions, s.rawConn)
s.Server.Lock()
if _, exists := s.Server.sessions[s.rawConn]; exists {
delete(s.Server.sessions, s.rawConn)
}
s.rawConn.Close()
delete(s.server.objectIDs, s)
s.server.Unlock()
delete(s.Server.objectIDs, s)
s.Server.Unlock()
for _, stage := range s.server.stages {
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 {
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" {
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 {
if session.CharID == s.CharID {
delete(stage.clients, session)
}
}
}
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = database.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
if err != nil {
panic(err)
}
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
_, err = database.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.Server.sessions), s.Server.ID)
if err != nil {
panic(err)
}
var timePlayed int
var sessionTime int
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
_ = database.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.CharID).Scan(&timePlayed)
sessionTime = int(gametime.TimeAdjusted().Unix()) - int(s.sessionStart)
timePlayed += sessionTime
@@ -177,43 +189,43 @@ func logoutPlayer(s *Session) {
if mhfcourse.CourseExists(30, s.courses) {
rpGained = timePlayed / 900
timePlayed = timePlayed % 900
s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID)
database.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.CharID)
} else {
rpGained = timePlayed / 1800
timePlayed = timePlayed % 1800
}
s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
database.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.CharID)
s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, s.charID)
database.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, s.CharID)
if s.stage == nil {
return
}
s.server.BroadcastMHF(&mhfpacket.MsgSysDeleteUser{
CharID: s.charID,
s.Server.BroadcastMHF(&mhfpacket.MsgSysDeleteUser{
CharID: s.CharID,
}, s)
s.server.Lock()
for _, stage := range s.server.stages {
if _, exists := stage.reservedClientSlots[s.charID]; exists {
delete(stage.reservedClientSlots, s.charID)
s.Server.Lock()
for _, stage := range s.Server.stages {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
delete(stage.reservedClientSlots, s.CharID)
}
}
s.server.Unlock()
s.Server.Unlock()
removeSessionFromSemaphore(s)
removeSessionFromStage(s)
saveData, err := GetCharacterSaveData(s, s.charID)
saveData, err := GetCharacterSaveData(s, s.CharID)
if err != nil || saveData == nil {
s.logger.Error("Failed to get savedata")
s.Logger.Error("Failed to get savedata")
return
}
saveData.RP += uint16(rpGained)
if saveData.RP >= s.server.erupeConfig.GameplayOptions.MaximumRP {
saveData.RP = s.server.erupeConfig.GameplayOptions.MaximumRP
if saveData.RP >= config.GetConfig().GameplayOptions.MaximumRP {
saveData.RP = config.GetConfig().GameplayOptions.MaximumRP
}
saveData.Save(s)
}
@@ -222,7 +234,7 @@ func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysPing(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysPing)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) {
@@ -253,25 +265,29 @@ func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {
// Issue it.
resp := byteframe.NewByteFrame()
resp.WriteBytes(logKey)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysRecordLog)
if _config.ErupeConfig.ClientID == _config.ZZ {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if config.GetConfig().ClientID == config.ZZ {
bf := byteframe.NewByteFrameFromBytes(pkt.Data)
bf.Seek(32, 0)
var val uint8
for i := 0; i < 176; i++ {
val = bf.ReadUint8()
if val > 0 && mhfmon.Monsters[i].Large {
s.server.db.Exec(`INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, $2, $3, $4)`, s.charID, i, val, gametime.TimeAdjusted())
database.Exec(`INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, $2, $3, $4)`, s.CharID, i, val, gametime.TimeAdjusted())
}
}
}
// remove a client returning to town from reserved slots to make sure the stage is hidden from board
delete(s.stage.reservedClientSlots, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
delete(s.stage.reservedClientSlots, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgSysEcho(s *Session, p mhfpacket.MHFPacket) {}
@@ -279,7 +295,7 @@ 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 _, channel := range s.Server.Channels {
for id := range channel.stages {
if strings.HasSuffix(id, pkt.UserIDString) {
sgid = channel.GlobalID
@@ -287,7 +303,7 @@ func handleMsgSysLockGlobalSema(s *Session, p mhfpacket.MHFPacket) {
}
}
bf := byteframe.NewByteFrame()
if len(sgid) > 0 && sgid != s.server.GlobalID {
if len(sgid) > 0 && sgid != s.Server.GlobalID {
bf.WriteUint8(0)
bf.WriteUint8(0)
ps.Uint16(bf, sgid, false)
@@ -296,12 +312,12 @@ func handleMsgSysLockGlobalSema(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(0)
ps.Uint16(bf, pkt.ServerChannelIDString, false)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgSysUnlockGlobalSema(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysUnlockGlobalSema)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgSysUpdateRight(s *Session, p mhfpacket.MHFPacket) {}
@@ -313,7 +329,7 @@ func handleMsgSysAuthTerminal(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysRightsReload)
updateRights(s)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
@@ -351,18 +367,18 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint16(0)
switch pkt.SearchType {
case 1, 2, 3: // usersearchidx, usersearchname, lobbysearchname
for _, c := range s.server.Channels {
for _, c := range s.Server.Channels {
for _, session := range c.sessions {
if count == maxResults {
break
}
if pkt.SearchType == 1 && session.charID != cid {
if pkt.SearchType == 1 && session.CharID != cid {
continue
}
if pkt.SearchType == 2 && !strings.Contains(session.Name, term) {
continue
}
if pkt.SearchType == 3 && session.server.IP != ip && session.server.Port != port && session.stage.id != term {
if pkt.SearchType == 3 && session.Server.IP != ip && session.Server.Port != port && session.stage.id != term {
continue
}
count++
@@ -374,13 +390,13 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(0x0100007F)
}
resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID)
resp.WriteUint32(session.CharID)
resp.WriteUint8(uint8(len(sessionStage) + 1))
resp.WriteUint8(uint8(len(sessionName) + 1))
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])))
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.CharID, index: 3}])))
// TODO: This case might be <=G2
if _config.ErupeConfig.ClientID <= _config.G1 {
if config.GetConfig().ClientID <= config.G1 {
resp.WriteBytes(make([]byte, 8))
} else {
resp.WriteBytes(make([]byte, 40))
@@ -389,7 +405,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
resp.WriteNullTerminatedBytes(sessionStage)
resp.WriteNullTerminatedBytes(sessionName)
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}])
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.CharID, 3}])
}
}
case 4: // lobbysearch
@@ -411,7 +427,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
case 0:
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
findPartyParams.RankRestriction = bf.ReadInt16()
} else {
findPartyParams.RankRestriction = int16(bf.ReadInt8())
@@ -420,7 +436,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
case 1:
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadInt16())
} else {
findPartyParams.Targets = append(findPartyParams.Targets, int16(bf.ReadInt8()))
@@ -430,7 +446,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
var value int16
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
value = bf.ReadInt16()
} else {
value = int16(bf.ReadInt8())
@@ -451,7 +467,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
case 3: // Unknown
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadInt16())
} else {
findPartyParams.Unk0 = append(findPartyParams.Unk0, int16(bf.ReadInt8()))
@@ -460,7 +476,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
case 4: // Looking for n or already have n
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadInt16())
} else {
findPartyParams.Unk1 = append(findPartyParams.Unk1, int16(bf.ReadInt8()))
@@ -469,7 +485,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
case 5:
values := bf.ReadUint8()
for i := uint8(0); i < values; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadInt16())
} else {
findPartyParams.QuestID = append(findPartyParams.QuestID, int16(bf.ReadInt8()))
@@ -477,7 +493,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
}
}
}
for _, c := range s.server.Channels {
for _, c := range s.Server.Channels {
for _, stage := range c.stages {
if count == maxResults {
break
@@ -487,15 +503,15 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
sb3.Seek(4, 0)
stageDataParams := 7
if _config.ErupeConfig.ClientID <= _config.G10 {
if config.GetConfig().ClientID <= config.G10 {
stageDataParams = 4
} else if _config.ErupeConfig.ClientID <= _config.Z1 {
} else if config.GetConfig().ClientID <= config.Z1 {
stageDataParams = 6
}
var stageData []int16
for i := 0; i < stageDataParams; i++ {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
stageData = append(stageData, sb3.ReadInt16())
} else {
stageData = append(stageData, int16(sb3.ReadInt8()))
@@ -544,7 +560,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
for i := range stageData {
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
resp.WriteInt16(stageData[i])
} else {
resp.WriteInt8(int8(stageData[i]))
@@ -562,7 +578,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
}
resp.Seek(0, io.SeekStart)
resp.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {}
@@ -571,8 +587,8 @@ func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAnnounce(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAnnounce)
s.server.BroadcastRaviente(pkt.IPAddress, pkt.Port, pkt.StageID, pkt.Data.ReadUint8())
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Server.BroadcastRaviente(pkt.IPAddress, pkt.Port, pkt.StageID, pkt.Data.ReadUint8())
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfSetLoginwindow(s *Session, p mhfpacket.MHFPacket) {}
@@ -595,7 +611,7 @@ func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfTransferItem)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
@@ -760,7 +776,7 @@ func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(gz.Unk3)
bf.WriteUint8(gz.Unk4)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) {
@@ -773,7 +789,11 @@ func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
func userGetItems(s *Session) []mhfitem.MHFItemStack {
var data []byte
var items []mhfitem.MHFItemStack
s.server.db.QueryRow(`SELECT item_box FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow(`SELECT item_box FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
@@ -790,14 +810,18 @@ func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
items := userGetItems(s)
bf := byteframe.NewByteFrame()
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem)
newStacks := mhfitem.DiffItemStacks(userGetItems(s), pkt.UpdatedItems)
s.server.db.Exec(`UPDATE users u SET item_box=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, mhfitem.SerializeWarehouseItems(newStacks), s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`UPDATE users u SET item_box=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, mhfitem.SerializeWarehouseItems(newStacks), s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
@@ -806,20 +830,24 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
var total, redeemed, updated uint16
var lastCheck time.Time
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_checked FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&lastCheck)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow(fmt.Sprintf("SELECT %s_checked FROM stamps WHERE character_id=$1", pkt.StampType), s.CharID).Scan(&lastCheck)
if err != nil {
lastCheck = gametime.TimeAdjusted()
s.server.db.Exec("INSERT INTO stamps (character_id, hl_checked, ex_checked) VALUES ($1, $2, $2)", s.charID, gametime.TimeAdjusted())
database.Exec("INSERT INTO stamps (character_id, hl_checked, ex_checked) VALUES ($1, $2, $2)", s.CharID, gametime.TimeAdjusted())
} else {
s.server.db.Exec(fmt.Sprintf(`UPDATE stamps SET %s_checked=$1 WHERE character_id=$2`, pkt.StampType), gametime.TimeAdjusted(), s.charID)
database.Exec(fmt.Sprintf(`UPDATE stamps SET %s_checked=$1 WHERE character_id=$2`, pkt.StampType), gametime.TimeAdjusted(), s.CharID)
}
if lastCheck.Before(gametime.TimeWeekStart()) {
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1 WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID)
database.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1 WHERE character_id=$1", pkt.StampType, pkt.StampType), s.CharID)
updated = 1
}
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
database.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.CharID).Scan(&total, &redeemed)
bf := byteframe.NewByteFrame()
bf.WriteUint16(total)
bf.WriteUint16(redeemed)
@@ -827,18 +855,22 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(0)
bf.WriteUint16(0)
bf.WriteUint32(uint32(gametime.TimeWeekStart().Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
var total, redeemed uint16
var tktStack mhfitem.MHFItemStack
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.Unk1 == 10 { // Yearly Sub Ex
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
database.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.CharID).Scan(&total, &redeemed)
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1}
} else {
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
database.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.CharID).Scan(&total, &redeemed)
if pkt.StampType == "hl" {
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1630}, Quantity: 5}
} else {
@@ -853,16 +885,20 @@ func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(tktStack.Item.ItemID)
bf.WriteUint16(tktStack.Quantity)
bf.WriteUint32(uint32(gametime.TimeWeekStart().Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func getGoocooData(s *Session, cid uint32) [][]byte {
var goocoo []byte
var goocoos [][]byte
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for i := 0; i < 5; i++ {
err := s.server.db.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", i), cid).Scan(&goocoo)
err := database.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", i), cid).Scan(&goocoo)
if err != nil {
s.server.db.Exec("INSERT INTO goocoo (id) VALUES ($1)", s.charID)
database.Exec("INSERT INTO goocoo (id) VALUES ($1)", s.CharID)
return goocoos
}
if err == nil && goocoo != nil {
@@ -875,20 +911,24 @@ func getGoocooData(s *Session, cid uint32) [][]byte {
func handleMsgMhfEnumerateGuacot(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuacot)
bf := byteframe.NewByteFrame()
goocoos := getGoocooData(s, s.charID)
goocoos := getGoocooData(s, s.CharID)
bf.WriteUint16(uint16(len(goocoos)))
bf.WriteUint16(0)
for _, goocoo := range goocoos {
bf.WriteBytes(goocoo)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuacot)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for _, goocoo := range pkt.Goocoos {
if goocoo.Data1[0] == 0 {
s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", goocoo.Index), s.charID)
database.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", goocoo.Index), s.CharID)
} else {
bf := byteframe.NewByteFrame()
bf.WriteUint32(goocoo.Index)
@@ -900,11 +940,11 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint8(uint8(len(goocoo.Name)))
bf.WriteBytes(goocoo.Name)
s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", goocoo.Index), bf.Data(), s.charID)
database.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", goocoo.Index), bf.Data(), s.CharID)
dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", goocoo.Index))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type Scenario struct {
@@ -921,11 +961,15 @@ func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfInfoScenarioCounter)
var scenarios []Scenario
var scenario Scenario
scenarioData, err := s.server.db.Queryx("SELECT scenario_id, category_id FROM scenario_counter")
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
scenarioData, err := database.Queryx("SELECT scenario_id, category_id FROM scenario_counter")
if err != nil {
scenarioData.Close()
s.logger.Error("Failed to get scenario counter info from db", zap.Error(err))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
s.Logger.Error("Failed to get scenario counter info from db", zap.Error(err))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
for scenarioData.Next() {
@@ -954,26 +998,29 @@ func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint8(scenario.CategoryID)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEtcPoints)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
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)
_ = database.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 gametime.TimeAdjusted().After(dailyTime) {
s.server.db.Exec("UPDATE characters SET bonus_quests = 0, daily_quests = 0 WHERE id=$1", s.charID)
database.Exec("UPDATE characters SET bonus_quests = 0, daily_quests = 0 WHERE id=$1", s.CharID)
}
var bonusQuests, dailyQuests, promoPoints uint32
_ = s.server.db.QueryRow(`SELECT bonus_quests, daily_quests, promo_points FROM characters WHERE id = $1`, s.charID).Scan(&bonusQuests, &dailyQuests, &promoPoints)
_ = database.QueryRow(`SELECT bonus_quests, daily_quests, promo_points FROM characters WHERE id = $1`, s.CharID).Scan(&bonusQuests, &dailyQuests, &promoPoints)
resp := byteframe.NewByteFrame()
resp.WriteUint8(3) // Maybe a count of uint32(s)?
resp.WriteUint32(bonusQuests)
resp.WriteUint32(dailyQuests)
resp.WriteUint32(promoPoints)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) {
@@ -988,17 +1035,20 @@ func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) {
case 2:
column = "promo_points"
}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var value int16
err := s.server.db.QueryRow(fmt.Sprintf(`SELECT %s FROM characters WHERE id = $1`, column), s.charID).Scan(&value)
err = database.QueryRow(fmt.Sprintf(`SELECT %s FROM characters WHERE id = $1`, column), s.CharID).Scan(&value)
if err == nil {
if value+pkt.Delta < 0 {
s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = 0 WHERE id = $1`, column), s.charID)
database.Exec(fmt.Sprintf(`UPDATE characters SET %s = 0 WHERE id = $1`, column), s.CharID)
} else {
s.server.db.Exec(fmt.Sprintf(`UPDATE characters SET %s = %s + $1 WHERE id = $2`, column, column), pkt.Delta, s.charID)
database.Exec(fmt.Sprintf(`UPDATE characters SET %s = %s + $1 WHERE id = $2`, column, column), pkt.Delta, s.CharID)
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
@@ -1017,7 +1067,7 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
{300, 5392, 1, 5392, 3},
{999, 5392, 1, 5392, 4},
}
if _config.ErupeConfig.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
for _, reward := range rewards {
if pkt.HR >= reward.HR {
pkt.Item1 = reward.Item1
@@ -1030,12 +1080,16 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint16(pkt.HR)
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
bf.WriteUint16(pkt.GR)
}
var stamps, rewardTier, rewardUnk uint16
reward := mhfitem.MHFItemStack{Item: mhfitem.MHFItem{}}
s.server.db.QueryRow(`UPDATE characters SET stampcard = stampcard + $1 WHERE id = $2 RETURNING stampcard`, pkt.Stamps, s.charID).Scan(&stamps)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow(`UPDATE characters SET stampcard = stampcard + $1 WHERE id = $2 RETURNING stampcard`, pkt.Stamps, s.CharID).Scan(&stamps)
bf.WriteUint16(stamps - pkt.Stamps)
bf.WriteUint16(stamps)
@@ -1055,14 +1109,14 @@ func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(rewardUnk)
bf.WriteUint16(reward.Item.ItemID)
bf.WriteUint16(reward.Quantity)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUnreserveSrg)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}
@@ -1072,10 +1126,10 @@ func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(gametime.TimeWeekStart().Unix())) // Start
bf.WriteUint32(uint32(gametime.TimeWeekNext().Unix())) // End
bf.WriteInt32(s.server.erupeConfig.EarthStatus)
bf.WriteInt32(s.server.erupeConfig.EarthID)
for i, m := range s.server.erupeConfig.EarthMonsters {
if _config.ErupeConfig.ClientID <= _config.G9 {
bf.WriteInt32(config.GetConfig().EarthStatus)
bf.WriteInt32(config.GetConfig().EarthID)
for i, m := range config.GetConfig().EarthMonsters {
if config.GetConfig().ClientID <= config.G9 {
if i == 3 {
break
}
@@ -1085,7 +1139,7 @@ func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteInt32(m)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
@@ -1124,7 +1178,7 @@ func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
}
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {}
@@ -1135,7 +1189,7 @@ func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {
for i := uint16(0); i < pkt.Results; i++ {
bf.WriteUint32(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {
@@ -1143,7 +1197,7 @@ func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
bf.WriteUint16(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type SeibattleTimetable struct {
@@ -1281,12 +1335,12 @@ func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostSeibattle)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetDailyMissionMaster(s *Session, p mhfpacket.MHFPacket) {}
@@ -1297,10 +1351,10 @@ func handleMsgMhfSetDailyMissionPersonal(s *Session, p mhfpacket.MHFPacket) {}
func equipSkinHistSize() int {
size := 3200
if _config.ErupeConfig.ClientID <= _config.Z2 {
if config.GetConfig().ClientID <= config.Z2 {
size = 2560
}
if _config.ErupeConfig.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
size = 1280
}
return size
@@ -1310,22 +1364,30 @@ func handleMsgMhfGetEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEquipSkinHist)
size := equipSkinHistSize()
var data []byte
err := s.server.db.QueryRow("SELECT COALESCE(skin_hist::bytea, $2::bytea) FROM characters WHERE id = $1", s.charID, make([]byte, size)).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load skin_hist", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT COALESCE(skin_hist::bytea, $2::bytea) FROM characters WHERE id = $1", s.CharID, make([]byte, size)).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load skin_hist", zap.Error(err))
data = make([]byte, size)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateEquipSkinHist)
size := equipSkinHistSize()
var data []byte
err := s.server.db.QueryRow("SELECT COALESCE(skin_hist, $2) FROM characters WHERE id = $1", s.charID, make([]byte, size)).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get skin_hist", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT COALESCE(skin_hist, $2) FROM characters WHERE id = $1", s.CharID, make([]byte, size)).Scan(&data)
if err != nil {
s.Logger.Error("Failed to get skin_hist", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
@@ -1336,15 +1398,15 @@ func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {
bitInByte := bit % 8
data[startByte+byteInd] |= bits.Reverse8(1 << uint(bitInByte))
dumpSaveData(s, data, "skinhist")
s.server.db.Exec("UPDATE characters SET skin_hist=$1 WHERE id=$2", data, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database.Exec("UPDATE characters SET skin_hist=$1 WHERE id=$2", data, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetUdShopCoin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdShopCoin)
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {}
@@ -1353,22 +1415,30 @@ func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEnhancedMinidata)
// this looks to be the detailed chunk of information you can pull up on players in town
var data []byte
err := s.server.db.QueryRow("SELECT minidata FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load minidata")
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT minidata FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load minidata")
data = make([]byte, 1)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetEnhancedMinidata)
dumpSaveData(s, pkt.RawDataPayload, "minidata")
_, err := s.server.db.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to save minidata", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
_, err = database.Exec("UPDATE characters SET minidata=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.Logger.Error("Failed to save minidata", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetLobbyCrowd(s *Session, p mhfpacket.MHFPacket) {
@@ -1378,7 +1448,7 @@ func handleMsgMhfGetLobbyCrowd(s *Session, p mhfpacket.MHFPacket) {
// It can be worried about later if we ever get to the point where there are
// full servers to actually need to migrate people from and empty ones to
pkt := p.(*mhfpacket.MsgMhfGetLobbyCrowd)
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x320))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x320))
}
type TrendWeapon struct {
@@ -1389,8 +1459,12 @@ type TrendWeapon struct {
func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetTrendWeapon)
trendWeapons := [14][3]TrendWeapon{}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for i := uint8(0); i < 14; i++ {
rows, err := s.server.db.Query(`SELECT weapon_id FROM trend_weapons WHERE weapon_type=$1 ORDER BY count DESC LIMIT 3`, i)
rows, err := database.Query(`SELECT weapon_id FROM trend_weapons WHERE weapon_type=$1 ORDER BY count DESC LIMIT 3`, i)
if err != nil {
continue
}
@@ -1414,12 +1488,16 @@ func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {
}
bf.Seek(0, 0)
bf.WriteUint8(x)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateUseTrendWeaponLog(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateUseTrendWeaponLog)
s.server.db.Exec(`INSERT INTO trend_weapons (weapon_id, weapon_type, count) VALUES ($1, $2, 1) ON CONFLICT (weapon_id) DO
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`INSERT INTO trend_weapons (weapon_id, weapon_type, count) VALUES ($1, $2, 1) ON CONFLICT (weapon_id) DO
UPDATE SET count = trend_weapons.count+1`, pkt.WeaponID, pkt.WeaponType)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"fmt"
"io"
)
@@ -88,19 +89,23 @@ func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetAchievement)
var exists int
err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID).Scan(&exists)
database, err := db.GetDB()
if err != nil {
s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID)
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID).Scan(&exists)
if err != nil {
database.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID)
}
var scores [33]int32
err = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID).Scan(&scores[0],
err = database.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID).Scan(&scores[0],
&scores[0], &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8],
&scores[9], &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16],
&scores[17], &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24],
&scores[25], &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32])
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 20))
return
}
@@ -136,26 +141,29 @@ func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(points)
resp.WriteUint32(points)
resp.WriteUint32(points)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfSetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetCaAchievementHist)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAddAchievement)
var exists int
err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID).Scan(&exists)
database, err := db.GetDB()
if err != nil {
s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID)
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var exists int
err = database.QueryRow("SELECT id FROM achievements WHERE id=$1", s.CharID).Scan(&exists)
if err != nil {
database.Exec("INSERT INTO achievements (id) VALUES ($1)", s.CharID)
}
s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID)
database.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.CharID)
}
func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/stringsupport"
@@ -18,7 +19,7 @@ func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// Checks the status of Bultin Board Server to see if authenticated
@@ -29,7 +30,7 @@ func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(401) //unk http status?
bf.WriteUint32(401) //unk http status?
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// Tells the game client what host port and gives the bultin board article a token
@@ -39,15 +40,15 @@ func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {
articleToken := token.Generate(40)
bf.WriteUint32(200) //http status //200 success //4XX An error occured server side
bf.WriteUint32(s.server.erupeConfig.Screenshots.Port)
bf.WriteUint32(config.GetConfig().Screenshots.Port)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false))
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false))
bf.WriteBytes(stringsupport.PaddedString(config.GetConfig().Screenshots.Host, 64, false))
//pkt.unk1[3] == Changes sometimes?
if s.server.erupeConfig.Screenshots.Enabled && s.server.erupeConfig.Discord.Enabled {
s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken)
if config.GetConfig().Screenshots.Enabled && config.GetConfig().Discord.Enabled {
s.Server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -2,10 +2,11 @@ package channelserver
import (
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/mhfcourse"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
ps "erupe-ce/utils/pascalstring"
"fmt"
@@ -18,25 +19,33 @@ import (
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
var netcafePoints uint32
err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafePoints)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get netcafe points from db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.CharID).Scan(&netcafePoints)
if err != nil {
s.Logger.Error("Failed to get netcafe points from db", zap.Error(err))
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(netcafePoints)
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint)
var netcafePoints uint32
err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafePoints)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get netcate points from db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.CharID).Scan(&netcafePoints)
if err != nil {
s.Logger.Error("Failed to get netcate points from db", zap.Error(err))
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(netcafePoints)
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
@@ -49,9 +58,13 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
// get time after which daily claiming would be valid from db
var dailyTime time.Time
err := 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)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get daily_time savedata from db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.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 err != nil {
s.Logger.Error("Failed to get daily_time savedata from db", zap.Error(err))
}
var bondBonus, bonusQuests, dailyQuests uint32
@@ -59,9 +72,9 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
if midday.After(dailyTime) {
addPointNetcafe(s, 5)
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)
bonusQuests = config.GetConfig().GameplayOptions.BonusQuestAllowance
dailyQuests = config.GetConfig().GameplayOptions.DailyQuestAllowance
database.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 {
bf.WriteBool(false)
@@ -69,27 +82,30 @@ func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(bondBonus)
bf.WriteUint32(bonusQuests)
bf.WriteUint32(dailyQuests)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetCafeDuration)
bf := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var cafeReset time.Time
err := s.server.db.QueryRow(`SELECT cafe_reset FROM characters WHERE id=$1`, s.charID).Scan(&cafeReset)
err = database.QueryRow(`SELECT cafe_reset FROM characters WHERE id=$1`, s.CharID).Scan(&cafeReset)
if err != nil {
cafeReset = gametime.TimeWeekNext()
s.server.db.Exec(`UPDATE characters SET cafe_reset=$1 WHERE id=$2`, cafeReset, s.charID)
database.Exec(`UPDATE characters SET cafe_reset=$1 WHERE id=$2`, cafeReset, s.CharID)
}
if gametime.TimeAdjusted().After(cafeReset) {
cafeReset = gametime.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)
database.Exec(`UPDATE characters SET cafe_time=0, cafe_reset=$1 WHERE id=$2`, cafeReset, s.CharID)
database.Exec(`DELETE FROM cafe_accepted WHERE character_id=$1`, s.CharID)
}
var cafeTime uint32
err = s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
err = database.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.CharID).Scan(&cafeTime)
if err != nil {
panic(err)
}
@@ -97,11 +113,11 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
cafeTime = uint32(gametime.TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
}
bf.WriteUint32(cafeTime)
if _config.ErupeConfig.ClientID >= _config.ZZ {
if config.GetConfig().ClientID >= config.ZZ {
bf.WriteUint16(0)
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
ps.Uint16(bf, fmt.Sprintf(s.Server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type CafeBonus struct {
@@ -116,26 +132,29 @@ type CafeBonus struct {
func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetCafeDurationBonusInfo)
bf := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var count uint32
rows, err := s.server.db.Queryx(`
rows, err := database.Queryx(`
SELECT cb.id, time_req, item_type, item_id, quantity,
(
SELECT count(*)
FROM cafe_accepted ca
WHERE cb.id = ca.cafe_id AND ca.character_id = $1
)::int::bool AS claimed
FROM cafebonus cb ORDER BY id ASC;`, s.charID)
FROM cafebonus cb ORDER BY id ASC;`, s.CharID)
if err != nil {
s.logger.Error("Error getting cafebonus", zap.Error(err))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Error getting cafebonus", zap.Error(err))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
for rows.Next() {
count++
cafeBonus := &CafeBonus{}
err = rows.StructScan(&cafeBonus)
if err != nil {
s.logger.Error("Error scanning cafebonus", zap.Error(err))
s.Logger.Error("Error scanning cafebonus", zap.Error(err))
}
bf.WriteUint32(cafeBonus.TimeReq)
bf.WriteUint32(cafeBonus.ItemType)
@@ -148,7 +167,7 @@ func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(uint32(gametime.TimeAdjusted().Unix()))
resp.WriteUint32(count)
resp.WriteBytes(bf.Data())
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
}
@@ -157,7 +176,11 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
var count uint32
bf.WriteUint32(0)
rows, err := s.server.db.Queryx(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`
SELECT c.id, time_req, item_type, item_id, quantity
FROM cafebonus c
WHERE (
@@ -168,9 +191,9 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
SELECT ch.cafe_time + $2
FROM characters ch
WHERE ch.id = $1
) >= time_req`, s.charID, gametime.TimeAdjusted().Unix()-s.sessionStart)
) >= time_req`, s.CharID, gametime.TimeAdjusted().Unix()-s.sessionStart)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
} else {
for rows.Next() {
cafeBonus := &CafeBonus{}
@@ -186,15 +209,19 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
}
bf.Seek(0, io.SeekStart)
bf.WriteUint32(count)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
}
func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var cafeBonus CafeBonus
for _, cbID := range pkt.CafeBonusID {
err := s.server.db.QueryRow(`
err := database.QueryRow(`
SELECT cb.id, item_type, quantity FROM cafebonus cb WHERE cb.id=$1
`, cbID).Scan(&cafeBonus.ID, &cafeBonus.ItemType, &cafeBonus.Quantity)
if err == nil {
@@ -202,85 +229,101 @@ func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket
addPointNetcafe(s, int(cafeBonus.Quantity))
}
}
s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.charID)
database.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func addPointNetcafe(s *Session, p int) error {
var points int
err := s.server.db.QueryRow("SELECT netcafe_points FROM characters WHERE id = $1", s.charID).Scan(&points)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT netcafe_points FROM characters WHERE id = $1", s.CharID).Scan(&points)
if err != nil {
return err
}
if points+p > s.server.erupeConfig.GameplayOptions.MaximumNP {
points = s.server.erupeConfig.GameplayOptions.MaximumNP
if points+p > config.GetConfig().GameplayOptions.MaximumNP {
points = config.GetConfig().GameplayOptions.MaximumNP
} else {
points += p
}
s.server.db.Exec("UPDATE characters SET netcafe_points=$1 WHERE id=$2", points, s.charID)
database.Exec("UPDATE characters SET netcafe_points=$1 WHERE id=$2", points, s.CharID)
return nil
}
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
bf := byteframe.NewByteFrame()
boostLimit := gametime.TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Second)
if s.server.erupeConfig.GameplayOptions.DisableBoostTime {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
boostLimit := gametime.TimeAdjusted().Add(time.Duration(config.GetConfig().GameplayOptions.BoostTimeDuration) * time.Second)
if config.GetConfig().GameplayOptions.DisableBoostTime {
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID)
database.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.CharID)
bf.WriteUint32(uint32(boostLimit.Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBoostTime)
doAckBufSucceed(s, pkt.AckHandle, []byte{})
DoAckBufSucceed(s, pkt.AckHandle, []byte{})
}
func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
bf := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var boostLimit time.Time
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
err = database.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.CharID).Scan(&boostLimit)
if err != nil {
bf.WriteUint32(0)
} else {
bf.WriteUint32(uint32(boostLimit.Unix()))
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBoostRight)
var boostLimit time.Time
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
database, err := db.GetDB()
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.CharID).Scan(&boostLimit)
if err != nil {
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
if boostLimit.After(gametime.TimeAdjusted()) {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
} else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})
}
}
func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostBoostTime)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeLimit)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -1,7 +1,7 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
ps "erupe-ce/utils/pascalstring"
@@ -68,7 +68,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt16(event.MaxHR)
bf.WriteInt16(event.MinSR)
bf.WriteInt16(event.MaxSR)
if _config.ErupeConfig.ClientID >= _config.G3 {
if config.GetConfig().ClientID >= config.G3 {
bf.WriteInt16(event.MinGR)
bf.WriteInt16(event.MaxGR)
}
@@ -129,7 +129,7 @@ func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(link.CategoryID)
bf.WriteUint32(link.CampaignID)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
@@ -137,14 +137,14 @@ func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint16(1)
bf.WriteUint16(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyCampaign)
bf := byteframe.NewByteFrame()
bf.WriteUint32(1)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
@@ -167,10 +167,10 @@ func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(item.Unk4)
bf.WriteUint32(item.Unk5)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireItem)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -67,7 +67,7 @@ func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
@@ -75,12 +75,12 @@ func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetTinyBin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetTinyBin)
// requested after conquest quests
doAckBufSucceed(s, pkt.AckHandle, []byte{})
DoAckBufSucceed(s, pkt.AckHandle, []byte{})
}
func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostTinyBin)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {
@@ -92,7 +92,7 @@ func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {
@@ -108,7 +108,7 @@ func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(1)
bf.WriteBytes(stringsupport.PaddedString("Test", 14, true))
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {
@@ -119,5 +119,5 @@ func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}

View File

@@ -3,10 +3,11 @@ package channelserver
import (
"crypto/rand"
"encoding/hex"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/binpacket"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/logger"
"erupe-ce/utils/mhfcid"
"erupe-ce/utils/mhfcourse"
@@ -43,17 +44,17 @@ const (
)
var (
commands map[string]_config.Command
commands map[string]config.Command
once sync.Once // Ensures that initialization happens only once
commandsMu sync.Mutex // Mutex to ensure thread safety for commands map
)
func (server *Server) initCommands() {
func (server *ChannelServer) initCommands() {
once.Do(func() {
commands = make(map[string]_config.Command)
commands = make(map[string]config.Command)
commandLogger := logger.Get().Named("command")
cmds := _config.ErupeConfig.Commands
cmds := config.GetConfig().Commands
for _, cmd := range cmds {
commands[cmd.Name] = cmd
if cmd.Enabled {
@@ -65,8 +66,8 @@ func (server *Server) initCommands() {
})
}
func sendDisabledCommandMessage(s *Session, cmd _config.Command) {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.disabled, cmd.Name))
func sendDisabledCommandMessage(s *Session, cmd config.Command) {
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.disabled, cmd.Name))
}
func sendServerChatMessage(s *Session, message string) {
@@ -92,7 +93,11 @@ func sendServerChatMessage(s *Session, message string) {
}
func parseChatCommand(s *Session, command string) {
args := strings.Split(command[len(s.server.erupeConfig.CommandPrefix):], " ")
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
args := strings.Split(command[len(config.GetConfig().CommandPrefix):], " ")
switch args[0] {
case commands["Ban"].Prefix:
if s.isOp() {
@@ -118,7 +123,7 @@ func parseChatCommand(s *Session, command string) {
expiry = time.Now().Add(time.Duration(length) * time.Hour * 24 * 365)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ban.error)
sendServerChatMessage(s, s.Server.i18n.commands.ban.error)
return
}
}
@@ -126,39 +131,39 @@ func parseChatCommand(s *Session, command string) {
if cid > 0 {
var uid uint32
var uname string
err := s.server.db.QueryRow(`SELECT id, username FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, cid).Scan(&uid, &uname)
err := database.QueryRow(`SELECT id, username FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, cid).Scan(&uid, &uname)
if err == nil {
if expiry.IsZero() {
s.server.db.Exec(`INSERT INTO bans VALUES ($1)
database.Exec(`INSERT INTO bans VALUES ($1)
ON CONFLICT (user_id) DO UPDATE SET expires=NULL`, uid)
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.ban.success, uname))
} else {
s.server.db.Exec(`INSERT INTO bans VALUES ($1, $2)
database.Exec(`INSERT INTO bans VALUES ($1, $2)
ON CONFLICT (user_id) DO UPDATE SET expires=$2`, uid, expiry)
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ban.success, uname)+fmt.Sprintf(s.server.i18n.commands.ban.length, expiry.Format(time.DateTime)))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.ban.success, uname)+fmt.Sprintf(s.Server.i18n.commands.ban.length, expiry.Format(time.DateTime)))
}
s.server.DisconnectUser(uid)
s.Server.DisconnectUser(uid)
} else {
sendServerChatMessage(s, s.server.i18n.commands.ban.noUser)
sendServerChatMessage(s, s.Server.i18n.commands.ban.noUser)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ban.invalid)
sendServerChatMessage(s, s.Server.i18n.commands.ban.invalid)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ban.error)
sendServerChatMessage(s, s.Server.i18n.commands.ban.error)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.noOp)
sendServerChatMessage(s, s.Server.i18n.commands.noOp)
}
case commands["Timer"].Prefix:
if commands["Timer"].Enabled || s.isOp() {
var state bool
s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&state)
s.server.db.Exec(`UPDATE users u SET timer=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, !state, s.charID)
database.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&state)
database.Exec(`UPDATE users u SET timer=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, !state, s.CharID)
if state {
sendServerChatMessage(s, s.server.i18n.commands.timer.disabled)
sendServerChatMessage(s, s.Server.i18n.commands.timer.disabled)
} else {
sendServerChatMessage(s, s.server.i18n.commands.timer.enabled)
sendServerChatMessage(s, s.Server.i18n.commands.timer.enabled)
}
} else {
sendDisabledCommandMessage(s, commands["Timer"])
@@ -167,56 +172,56 @@ func parseChatCommand(s *Session, command string) {
if commands["PSN"].Enabled || s.isOp() {
if len(args) > 1 {
var exists int
s.server.db.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, args[1]).Scan(&exists)
database.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, args[1]).Scan(&exists)
if exists == 0 {
_, err := s.server.db.Exec(`UPDATE users u SET psn_id=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, args[1], s.charID)
_, err := database.Exec(`UPDATE users u SET psn_id=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, args[1], s.CharID)
if err == nil {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.success, args[1]))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.psn.success, args[1]))
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.psn.exists)
sendServerChatMessage(s, s.Server.i18n.commands.psn.exists)
}
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.psn.error, commands["PSN"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.psn.error, commands["PSN"].Prefix))
}
} else {
sendDisabledCommandMessage(s, commands["PSN"])
}
case commands["Reload"].Prefix:
if commands["Reload"].Enabled || s.isOp() {
sendServerChatMessage(s, s.server.i18n.commands.reload)
sendServerChatMessage(s, s.Server.i18n.commands.reload)
var temp mhfpacket.MHFPacket
for _, object := range s.stage.objects {
if object.ownerCharID == s.charID {
if object.ownerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
s.QueueSendMHF(temp)
}
for _, session := range s.server.sessions {
for _, session := range s.Server.sessions {
if s == session {
continue
}
temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID}
temp = &mhfpacket.MsgSysDeleteUser{CharID: session.CharID}
s.QueueSendMHF(temp)
}
time.Sleep(500 * time.Millisecond)
for _, session := range s.server.sessions {
for _, session := range s.Server.sessions {
if s == session {
continue
}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.CharID}
s.QueueSendMHF(temp)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
CharID: session.CharID,
BinaryType: uint8(i + 1),
}
s.QueueSendMHF(temp)
}
}
for _, obj := range s.stage.objects {
if obj.ownerCharID == s.charID {
if obj.ownerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDuplicateObject{
@@ -234,20 +239,20 @@ func parseChatCommand(s *Session, command string) {
}
case commands["KeyQuest"].Prefix:
if commands["KeyQuest"].Enabled || s.isOp() {
if s.server.erupeConfig.ClientID < _config.G10 {
sendServerChatMessage(s, s.server.i18n.commands.kqf.version)
if config.GetConfig().ClientID < config.G10 {
sendServerChatMessage(s, s.Server.i18n.commands.kqf.version)
} else {
if len(args) > 1 {
if args[1] == "get" {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.get, s.kqf))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.kqf.get, s.kqf))
} else if args[1] == "set" {
if len(args) > 2 && len(args[2]) == 16 {
hexd, _ := hex.DecodeString(args[2])
s.kqf = hexd
s.kqfOverride = true
sendServerChatMessage(s, s.server.i18n.commands.kqf.set.success)
sendServerChatMessage(s, s.Server.i18n.commands.kqf.set.success)
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.kqf.set.error, commands["KeyQuest"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.kqf.set.error, commands["KeyQuest"].Prefix))
}
}
}
@@ -259,14 +264,14 @@ func parseChatCommand(s *Session, command string) {
if commands["Rights"].Enabled || s.isOp() {
if len(args) > 1 {
v, _ := strconv.Atoi(args[1])
_, err := s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.charID)
_, err := database.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", v, s.CharID)
if err == nil {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.success, v))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.rights.success, v))
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.rights.error, commands["Rights"].Prefix))
}
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.rights.error, commands["Rights"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.rights.error, commands["Rights"].Prefix))
}
} else {
sendDisabledCommandMessage(s, commands["Rights"])
@@ -277,7 +282,7 @@ func parseChatCommand(s *Session, command string) {
for _, course := range mhfcourse.Courses() {
for _, alias := range course.Aliases() {
if strings.ToLower(args[1]) == strings.ToLower(alias) {
if slices.Contains(s.server.erupeConfig.Courses, _config.Course{Name: course.Aliases()[0], Enabled: true}) {
if slices.Contains(config.GetConfig().Courses, config.Course{Name: course.Aliases()[0], Enabled: true}) {
var delta, rightsInt uint32
if mhfcourse.CourseExists(course.ID, s.courses) {
ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool {
@@ -290,26 +295,26 @@ func parseChatCommand(s *Session, command string) {
})
if ei != -1 {
delta = uint32(-1 * math.Pow(2, float64(course.ID)))
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.disabled, course.Aliases()[0]))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.course.disabled, course.Aliases()[0]))
}
} else {
delta = uint32(math.Pow(2, float64(course.ID)))
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.enabled, course.Aliases()[0]))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.course.enabled, course.Aliases()[0]))
}
err := 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)
err := database.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", s.CharID).Scan(&rightsInt)
if err == nil {
s.server.db.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", rightsInt+delta, s.charID)
database.Exec("UPDATE users u SET rights=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", rightsInt+delta, s.CharID)
}
updateRights(s)
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.locked, course.Aliases()[0]))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.course.locked, course.Aliases()[0]))
}
return
}
}
}
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.course.error, commands["Course"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.course.error, commands["Course"].Prefix))
}
} else {
sendDisabledCommandMessage(s, commands["Course"])
@@ -317,50 +322,50 @@ func parseChatCommand(s *Session, command string) {
case commands["Raviente"].Prefix:
if commands["Raviente"].Enabled || s.isOp() {
if len(args) > 1 {
if s.server.getRaviSemaphore() != nil {
if s.Server.getRaviSemaphore() != nil {
switch args[1] {
case "start":
if s.server.raviente.register[1] == 0 {
s.server.raviente.register[1] = s.server.raviente.register[3]
sendServerChatMessage(s, s.server.i18n.commands.ravi.start.success)
if s.Server.raviente.register[1] == 0 {
s.Server.raviente.register[1] = s.Server.raviente.register[3]
sendServerChatMessage(s, s.Server.i18n.commands.ravi.start.success)
s.notifyRavi()
} else {
sendServerChatMessage(s, s.server.i18n.commands.ravi.start.error)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.start.error)
}
case "cm", "check", "checkmultiplier", "multiplier":
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.ravi.multiplier, s.server.GetRaviMultiplier()))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.ravi.multiplier, s.Server.GetRaviMultiplier()))
case "sr", "sendres", "resurrection", "ss", "sendsed", "rs", "reqsed":
if s.server.erupeConfig.ClientID == _config.ZZ {
if config.GetConfig().ClientID == config.ZZ {
switch args[1] {
case "sr", "sendres", "resurrection":
if s.server.raviente.state[28] > 0 {
sendServerChatMessage(s, s.server.i18n.commands.ravi.res.success)
s.server.raviente.state[28] = 0
if s.Server.raviente.state[28] > 0 {
sendServerChatMessage(s, s.Server.i18n.commands.ravi.res.success)
s.Server.raviente.state[28] = 0
} else {
sendServerChatMessage(s, s.server.i18n.commands.ravi.res.error)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.res.error)
}
case "ss", "sendsed":
sendServerChatMessage(s, s.server.i18n.commands.ravi.sed.success)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.sed.success)
// Total BerRavi HP
HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4]
s.server.raviente.support[1] = HP
HP := s.Server.raviente.state[0] + s.Server.raviente.state[1] + s.Server.raviente.state[2] + s.Server.raviente.state[3] + s.Server.raviente.state[4]
s.Server.raviente.support[1] = HP
case "rs", "reqsed":
sendServerChatMessage(s, s.server.i18n.commands.ravi.request)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.request)
// Total BerRavi HP
HP := s.server.raviente.state[0] + s.server.raviente.state[1] + s.server.raviente.state[2] + s.server.raviente.state[3] + s.server.raviente.state[4]
s.server.raviente.support[1] = HP + 1
HP := s.Server.raviente.state[0] + s.Server.raviente.state[1] + s.Server.raviente.state[2] + s.Server.raviente.state[3] + s.Server.raviente.state[4]
s.Server.raviente.support[1] = HP + 1
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ravi.version)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.version)
}
default:
sendServerChatMessage(s, s.server.i18n.commands.ravi.error)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.error)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ravi.noPlayers)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.noPlayers)
}
} else {
sendServerChatMessage(s, s.server.i18n.commands.ravi.error)
sendServerChatMessage(s, s.Server.i18n.commands.ravi.error)
}
} else {
sendDisabledCommandMessage(s, commands["Raviente"])
@@ -377,13 +382,13 @@ func parseChatCommand(s *Session, command string) {
payload.WriteInt16(int16(y)) // Y
payloadBytes := payload.Data()
s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{
CharID: s.charID,
CharID: s.CharID,
MessageType: BinaryMessageTypeState,
RawDataPayload: payloadBytes,
})
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.success, x, y))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.teleport.success, x, y))
} else {
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.teleport.error, commands["Teleport"].Prefix))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.teleport.error, commands["Teleport"].Prefix))
}
} else {
sendDisabledCommandMessage(s, commands["Teleport"])
@@ -391,14 +396,14 @@ func parseChatCommand(s *Session, command string) {
case commands["Discord"].Prefix:
if commands["Discord"].Enabled || s.isOp() {
var _token string
err := s.server.db.QueryRow(`SELECT discord_token FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&_token)
err := database.QueryRow(`SELECT discord_token FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&_token)
if err != nil {
randToken := make([]byte, 4)
rand.Read(randToken)
_token = fmt.Sprintf("%x-%x", randToken[:2], randToken[2:])
s.server.db.Exec(`UPDATE users u SET discord_token = $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, _token, s.charID)
database.Exec(`UPDATE users u SET discord_token = $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, _token, s.CharID)
}
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.discord.success, _token))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.commands.discord.success, _token))
} else {
sendDisabledCommandMessage(s, commands["Discord"])
}
@@ -406,7 +411,7 @@ func parseChatCommand(s *Session, command string) {
if commands["Help"].Enabled || s.isOp() {
for _, command := range commands {
if command.Enabled || s.isOp() {
sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", s.server.erupeConfig.CommandPrefix, command.Prefix, command.Description))
sendServerChatMessage(s, fmt.Sprintf("%s%s: %s", config.GetConfig().CommandPrefix, command.Prefix, command.Description))
}
}
} else {
@@ -418,21 +423,24 @@ func parseChatCommand(s *Session, command string) {
func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCastBinary)
tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x03 && len(pkt.RawDataPayload) == 0x10 {
if tmp.ReadUint16() == 0x0002 && tmp.ReadUint8() == 0x18 {
var timer bool
s.server.db.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&timer)
database.QueryRow(`SELECT COALESCE(timer, false) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&timer)
if timer {
_ = tmp.ReadBytes(9)
tmp.SetLE()
frame := tmp.ReadUint32()
sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.timer, frame/30/60/60, frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame))
sendServerChatMessage(s, fmt.Sprintf(s.Server.i18n.timer, frame/30/60/60, frame/30/60, frame/30%60, int(math.Round(float64(frame%30*100)/3)), frame))
}
}
}
if s.server.erupeConfig.DebugOptions.QuestTools {
if config.GetConfig().DebugOptions.QuestTools {
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
// This is only correct most of the time
tmp.ReadBytes(20)
@@ -440,7 +448,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
x := tmp.ReadFloat32()
y := tmp.ReadFloat32()
z := tmp.ReadFloat32()
s.logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z}))
s.Logger.Debug("Coord", zap.Float32s("XYZ", []float32{x, y, z}))
}
}
@@ -463,7 +471,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
msgBinTargeted = &binpacket.MsgBinTargeted{}
err := msgBinTargeted.Parse(tmp)
if err != nil {
s.logger.Warn("Failed to parse targeted cast binary")
s.Logger.Warn("Failed to parse targeted cast binary")
return
}
realPayload = msgBinTargeted.RawDataPayload
@@ -485,19 +493,19 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
bf.SetLE()
chatMessage := &binpacket.MsgBinChat{}
chatMessage.Parse(bf)
if strings.HasPrefix(chatMessage.Message, s.server.erupeConfig.CommandPrefix) {
if strings.HasPrefix(chatMessage.Message, config.GetConfig().CommandPrefix) {
parseChatCommand(s, chatMessage.Message)
return
}
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
s.Server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
}
}
}
// Make the response to forward to the other client(s).
resp := &mhfpacket.MsgSysCastedBinary{
CharID: s.charID,
CharID: s.CharID,
BroadcastType: pkt.BroadcastType, // (The client never uses Type0 upon receiving)
MessageType: pkt.MessageType,
RawDataPayload: realPayload,
@@ -506,7 +514,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
// Send to the proper recipients.
switch pkt.BroadcastType {
case BroadcastTypeWorld:
s.server.WorldcastMHF(resp, s, nil)
s.Server.WorldcastMHF(resp, s, nil)
case BroadcastTypeStage:
if returnToSender {
s.stage.BroadcastMHF(resp, nil)
@@ -515,16 +523,16 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
}
case BroadcastTypeServer:
if pkt.MessageType == 1 {
raviSema := s.server.getRaviSemaphore()
raviSema := s.Server.getRaviSemaphore()
if raviSema != nil {
raviSema.BroadcastMHF(resp, s)
}
} else {
s.server.BroadcastMHF(resp, s)
s.Server.BroadcastMHF(resp, s)
}
case BroadcastTypeTargeted:
for _, targetID := range (*msgBinTargeted).TargetCharIDs {
char := s.server.FindSessionByCharID(targetID)
char := s.Server.FindSessionByCharID(targetID)
if char != nil {
char.QueueSendMHF(resp)

View File

@@ -3,9 +3,11 @@ package channelserver
import (
"encoding/binary"
"errors"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/bfutil"
"erupe-ce/utils/db"
"erupe-ce/utils/stringsupport"
"fmt"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/nullcomp"
@@ -58,8 +60,8 @@ type CharacterSaveData struct {
func getPointers() map[SavePointer]int {
pointers := map[SavePointer]int{pGender: 81, lBookshelfData: 5576}
switch _config.ErupeConfig.ClientID {
case _config.ZZ:
switch config.GetConfig().ClientID {
case config.ZZ:
pointers[pWeaponID] = 128522
pointers[pWeaponType] = 128789
pointers[pHouseTier] = 129900
@@ -72,9 +74,9 @@ func getPointers() map[SavePointer]int {
pointers[pGardenData] = 142424
pointers[pRP] = 142614
pointers[pKQF] = 146720
case _config.Z2, _config.Z1, _config.G101, _config.G10, _config.G91, _config.G9, _config.G81, _config.G8,
_config.G7, _config.G61, _config.G6, _config.G52, _config.G51, _config.G5, _config.GG, _config.G32, _config.G31,
_config.G3, _config.G2, _config.G1:
case config.Z2, config.Z1, config.G101, config.G10, config.G91, config.G9, config.G81, config.G8,
config.G7, config.G61, config.G6, config.G52, config.G51, config.G5, config.GG, config.G32, config.G31,
config.G3, config.G2, config.G1:
pointers[pWeaponID] = 92522
pointers[pWeaponType] = 92789
pointers[pHouseTier] = 93900
@@ -87,7 +89,7 @@ func getPointers() map[SavePointer]int {
pointers[pGardenData] = 106424
pointers[pRP] = 106614
pointers[pKQF] = 110720
case _config.F5, _config.F4:
case config.F5, config.F4:
pointers[pWeaponID] = 60522
pointers[pWeaponType] = 60789
pointers[pHouseTier] = 61900
@@ -98,7 +100,7 @@ func getPointers() map[SavePointer]int {
pointers[pGalleryData] = 72064
pointers[pGardenData] = 74424
pointers[pRP] = 74614
case _config.S6:
case config.S6:
pointers[pWeaponID] = 12522
pointers[pWeaponType] = 12789
pointers[pHouseTier] = 13900
@@ -110,24 +112,28 @@ func getPointers() map[SavePointer]int {
pointers[pGardenData] = 26424
pointers[pRP] = 26614
}
if _config.ErupeConfig.ClientID == _config.G5 {
if config.GetConfig().ClientID == config.G5 {
pointers[lBookshelfData] = 5548
} else if _config.ErupeConfig.ClientID <= _config.GG {
} else if config.GetConfig().ClientID <= config.GG {
pointers[lBookshelfData] = 4520
}
return pointers
}
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
result, err := database.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
if err != nil {
s.Logger.Error("Failed to get savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
defer result.Close()
if !result.Next() {
err = errors.New("no savedata found")
s.logger.Error("No savedata found", zap.Uint32("charID", charID))
s.Logger.Error("No savedata found", zap.Uint32("charID", charID))
return nil, err
}
@@ -136,7 +142,7 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
}
err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil {
s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
s.Logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
@@ -146,7 +152,7 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
err = saveData.Decompress()
if err != nil {
s.logger.Error("Failed to decompress savedata", zap.Error(err))
s.Logger.Error("Failed to decompress savedata", zap.Error(err))
return nil, err
}
@@ -156,6 +162,10 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
}
func (save *CharacterSaveData) Save(s *Session) {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if !s.kqfOverride {
s.kqf = save.KQF
} else {
@@ -164,10 +174,10 @@ func (save *CharacterSaveData) Save(s *Session) {
save.updateSaveDataWithStruct()
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
err := save.Compress()
if err != nil {
s.logger.Error("Failed to compress savedata", zap.Error(err))
s.Logger.Error("Failed to compress savedata", zap.Error(err))
return
}
} else {
@@ -175,14 +185,14 @@ func (save *CharacterSaveData) Save(s *Session) {
save.compSave = save.decompSave
}
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
_, err = database.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
`, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
if err != nil {
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
s.Logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
}
s.server.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7
`, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.charID)
database.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7
`, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.CharID)
}
func (save *CharacterSaveData) Compress() error {
@@ -207,10 +217,10 @@ func (save *CharacterSaveData) Decompress() error {
func (save *CharacterSaveData) updateSaveDataWithStruct() {
rpBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(rpBytes, save.RP)
if _config.ErupeConfig.ClientID >= _config.F4 {
if config.GetConfig().ClientID >= config.F4 {
copy(save.decompSave[save.Pointers[pRP]:save.Pointers[pRP]+2], rpBytes)
}
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
copy(save.decompSave[save.Pointers[pKQF]:save.Pointers[pKQF]+8], save.KQF)
}
}
@@ -224,7 +234,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
save.Gender = false
}
if !save.IsNewCharacter {
if _config.ErupeConfig.ClientID >= _config.S6 {
if config.GetConfig().ClientID >= config.S6 {
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
@@ -235,12 +245,12 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
save.HR = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHR] : save.Pointers[pHR]+2])
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
if save.HR == uint16(999) {
save.GR = grpToGR(int(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])))
}
}
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8]
}
}
@@ -250,5 +260,5 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSexChanger)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -3,7 +3,9 @@ package channelserver
import (
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/stringsupport"
"fmt"
"go.uber.org/zap"
)
@@ -11,15 +13,15 @@ import (
func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnumerateClient)
s.server.stagesLock.RLock()
stage, ok := s.server.stages[pkt.StageID]
s.Server.stagesLock.RLock()
stage, ok := s.Server.stages[pkt.StageID]
if !ok {
s.server.stagesLock.RUnlock()
s.logger.Warn("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID))
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
s.Server.stagesLock.RUnlock()
s.Logger.Warn("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
s.server.stagesLock.RUnlock()
s.Server.stagesLock.RUnlock()
// Read-lock the stage and make the response with all of the charID's in the stage.
resp := byteframe.NewByteFrame()
@@ -52,23 +54,26 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
}
stage.RUnlock()
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
s.logger.Debug("MsgSysEnumerateClient Done!")
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
s.Logger.Debug("MsgSysEnumerateClient Done!")
}
func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfListMember)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var csv string
var count uint32
resp := byteframe.NewByteFrame()
resp.WriteUint32(0) // Blacklist count
err := s.server.db.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.charID).Scan(&csv)
err = database.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.CharID).Scan(&csv)
if err == nil {
cids := stringsupport.CSVElems(csv)
for _, cid := range cids {
var name string
err = s.server.db.QueryRow("SELECT name FROM characters WHERE id=$1", cid).Scan(&name)
err = database.QueryRow("SELECT name FROM characters WHERE id=$1", cid).Scan(&name)
if err != nil {
continue
}
@@ -80,36 +85,40 @@ func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) {
}
resp.Seek(0, 0)
resp.WriteUint32(count)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfOprMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOprMember)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var csv string
for _, cid := range pkt.CharIDs {
if pkt.Blacklist {
err := s.server.db.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.charID).Scan(&csv)
err := database.QueryRow("SELECT blocked FROM characters WHERE id=$1", s.CharID).Scan(&csv)
if err == nil {
if pkt.Operation {
csv = stringsupport.CSVRemove(csv, int(cid))
} else {
csv = stringsupport.CSVAdd(csv, int(cid))
}
s.server.db.Exec("UPDATE characters SET blocked=$1 WHERE id=$2", csv, s.charID)
database.Exec("UPDATE characters SET blocked=$1 WHERE id=$2", csv, s.CharID)
}
} else { // Friendlist
err := s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&csv)
err := database.QueryRow("SELECT friends FROM characters WHERE id=$1", s.CharID).Scan(&csv)
if err == nil {
if pkt.Operation {
csv = stringsupport.CSVRemove(csv, int(cid))
} else {
csv = stringsupport.CSVAdd(csv, int(cid))
}
s.server.db.Exec("UPDATE characters SET friends=$1 WHERE id=$2", csv, s.charID)
database.Exec("UPDATE characters SET friends=$1 WHERE id=$2", csv, s.CharID)
}
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfShutClient(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -1,7 +1,8 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/mhfmon"
"erupe-ce/utils/stringsupport"
@@ -21,9 +22,13 @@ import (
func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavedata)
characterSaveData, err := GetCharacterSaveData(s, s.charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("failed to retrieve character save data from db", zap.Error(err), zap.Uint32("charID", s.charID))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
characterSaveData, err := GetCharacterSaveData(s, s.CharID)
if err != nil {
s.Logger.Error("failed to retrieve character save data from db", zap.Error(err), zap.Uint32("charID", s.CharID))
return
}
// Var to hold the decompressed savedata for updating the launcher response fields.
@@ -32,26 +37,26 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
// diffs themselves are also potentially compressed
diff, err := nullcomp.Decompress(pkt.RawDataPayload)
if err != nil {
s.logger.Error("Failed to decompress diff", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to decompress diff", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
// Perform diff.
s.logger.Info("Diffing...")
s.Logger.Info("Diffing...")
characterSaveData.decompSave = deltacomp.ApplyDataDiff(diff, characterSaveData.decompSave)
} else {
dumpSaveData(s, pkt.RawDataPayload, "savedata")
// Regular blob update.
saveData, err := nullcomp.Decompress(pkt.RawDataPayload)
if err != nil {
s.logger.Error("Failed to decompress savedata from packet", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to decompress savedata from packet", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
if s.server.erupeConfig.SaveDumps.RawEnabled {
if config.GetConfig().SaveDumps.RawEnabled {
dumpSaveData(s, saveData, "raw-savedata")
}
s.logger.Info("Updating save with blob")
s.Logger.Info("Updating save with blob")
characterSaveData.decompSave = saveData
}
characterSaveData.updateStructWithSaveData()
@@ -61,22 +66,22 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
s.Name = characterSaveData.Name
}
if characterSaveData.Name == s.Name || _config.ErupeConfig.ClientID <= _config.S10 {
if characterSaveData.Name == s.Name || config.GetConfig().ClientID <= config.S10 {
characterSaveData.Save(s)
s.logger.Info("Wrote recompressed savedata back to DB.")
s.Logger.Info("Wrote recompressed savedata back to DB.")
} else {
s.rawConn.Close()
s.logger.Warn("Save cancelled due to corruption.")
if s.server.erupeConfig.DeleteOnSaveCorruption {
s.server.db.Exec("UPDATE characters SET deleted=true WHERE id=$1", s.charID)
s.Logger.Warn("Save cancelled due to corruption.")
if config.GetConfig().DeleteOnSaveCorruption {
database.Exec("UPDATE characters SET deleted=true WHERE id=$1", s.CharID)
}
return
}
_, err = s.server.db.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.charID)
_, err = database.Exec("UPDATE characters SET name=$1 WHERE id=$2", characterSaveData.Name, s.CharID)
if err != nil {
s.logger.Error("Failed to update character name in db", zap.Error(err))
s.Logger.Error("Failed to update character name in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func grpToGR(n int) uint16 {
@@ -115,83 +120,95 @@ func grpToGR(n int) uint16 {
}
func dumpSaveData(s *Session, data []byte, suffix string) {
if !s.server.erupeConfig.SaveDumps.Enabled {
if !config.GetConfig().SaveDumps.Enabled {
return
} else {
dir := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID))
path := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix))
dir := filepath.Join(config.GetConfig().SaveDumps.OutputDir, fmt.Sprintf("%d", s.CharID))
path := filepath.Join(config.GetConfig().SaveDumps.OutputDir, fmt.Sprintf("%d", s.CharID), fmt.Sprintf("%d_%s.bin", s.CharID, suffix))
_, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
s.logger.Error("Error dumping savedata, could not create folder")
s.Logger.Error("Error dumping savedata, could not create folder")
return
}
} else {
s.logger.Error("Error dumping savedata")
s.Logger.Error("Error dumping savedata")
return
}
}
err = os.WriteFile(path, data, 0644)
if err != nil {
s.logger.Error("Error dumping savedata, could not write file", zap.Error(err))
s.Logger.Error("Error dumping savedata, could not write file", zap.Error(err))
}
}
}
func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoaddata)
if _, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, "save_override.bin")); err == nil {
data, _ := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, "save_override.bin"))
doAckBufSucceed(s, pkt.AckHandle, data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if _, err := os.Stat(filepath.Join(config.GetConfig().BinPath, "save_override.bin")); err == nil {
data, _ := os.ReadFile(filepath.Join(config.GetConfig().BinPath, "save_override.bin"))
DoAckBufSucceed(s, pkt.AckHandle, data)
return
}
var data []byte
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
err = database.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil || len(data) == 0 {
s.logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.charID), zap.Error(err))
s.Logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.CharID), zap.Error(err))
s.rawConn.Close() // Terminate the connection
return
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
decompSaveData, err := nullcomp.Decompress(data)
if err != nil {
s.logger.Error("Failed to decompress savedata", zap.Error(err))
s.Logger.Error("Failed to decompress savedata", zap.Error(err))
}
bf := byteframe.NewByteFrameFromBytes(decompSaveData)
bf.Seek(88, io.SeekStart)
name := bf.ReadNullTerminatedBytes()
s.server.userBinaryPartsLock.Lock()
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(name, []byte{0x00}...)
s.server.userBinaryPartsLock.Unlock()
s.Server.userBinaryPartsLock.Lock()
s.Server.userBinaryParts[userBinaryPartID{charID: s.CharID, index: 1}] = append(name, []byte{0x00}...)
s.Server.userBinaryPartsLock.Unlock()
s.Name = stringsupport.SJISToUTF8(name)
}
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveScenarioData)
dumpSaveData(s, pkt.RawDataPayload, "scenario")
_, err := s.server.db.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to update scenario data in db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
dumpSaveData(s, pkt.RawDataPayload, "scenario")
_, err = database.Exec("UPDATE characters SET scenariodata = $1 WHERE id = $2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.Logger.Error("Failed to update scenario data in db", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadScenarioData)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var scenarioData []byte
bf := byteframe.NewByteFrame()
err := s.server.db.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.charID).Scan(&scenarioData)
err = database.QueryRow("SELECT scenariodata FROM characters WHERE id = $1", s.CharID).Scan(&scenarioData)
if err != nil || len(scenarioData) < 10 {
s.logger.Error("Failed to load scenariodata", zap.Error(err))
s.Logger.Error("Failed to load scenariodata", zap.Error(err))
bf.WriteBytes(make([]byte, 10))
} else {
bf.WriteBytes(scenarioData)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
var paperGiftData = map[uint32][]PaperGift{
@@ -1509,7 +1526,7 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
}
default:
if pkt.Unk2 < 1000 {
s.logger.Info("PaperData request for unknown type", zap.Uint32("Unk2", pkt.Unk2))
s.Logger.Info("PaperData request for unknown type", zap.Uint32("Unk2", pkt.Unk2))
}
}
@@ -1518,7 +1535,7 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
if ok {
paperGift = paperGiftData[pkt.Unk2]
} else {
s.logger.Info("PaperGift request for unknown type", zap.Uint32("Unk2", pkt.Unk2))
s.Logger.Info("PaperGift request for unknown type", zap.Uint32("Unk2", pkt.Unk2))
}
for _, gift := range paperGift {
bf := byteframe.NewByteFrame()
@@ -1528,7 +1545,7 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(gift.Unk3)
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
} else if pkt.Unk2 == 0 {
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(paperMissions.Timetables)))
@@ -1546,7 +1563,7 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(mdata.Reward2ID)
bf.WriteUint8(mdata.Reward2Quantity)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
} else {
for _, pdata := range paperData {
bf := byteframe.NewByteFrame()
@@ -1559,7 +1576,7 @@ func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt16(pdata.Unk6)
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
}

View File

@@ -1,10 +1,12 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
ps "erupe-ce/utils/pascalstring"
"fmt"
"time"
"go.uber.org/zap"
@@ -32,7 +34,11 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
var itemDists []Distribution
bf := byteframe.NewByteFrame()
rows, err := s.server.db.Queryx(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`
SELECT d.id, event_name, description, COALESCE(rights, 0) AS rights, COALESCE(selection, false) AS selection, times_acceptable,
COALESCE(min_hr, -1) AS min_hr, COALESCE(max_hr, -1) AS max_hr,
COALESCE(min_sr, -1) AS min_sr, COALESCE(max_sr, -1) AS max_sr,
@@ -44,7 +50,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
COALESCE(deadline, TO_TIMESTAMP(0)) AS deadline
FROM distribution d
WHERE character_id = $1 AND type = $2 OR character_id IS NULL AND type = $2 ORDER BY id DESC
`, s.charID, pkt.DistType)
`, s.CharID, pkt.DistType)
if err == nil {
var itemDist Distribution
@@ -64,7 +70,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(dist.Rights)
bf.WriteUint16(dist.TimesAcceptable)
bf.WriteUint16(dist.TimesAccepted)
if _config.ErupeConfig.ClientID >= _config.G9 {
if config.GetConfig().ClientID >= config.G9 {
bf.WriteUint16(0) // Unk
}
bf.WriteInt16(dist.MinHR)
@@ -73,29 +79,29 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt16(dist.MaxSR)
bf.WriteInt16(dist.MinGR)
bf.WriteInt16(dist.MaxGR)
if _config.ErupeConfig.ClientID >= _config.G7 {
if config.GetConfig().ClientID >= config.G7 {
bf.WriteUint8(0) // Unk
}
if _config.ErupeConfig.ClientID >= _config.G6 {
if config.GetConfig().ClientID >= config.G6 {
bf.WriteUint16(0) // Unk
}
if _config.ErupeConfig.ClientID >= _config.G8 {
if config.GetConfig().ClientID >= config.G8 {
if dist.Selection {
bf.WriteUint8(2) // Selection
} else {
bf.WriteUint8(0)
}
}
if _config.ErupeConfig.ClientID >= _config.G7 {
if config.GetConfig().ClientID >= config.G7 {
bf.WriteUint16(0) // Unk
bf.WriteUint16(0) // Unk
}
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
bf.WriteUint8(0) // Unk
}
ps.Uint8(bf, dist.EventName, true)
k := 6
if _config.ErupeConfig.ClientID >= _config.G8 {
if config.GetConfig().ClientID >= config.G8 {
k = 13
}
for i := 0; i < 6; i++ {
@@ -104,7 +110,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(0)
}
}
if _config.ErupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
i := uint8(0)
bf.WriteUint8(i)
if i <= 10 {
@@ -116,7 +122,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) {
}
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type DistributionItem struct {
@@ -127,8 +133,12 @@ type DistributionItem struct {
}
func getDistributionItems(s *Session, i uint32) []DistributionItem {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var distItems []DistributionItem
rows, err := s.server.db.Queryx(`SELECT id, item_type, COALESCE(item_id, 0) AS item_id, COALESCE(quantity, 0) AS quantity FROM distribution_items WHERE distribution_id=$1`, i)
rows, err := database.Queryx(`SELECT id, item_type, COALESCE(item_id, 0) AS item_id, COALESCE(quantity, 0) AS quantity FROM distribution_items WHERE distribution_id=$1`, i)
if err == nil {
var distItem DistributionItem
for rows.Next() {
@@ -152,17 +162,21 @@ func handleMsgMhfApplyDistItem(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(item.ItemType)
bf.WriteUint32(item.ItemID)
bf.WriteUint32(item.Quantity)
if _config.ErupeConfig.ClientID >= _config.G8 {
if config.GetConfig().ClientID >= config.G8 {
bf.WriteUint32(item.ID)
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireDistItem)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.DistributionID > 0 {
_, err := s.server.db.Exec(`INSERT INTO public.distributions_accepted VALUES ($1, $2)`, pkt.DistributionID, s.charID)
_, err := database.Exec(`INSERT INTO public.distributions_accepted VALUES ($1, $2)`, pkt.DistributionID, s.CharID)
if err == nil {
distItems := getDistributionItems(s, pkt.DistributionID)
for _, item := range distItems {
@@ -170,13 +184,13 @@ func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) {
case 17:
_ = addPointNetcafe(s, int(item.Quantity))
case 19:
s.server.db.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
database.Exec("UPDATE users u SET gacha_premium=gacha_premium+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.CharID)
case 20:
s.server.db.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
database.Exec("UPDATE users u SET gacha_trial=gacha_trial+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.CharID)
case 21:
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.charID)
database.Exec("UPDATE users u SET frontier_points=frontier_points+$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", item.Quantity, s.CharID)
case 23:
saveData, err := GetCharacterSaveData(s, s.charID)
saveData, err := GetCharacterSaveData(s, s.CharID)
if err == nil {
saveData.RP += uint16(item.Quantity)
saveData.Save(s)
@@ -185,20 +199,24 @@ func handleMsgMhfAcquireDistItem(s *Session, p mhfpacket.MHFPacket) {
}
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetDistDescription(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetDistDescription)
var desc string
err := s.server.db.QueryRow("SELECT description FROM distribution WHERE id = $1", pkt.DistributionID).Scan(&desc)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Error parsing item distribution description", zap.Error(err))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var desc string
err = database.QueryRow("SELECT description FROM distribution WHERE id = $1", pkt.DistributionID).Scan(&desc)
if err != nil {
s.Logger.Error("Error parsing item distribution description", zap.Error(err))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf := byteframe.NewByteFrame()
ps.Uint16(bf, desc, true)
ps.Uint16(bf, "", false)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -2,9 +2,11 @@ package channelserver
import (
"encoding/hex"
_config "erupe-ce/config"
config "erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
"fmt"
"time"
"erupe-ce/network/mhfpacket"
@@ -12,10 +14,18 @@ import (
)
func cleanupDiva(s *Session) {
s.server.db.Exec("DELETE FROM events WHERE event_type='diva'")
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("DELETE FROM events WHERE event_type='diva'")
}
func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
timestamps := make([]uint32, 6)
midnight := gametime.TimeMidnight()
if debug && start <= 3 {
@@ -49,7 +59,7 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
cleanupDiva(s)
// Generate a new diva defense, starting midnight tomorrow
start = uint32(midnight.Add(24 * time.Hour).Unix())
s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('diva', to_timestamp($1)::timestamp without time zone)", start)
database.Exec("INSERT INTO events (event_type, start_time) VALUES ('diva', to_timestamp($1)::timestamp without time zone)", start)
}
timestamps[0] = start
timestamps[1] = timestamps[0] + 601200
@@ -63,29 +73,32 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdSchedule)
bf := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
id, start := uint32(0xCAFEBEEF), uint32(0)
rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='diva'")
rows, _ := database.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='diva'")
for rows.Next() {
rows.Scan(&id, &start)
}
var timestamps []uint32
if s.server.erupeConfig.DebugOptions.DivaOverride >= 0 {
if s.server.erupeConfig.DebugOptions.DivaOverride == 0 {
if s.server.erupeConfig.ClientID >= _config.Z2 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
if config.GetConfig().DebugOptions.DivaOverride >= 0 {
if config.GetConfig().DebugOptions.DivaOverride == 0 {
if config.GetConfig().ClientID >= config.Z2 {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 32))
}
return
}
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.DivaOverride), true)
timestamps = generateDivaTimestamps(s, uint32(config.GetConfig().DebugOptions.DivaOverride), true)
} else {
timestamps = generateDivaTimestamps(s, start, false)
}
if s.server.erupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
bf.WriteUint32(id)
}
for i := range timestamps {
@@ -97,7 +110,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(0x02) // Unk 00000010
bf.WriteUint16(0x02) // Unk 00000010
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) {
@@ -117,45 +130,45 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(uint32(udInfo.EndTime.Unix()))
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetKijuInfo)
// Temporary canned response
data, _ := hex.DecodeString("04965C959782CC8B468EEC00000000000000000000000000000000000000000000815C82A082E782B582DC82A982BA82CC82AB82B682E3815C0A965C959782C682CD96D282E98E7682A281420A95B782AD8ED282C997458B4382F0975E82A682E98142000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001018BAD8C8282CC8B468EEC00000000000000000000000000000000000000000000815C82AB82E582A482B082AB82CC82AB82B682E3815C0A8BAD8C8282C682CD8BAD82A290BA904681420A95B782AD8ED282CC97CD82F08CA482AC909F82DC82B78142200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003138C8B8F5782CC8B468EEC00000000000000000000000000000000000000000000815C82AF82C182B582E382A482CC82AB82B682E3815C0A8C8B8F5782C682CD8A6D8CC582BD82E9904D978A81420A8F5782DF82E982D982C782C98EEB906C82BD82BF82CC90B8905F97CD82C682C882E9814200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041189CC8CEC82CC8B468EEC00000000000000000000000000000000000000000000815C82A482BD82DC82E082E882CC82AB82B682E3815C0A89CC8CEC82C682CD89CC955082CC8CEC82E881420A8F5782DF82E982D982C782C98EEB906C82BD82BF82CC8E7882A682C682C882E9814220000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000212")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSetKiju(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetKiju)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfAddUdPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAddUdPoint)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetUdMyPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdMyPoint)
// Temporary canned response
data, _ := hex.DecodeString("00040000013C000000FA000000000000000000040000007E0000003C02000000000000000000000000000000000000000000000000000002000004CC00000438000000000000000000000000000000000000000000000000000000020000026E00000230000000000000000000020000007D0000007D000000000000000000000000000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfGetUdTotalPointInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdTotalPointInfo)
// Temporary canned response
data, _ := hex.DecodeString("00000000000007A12000000000000F424000000000001E848000000000002DC6C000000000003D090000000000004C4B4000000000005B8D8000000000006ACFC000000000007A1200000000000089544000000000009896800000000000E4E1C00000000001312D0000000000017D78400000000001C9C3800000000002160EC00000000002625A000000000002AEA5400000000002FAF0800000000003473BC0000000000393870000000000042C1D800000000004C4B40000000000055D4A800000000005F5E10000000000008954400000000001C9C3800000000003473BC00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001020300000000000000000000000000000000000000000000000000000000000000000000000000000000101F1420")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfGetUdSelectedColorInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdSelectedColorInfo)
// Unk
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x02, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x01, 0x01, 0x01, 0x02, 0x03, 0x02, 0x00, 0x00})
}
func handleMsgMhfGetUdMonsterPoint(s *Session, p mhfpacket.MHFPacket) {
@@ -287,36 +300,36 @@ func handleMsgMhfGetUdMonsterPoint(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint16(mp.Points)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfGetUdDailyPresentList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdDailyPresentList)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfGetUdNormaPresentList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdNormaPresentList)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfAcquireUdItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireUdItem)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetUdRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdRanking)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdMyRanking)
// Temporary canned response
data, _ := hex.DecodeString("00000515000005150000CEB4000003CE000003CE0000CEB44D49444E494748542D414E47454C0000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}

View File

@@ -1,9 +1,11 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/token"
"fmt"
"math"
"time"
@@ -45,7 +47,7 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type activeFeature struct {
@@ -55,7 +57,10 @@ type activeFeature struct {
func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var features []activeFeature
times := []time.Time{
gametime.TimeMidnight().Add(-24 * time.Hour),
@@ -65,12 +70,12 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
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)
err := database.QueryRowx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1`, t).StructScan(&temp)
if err != nil || temp.StartTime.IsZero() {
weapons := token.RNG.Intn(s.server.erupeConfig.GameplayOptions.MaxFeatureWeapons-s.server.erupeConfig.GameplayOptions.MinFeatureWeapons+1) + s.server.erupeConfig.GameplayOptions.MinFeatureWeapons
weapons := token.RNG.Intn(config.GetConfig().GameplayOptions.MaxFeatureWeapons-config.GetConfig().GameplayOptions.MinFeatureWeapons+1) + config.GetConfig().GameplayOptions.MinFeatureWeapons
temp = generateFeatureWeapons(weapons)
temp.StartTime = t
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
database.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
}
features = append(features, temp)
}
@@ -83,18 +88,18 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(feature.ActiveFeatures)
bf.WriteUint16(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func generateFeatureWeapons(count int) activeFeature {
_max := 14
if _config.ErupeConfig.ClientID < _config.ZZ {
if config.GetConfig().ClientID < config.ZZ {
_max = 13
}
if _config.ErupeConfig.ClientID < _config.G10 {
if config.GetConfig().ClientID < config.G10 {
_max = 12
}
if _config.ErupeConfig.ClientID < _config.GG {
if config.GetConfig().ClientID < config.GG {
_max = 11
}
if count > _max {
@@ -131,14 +136,17 @@ type loginBoost struct {
func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetKeepLoginBoostStatus)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", 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 {
rows, err := database.Queryx("SELECT week_req, expiration, reset FROM login_boost WHERE char_id=$1 ORDER BY week_req", s.CharID)
if err != nil || config.GetConfig().GameplayOptions.DisableLoginBoost {
rows.Close()
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 35))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 35))
return
}
for rows.Next() {
@@ -156,7 +164,7 @@ func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
{WeekReq: 5, Expiration: temp},
}
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{})
database.Exec(`INSERT INTO login_boost VALUES ($1, $2, $3, $4)`, s.CharID, boost.WeekReq, boost.Expiration, time.Time{})
}
}
@@ -165,7 +173,7 @@ func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
if !boost.Reset.IsZero() && boost.Reset.Before(gametime.TimeAdjusted()) {
boost.Expiration = gametime.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)
database.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)
}
boost.WeekCount = uint8((gametime.TimeAdjusted().Unix()-boost.Expiration.Unix())/604800 + 1)
@@ -191,7 +199,7 @@ func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {
@@ -208,13 +216,17 @@ func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {
expiration = gametime.TimeAdjusted().Add(240 * time.Minute)
}
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, gametime.TimeWeekNext(), s.charID, pkt.BoostWeekUsed)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`UPDATE login_boost SET expiration=$1, reset=$2 WHERE char_id=$3 AND week_req=$4`, expiration, gametime.TimeWeekNext(), s.CharID, pkt.BoostWeekUsed)
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetRestrictionEvent)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -1,26 +1,36 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
ps "erupe-ce/utils/pascalstring"
"erupe-ce/utils/token"
"fmt"
"sort"
"time"
)
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})
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.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)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
s.server.db.QueryRow(`SELECT mezfes FROM characters WHERE id=$1`, s.charID).Scan(&data)
database.QueryRow(`SELECT mezfes FROM characters WHERE id=$1`, s.CharID).Scan(&data)
bf := byteframe.NewByteFrame()
if len(data) > 0 {
bf.WriteBytes(data)
@@ -31,13 +41,13 @@ func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(0)
bf.WriteUint32(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateRanking)
bf := byteframe.NewByteFrame()
state := s.server.erupeConfig.DebugOptions.TournamentOverride
state := config.GetConfig().DebugOptions.TournamentOverride
// Unk
// Unk
// Start?
@@ -64,7 +74,7 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(uint32(gametime.TimeAdjusted().Unix())) // TS Current Time
bf.WriteUint8(3)
bf.WriteBytes(make([]byte, 4))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
bf.WriteUint32(uint32(gametime.TimeAdjusted().Unix())) // TS Current Time
@@ -90,18 +100,26 @@ func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
psUint16 desc
*/
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func cleanupFesta(s *Session) {
s.server.db.Exec("DELETE FROM events WHERE event_type='festa'")
s.server.db.Exec("DELETE FROM festa_registrations")
s.server.db.Exec("DELETE FROM festa_submissions")
s.server.db.Exec("DELETE FROM festa_prizes_accepted")
s.server.db.Exec("UPDATE guild_characters SET trial_vote=NULL")
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("DELETE FROM events WHERE event_type='festa'")
database.Exec("DELETE FROM festa_registrations")
database.Exec("DELETE FROM festa_submissions")
database.Exec("DELETE FROM festa_prizes_accepted")
database.Exec("UPDATE guild_characters SET trial_vote=NULL")
}
func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
timestamps := make([]uint32, 5)
midnight := gametime.TimeMidnight()
if debug && start <= 3 {
@@ -132,7 +150,7 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
cleanupFesta(s)
// Generate a new festa, starting midnight tomorrow
start = uint32(midnight.Add(24 * time.Hour).Unix())
s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', to_timestamp($1)::timestamp without time zone)", start)
database.Exec("INSERT INTO events (event_type, start_time) VALUES ('festa', to_timestamp($1)::timestamp without time zone)", start)
}
timestamps[0] = start
timestamps[1] = timestamps[0] + 604800
@@ -167,32 +185,35 @@ type FestaReward struct {
func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfInfoFesta)
bf := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
id, start := uint32(0xDEADBEEF), uint32(0)
rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='festa'")
rows, _ := database.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='festa'")
for rows.Next() {
rows.Scan(&id, &start)
}
var timestamps []uint32
if s.server.erupeConfig.DebugOptions.FestaOverride >= 0 {
if s.server.erupeConfig.DebugOptions.FestaOverride == 0 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
if config.GetConfig().DebugOptions.FestaOverride >= 0 {
if config.GetConfig().DebugOptions.FestaOverride == 0 {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.FestaOverride), true)
timestamps = generateFestaTimestamps(s, uint32(config.GetConfig().DebugOptions.FestaOverride), true)
} else {
timestamps = generateFestaTimestamps(s, start, false)
}
if timestamps[0] > uint32(gametime.TimeAdjusted().Unix()) {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
var blueSouls, redSouls uint32
s.server.db.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'blue'`).Scan(&blueSouls)
s.server.db.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'red'`).Scan(&redSouls)
database.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'blue'`).Scan(&blueSouls)
database.QueryRow(`SELECT COALESCE(SUM(fs.souls), 0) AS souls FROM festa_registrations fr LEFT JOIN festa_submissions fs ON fr.guild_id = fs.guild_id AND fr.team = 'red'`).Scan(&redSouls)
bf.WriteUint32(id)
for _, timestamp := range timestamps {
@@ -207,7 +228,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
var trials []FestaTrial
var trial FestaTrial
rows, _ = s.server.db.Queryx(`SELECT ft.*,
rows, _ = database.Queryx(`SELECT ft.*,
COALESCE(CASE
WHEN COUNT(gc.id) FILTER (WHERE fr.team = 'blue' AND gc.trial_vote = ft.id) >
COUNT(gc.id) FILTER (WHERE fr.team = 'red' AND gc.trial_vote = ft.id)
@@ -236,7 +257,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(trial.Locale)
bf.WriteUint16(trial.Reward)
bf.WriteInt16(FestivalColorCodes[trial.Monopoly])
if _config.ErupeConfig.ClientID >= _config.F4 { // Not in S6.0
if config.GetConfig().ClientID >= config.F4 { // Not in S6.0
bf.WriteUint16(trial.Unk)
}
}
@@ -279,19 +300,19 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(reward.Quantity)
bf.WriteUint16(reward.ItemID)
// Not confirmed to be G1 but exists in G3
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
bf.WriteUint16(reward.Unk5)
bf.WriteUint16(reward.Unk6)
bf.WriteUint8(reward.Unk7)
}
}
if _config.ErupeConfig.ClientID <= _config.G61 {
if s.server.erupeConfig.GameplayOptions.MaximumFP > 0xFFFF {
s.server.erupeConfig.GameplayOptions.MaximumFP = 0xFFFF
if config.GetConfig().ClientID <= config.G61 {
if config.GetConfig().GameplayOptions.MaximumFP > 0xFFFF {
config.GetConfig().GameplayOptions.MaximumFP = 0xFFFF
}
bf.WriteUint16(uint16(s.server.erupeConfig.GameplayOptions.MaximumFP))
bf.WriteUint16(uint16(config.GetConfig().GameplayOptions.MaximumFP))
} else {
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP)
bf.WriteUint32(config.GetConfig().GameplayOptions.MaximumFP)
}
bf.WriteUint16(100) // Reward multiplier (%)
@@ -301,7 +322,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
var guildID uint32
var guildName string
var guildTeam = FestivalColorNone
s.server.db.QueryRow(`
database.QueryRow(`
SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _
FROM festa_submissions fs
LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id
@@ -321,7 +342,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
var guildName string
var guildTeam = FestivalColorNone
offset := 86400 * uint32(i)
s.server.db.QueryRow(`
database.QueryRow(`
SELECT fs.guild_id, g.name, fr.team, SUM(fs.souls) as _
FROM festa_submissions fs
LEFT JOIN festa_registrations fr ON fs.guild_id = fr.guild_id
@@ -348,27 +369,31 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(100) // Normal rate
bf.WriteUint16(50) // 50% penalty
if _config.ErupeConfig.ClientID >= _config.G52 {
if config.GetConfig().ClientID >= config.G52 {
ps.Uint16(bf, "", false)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// state festa (U)ser
func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateFestaU)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
applicant := false
if guild != nil {
applicant, _ = guild.HasApplicationForCharID(s, s.charID)
applicant, _ = guild.HasApplicationForCharID(s, s.CharID)
}
if err != nil || guild == nil || applicant {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
var souls, exists uint32
s.server.db.QueryRow(`SELECT COALESCE((SELECT SUM(souls) FROM festa_submissions WHERE character_id=$1), 0)`, 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)
database.QueryRow(`SELECT COALESCE((SELECT SUM(souls) FROM festa_submissions WHERE character_id=$1), 0)`, s.CharID).Scan(&souls)
err = database.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)
if err != nil {
@@ -378,16 +403,16 @@ func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {
bf.WriteBool(false)
bf.WriteBool(true)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// state festa (G)uild
func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateFestaG)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
applicant := false
if guild != nil {
applicant, _ = guild.HasApplicationForCharID(s, s.charID)
applicant, _ = guild.HasApplicationForCharID(s, s.CharID)
}
resp := byteframe.NewByteFrame()
if err != nil || guild == nil || applicant {
@@ -396,7 +421,7 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
resp.WriteInt32(-1)
resp.WriteInt32(0)
resp.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
return
}
resp.WriteUint32(guild.Souls)
@@ -404,19 +429,19 @@ func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {
resp.WriteInt32(1) // unk, rank?
resp.WriteInt32(1) // unk
resp.WriteInt32(1) // unk
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateFestaMember)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
members, err := GetGuildMembers(s, guild.ID, false)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
sort.Slice(members, func(i, j int) bool {
@@ -433,70 +458,94 @@ func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(0) // Unk
for _, member := range validMembers {
bf.WriteUint32(member.CharID)
if _config.ErupeConfig.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
bf.WriteUint16(uint16(member.Souls))
bf.WriteUint16(0)
} else {
bf.WriteUint32(member.Souls)
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfVoteFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfVoteFesta)
s.server.db.Exec(`UPDATE guild_characters SET trial_vote=$1 WHERE character_id=$2`, pkt.TrialID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`UPDATE guild_characters SET trial_vote=$1 WHERE character_id=$2`, pkt.TrialID, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEntryFesta)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
team := uint32(token.RNG.Intn(2))
switch team {
case 0:
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)
database.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)
case 1:
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'red')", guild.ID)
database.Exec("INSERT INTO festa_registrations VALUES ($1, 'red')", guild.ID)
}
bf := byteframe.NewByteFrame()
bf.WriteUint32(team)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfChargeFesta)
tx, _ := s.server.db.Begin()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
tx, _ := database.Begin()
for i := range pkt.Souls {
if pkt.Souls[i] == 0 {
continue
}
_, _ = tx.Exec(`INSERT INTO festa_submissions VALUES ($1, $2, $3, $4, now())`, s.charID, pkt.GuildID, i, pkt.Souls[i])
_, _ = tx.Exec(`INSERT INTO festa_submissions VALUES ($1, $2, $3, $4, now())`, s.CharID, pkt.GuildID, i, pkt.Souls[i])
}
_ = tx.Commit()
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
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))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("INSERT INTO public.festa_prizes_accepted VALUES (0, $1)", s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAcquireFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireFestaPersonalPrize)
s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAcquireFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireFestaIntermediatePrize)
s.server.db.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("INSERT INTO public.festa_prizes_accepted VALUES ($1, $2)", pkt.PrizeID, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type Prize struct {
@@ -510,7 +559,11 @@ 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 = $1) AS claimed FROM festa_prizes fp WHERE type='personal'`, s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, _ := database.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() {
@@ -531,12 +584,16 @@ func handleMsgMhfEnumerateFestaPersonalPrize(s *Session, p mhfpacket.MHFPacket)
bf := byteframe.NewByteFrame()
bf.WriteUint32(count)
bf.WriteBytes(prizeData.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
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 = $1) AS claimed FROM festa_prizes fp WHERE type='guild'`, s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, _ := database.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() {
@@ -557,5 +614,5 @@ func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPack
bf := byteframe.NewByteFrame()
bf.WriteUint32(count)
bf.WriteBytes(prizeData.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,12 @@
package channelserver
import (
"fmt"
"time"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
@@ -20,13 +22,17 @@ type GuildAdventure struct {
CollectedBy string `db:"collected_by"`
}
func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadGuildAdventure)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
data, err := s.server.db.Queryx("SELECT id, destination, charge, depart, return, collected_by FROM guild_adventures WHERE guild_id = $1", guild.ID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get guild adventures from db", zap.Error(err))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
guild, _ := GetGuildInfoByCharacterId(s, s.CharID)
data, err := database.Queryx("SELECT id, destination, charge, depart, return, collected_by FROM guild_adventures WHERE guild_id = $1", guild.ID)
if err != nil {
s.Logger.Error("Failed to get guild adventures from db", zap.Error(err))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
temp := byteframe.NewByteFrame()
@@ -43,55 +49,71 @@ func handleMsgMhfLoadGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
temp.WriteUint32(adventureData.Charge)
temp.WriteUint32(adventureData.Depart)
temp.WriteUint32(adventureData.Return)
temp.WriteBool(stringsupport.CSVContains(adventureData.CollectedBy, int(s.charID)))
temp.WriteBool(stringsupport.CSVContains(adventureData.CollectedBy, int(s.CharID)))
}
bf := byteframe.NewByteFrame()
bf.WriteUint8(uint8(count))
bf.WriteBytes(temp.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistGuildAdventure(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, gametime.TimeAdjusted().Unix(), gametime.TimeAdjusted().Add(6*time.Hour).Unix())
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to register guild adventure", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
guild, _ := GetGuildInfoByCharacterId(s, s.CharID)
_, err = database.Exec("INSERT INTO guild_adventures (guild_id, destination, depart, return) VALUES ($1, $2, $3, $4)", guild.ID, pkt.Destination, gametime.TimeAdjusted().Unix(), gametime.TimeAdjusted().Add(6*time.Hour).Unix())
if err != nil {
s.Logger.Error("Failed to register guild adventure", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAcquireGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfAcquireGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireGuildAdventure)
var collectedBy string
err := s.server.db.QueryRow("SELECT collected_by FROM guild_adventures WHERE id = $1", pkt.ID).Scan(&collectedBy)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Error parsing adventure collected by", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var collectedBy string
err = database.QueryRow("SELECT collected_by FROM guild_adventures WHERE id = $1", pkt.ID).Scan(&collectedBy)
if err != nil {
s.Logger.Error("Error parsing adventure collected by", zap.Error(err))
} else {
collectedBy = stringsupport.CSVAdd(collectedBy, int(s.charID))
_, err := s.server.db.Exec("UPDATE guild_adventures SET collected_by = $1 WHERE id = $2", collectedBy, pkt.ID)
collectedBy = stringsupport.CSVAdd(collectedBy, int(s.CharID))
_, err := database.Exec("UPDATE guild_adventures SET collected_by = $1 WHERE id = $2", collectedBy, pkt.ID)
if err != nil {
s.logger.Error("Failed to collect adventure in db", zap.Error(err))
s.Logger.Error("Failed to collect adventure in db", zap.Error(err))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfChargeGuildAdventure)
_, err := s.server.db.Exec("UPDATE guild_adventures SET charge = charge + $1 WHERE id = $2", pkt.Amount, pkt.ID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to charge guild adventure", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
_, err = database.Exec("UPDATE guild_adventures SET charge = charge + $1 WHERE id = $2", pkt.Amount, pkt.ID)
if err != nil {
s.Logger.Error("Failed to charge guild adventure", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfRegistGuildAdventureDiva(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, gametime.TimeAdjusted().Unix(), gametime.TimeAdjusted().Add(1*time.Hour).Unix())
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to register guild adventure", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
guild, _ := GetGuildInfoByCharacterId(s, s.CharID)
_, err = database.Exec("INSERT INTO guild_adventures (guild_id, destination, charge, depart, return) VALUES ($1, $2, $3, $4, $5)", guild.ID, pkt.Destination, pkt.Charge, gametime.TimeAdjusted().Unix(), gametime.TimeAdjusted().Add(1*time.Hour).Unix())
if err != nil {
s.Logger.Error("Failed to register guild adventure", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -2,6 +2,7 @@ package channelserver
import (
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
ps "erupe-ce/utils/pascalstring"
"fmt"
"time"
@@ -45,12 +46,16 @@ type GuildAlliance struct {
}
func GetAllianceData(s *Session, AllianceID uint32) (*GuildAlliance, error) {
rows, err := s.server.db.Queryx(fmt.Sprintf(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(fmt.Sprintf(`
%s
WHERE ga.id = $1
`, allianceInfoSelectQuery), AllianceID)
if err != nil {
s.logger.Error("Failed to retrieve alliance data from database", zap.Error(err))
s.Logger.Error("Failed to retrieve alliance data from database", zap.Error(err))
return nil, err
}
defer rows.Close()
@@ -68,13 +73,13 @@ func buildAllianceObjectFromDbResult(result *sqlx.Rows, err error, s *Session) (
err = result.StructScan(alliance)
if err != nil {
s.logger.Error("failed to retrieve alliance from database", zap.Error(err))
s.Logger.Error("failed to retrieve alliance from database", zap.Error(err))
return nil, err
}
parentGuild, err := GetGuildInfoByID(s, alliance.ParentGuildID)
if err != nil {
s.logger.Error("Failed to get parent guild info", zap.Error(err))
s.Logger.Error("Failed to get parent guild info", zap.Error(err))
return nil, err
} else {
alliance.ParentGuild = *parentGuild
@@ -84,7 +89,7 @@ func buildAllianceObjectFromDbResult(result *sqlx.Rows, err error, s *Session) (
if alliance.SubGuild1ID > 0 {
subGuild1, err := GetGuildInfoByID(s, alliance.SubGuild1ID)
if err != nil {
s.logger.Error("Failed to get sub guild 1 info", zap.Error(err))
s.Logger.Error("Failed to get sub guild 1 info", zap.Error(err))
return nil, err
} else {
alliance.SubGuild1 = *subGuild1
@@ -95,7 +100,7 @@ func buildAllianceObjectFromDbResult(result *sqlx.Rows, err error, s *Session) (
if alliance.SubGuild2ID > 0 {
subGuild2, err := GetGuildInfoByID(s, alliance.SubGuild2ID)
if err != nil {
s.logger.Error("Failed to get sub guild 2 info", zap.Error(err))
s.Logger.Error("Failed to get sub guild 2 info", zap.Error(err))
return nil, err
} else {
alliance.SubGuild2 = *subGuild2
@@ -106,92 +111,99 @@ func buildAllianceObjectFromDbResult(result *sqlx.Rows, err error, s *Session) (
return alliance, nil
}
func handleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfCreateJoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCreateJoint)
_, err := s.server.db.Exec("INSERT INTO guild_alliances (name, parent_id) VALUES ($1, $2)", pkt.Name, pkt.GuildID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to create guild alliance in db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x01, 0x01, 0x01})
_, err = database.Exec("INSERT INTO guild_alliances (name, parent_id) VALUES ($1, $2)", pkt.Name, pkt.GuildID)
if err != nil {
s.Logger.Error("Failed to create guild alliance in db", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x01, 0x01, 0x01, 0x01})
}
func handleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfOperateJoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateJoint)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
guild, err := GetGuildInfoByID(s, pkt.GuildID)
if err != nil {
s.logger.Error("Failed to get guild info", zap.Error(err))
s.Logger.Error("Failed to get guild info", zap.Error(err))
}
alliance, err := GetAllianceData(s, pkt.AllianceID)
if err != nil {
s.logger.Error("Failed to get alliance info", zap.Error(err))
s.Logger.Error("Failed to get alliance info", zap.Error(err))
}
switch pkt.Action {
case mhfpacket.OPERATE_JOINT_DISBAND:
if guild.LeaderCharID == s.charID && alliance.ParentGuildID == guild.ID {
_, err = s.server.db.Exec("DELETE FROM guild_alliances WHERE id=$1", alliance.ID)
if guild.LeaderCharID == s.CharID && alliance.ParentGuildID == guild.ID {
_, err = database.Exec("DELETE FROM guild_alliances WHERE id=$1", alliance.ID)
if err != nil {
s.logger.Error("Failed to disband alliance", zap.Error(err))
s.Logger.Error("Failed to disband alliance", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
s.logger.Warn(
s.Logger.Warn(
"Non-owner of alliance attempted disband",
zap.Uint32("CharID", s.charID),
zap.Uint32("CharID", s.CharID),
zap.Uint32("AllyID", alliance.ID),
)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
case mhfpacket.OPERATE_JOINT_LEAVE:
if guild.LeaderCharID == s.charID {
if guild.LeaderCharID == s.CharID {
if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
} else if guild.ID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
} else {
s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
}
// TODO: Handle deleting Alliance applications
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
s.logger.Warn(
s.Logger.Warn(
"Non-owner of guild attempted alliance leave",
zap.Uint32("CharID", s.charID),
zap.Uint32("CharID", s.CharID),
)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
case mhfpacket.OPERATE_JOINT_KICK:
if alliance.ParentGuild.LeaderCharID == s.charID {
if alliance.ParentGuild.LeaderCharID == s.CharID {
kickedGuildID := pkt.Data1.ReadUint32()
if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID > 0 {
s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, alliance.ID)
} else if kickedGuildID == alliance.SubGuild1ID && alliance.SubGuild2ID == 0 {
s.server.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, alliance.ID)
} else {
s.server.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
database.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, alliance.ID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
s.logger.Warn(
s.Logger.Warn(
"Non-owner of alliance attempted kick",
zap.Uint32("CharID", s.charID),
zap.Uint32("CharID", s.CharID),
zap.Uint32("AllyID", alliance.ID),
)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
default:
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
panic(fmt.Sprintf("Unhandled operate joint action '%d'", pkt.Action))
}
}
func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfInfoJoint)
bf := byteframe.NewByteFrame()
alliance, err := GetAllianceData(s, pkt.AllianceID)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
} else {
bf.WriteUint32(alliance.ID)
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
@@ -229,6 +241,6 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
ps.Uint16(bf, alliance.SubGuild2.Name, true)
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
"erupe-ce/utils/db"
"fmt"
"time"
@@ -46,10 +47,14 @@ func (gm *GuildMember) IsSubLeader() bool {
}
func (gm *GuildMember) Save(s *Session) error {
_, err := s.server.db.Exec("UPDATE guild_characters SET avoid_leadership=$1, order_index=$2 WHERE character_id=$3", gm.AvoidLeadership, gm.OrderIndex, gm.CharID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = database.Exec("UPDATE guild_characters SET avoid_leadership=$1, order_index=$2 WHERE character_id=$3", gm.AvoidLeadership, gm.OrderIndex, gm.CharID)
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to update guild member data",
zap.Error(err),
zap.Uint32("charID", gm.CharID),
@@ -87,13 +92,17 @@ SELECT * FROM (
`
func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) {
rows, err := s.server.db.Queryx(fmt.Sprintf(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(fmt.Sprintf(`
%s
WHERE guild_id = $1 AND is_applicant = $2
`, guildMembersSelectSQL), guildID, applicants)
if err != nil {
s.logger.Error("failed to retrieve membership data for guild", zap.Error(err), zap.Uint32("guildID", guildID))
s.Logger.Error("failed to retrieve membership data for guild", zap.Error(err), zap.Uint32("guildID", guildID))
return nil, err
}
@@ -115,10 +124,15 @@ func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMembe
}
func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) {
rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID)
if err != nil {
s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID))
s.Logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID))
return nil, err
}
@@ -139,7 +153,7 @@ func buildGuildMemberObjectFromDBResult(rows *sqlx.Rows, err error, s *Session)
err = rows.StructScan(&memberData)
if err != nil {
s.logger.Error("failed to retrieve guild data from database", zap.Error(err))
s.Logger.Error("failed to retrieve guild data from database", zap.Error(err))
return nil, err
}

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
"fmt"
@@ -11,41 +12,44 @@ import (
"go.uber.org/zap"
)
func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostGuildScout)
actorCharGuildData, err := GetCharacterGuildData(s, s.charID)
actorCharGuildData, err := GetCharacterGuildData(s, s.CharID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
panic(err)
}
if actorCharGuildData == nil || !actorCharGuildData.CanRecruit() {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
guildInfo, err := GetGuildInfoByID(s, actorCharGuildData.GuildID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
panic(err)
}
hasApplication, err := guildInfo.HasApplicationForCharID(s, pkt.CharID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
panic(err)
}
if hasApplication {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x04})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x04})
return
}
transaction, err := s.server.db.Begin()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
transaction, err := database.Begin()
if err != nil {
panic(err)
@@ -55,16 +59,16 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
if err != nil {
rollbackTransaction(s, transaction)
doAckBufFail(s, pkt.AckHandle, nil)
DoAckBufFail(s, pkt.AckHandle, nil)
panic(err)
}
mail := &Mail{
SenderID: s.charID,
SenderID: s.CharID,
RecipientID: pkt.CharID,
Subject: s.server.i18n.guild.invite.title,
Subject: s.Server.i18n.guild.invite.title,
Body: fmt.Sprintf(
s.server.i18n.guild.invite.body,
s.Server.i18n.guild.invite.body,
guildInfo.Name,
),
IsGuildInvite: true,
@@ -74,52 +78,52 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
if err != nil {
rollbackTransaction(s, transaction)
doAckBufFail(s, pkt.AckHandle, nil)
DoAckBufFail(s, pkt.AckHandle, nil)
return
}
err = transaction.Commit()
if err != nil {
doAckBufFail(s, pkt.AckHandle, nil)
DoAckBufFail(s, pkt.AckHandle, nil)
panic(err)
}
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCancelGuildScout)
guildCharData, err := GetCharacterGuildData(s, s.charID)
guildCharData, err := GetCharacterGuildData(s, s.CharID)
if err != nil {
panic(err)
}
if guildCharData == nil || !guildCharData.CanRecruit() {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
guild, err := GetGuildInfoByID(s, guildCharData.GuildID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
err = guild.CancelInvitation(s, pkt.InvitationID)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
return
}
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAnswerGuildScout)
bf := byteframe.NewByteFrame()
guild, err := GetGuildInfoByCharacterId(s, pkt.LeaderID)
@@ -128,84 +132,87 @@ func handleMsgMhfAnswerGuildScout(s *Session, p mhfpacket.MHFPacket) {
panic(err)
}
app, err := guild.GetApplicationForCharID(s, s.charID, GuildApplicationTypeInvited)
app, err := guild.GetApplicationForCharID(s, s.CharID, GuildApplicationTypeInvited)
if app == nil || err != nil {
s.logger.Warn(
s.Logger.Warn(
"Guild invite missing, deleted?",
zap.Error(err),
zap.Uint32("guildID", guild.ID),
zap.Uint32("charID", s.charID),
zap.Uint32("charID", s.CharID),
)
bf.WriteUint32(7)
bf.WriteUint32(guild.ID)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
var mail []Mail
if pkt.Answer {
err = guild.AcceptApplication(s, s.charID)
err = guild.AcceptApplication(s, s.CharID)
mail = append(mail, Mail{
RecipientID: s.charID,
Subject: s.server.i18n.guild.invite.success.title,
Body: fmt.Sprintf(s.server.i18n.guild.invite.success.body, guild.Name),
RecipientID: s.CharID,
Subject: s.Server.i18n.guild.invite.success.title,
Body: fmt.Sprintf(s.Server.i18n.guild.invite.success.body, guild.Name),
IsSystemMessage: true,
})
mail = append(mail, Mail{
SenderID: s.charID,
SenderID: s.CharID,
RecipientID: pkt.LeaderID,
Subject: s.server.i18n.guild.invite.accepted.title,
Body: fmt.Sprintf(s.server.i18n.guild.invite.accepted.body, guild.Name),
Subject: s.Server.i18n.guild.invite.accepted.title,
Body: fmt.Sprintf(s.Server.i18n.guild.invite.accepted.body, guild.Name),
IsSystemMessage: true,
})
} else {
err = guild.RejectApplication(s, s.charID)
err = guild.RejectApplication(s, s.CharID)
mail = append(mail, Mail{
RecipientID: s.charID,
Subject: s.server.i18n.guild.invite.rejected.title,
Body: fmt.Sprintf(s.server.i18n.guild.invite.rejected.body, guild.Name),
RecipientID: s.CharID,
Subject: s.Server.i18n.guild.invite.rejected.title,
Body: fmt.Sprintf(s.Server.i18n.guild.invite.rejected.body, guild.Name),
IsSystemMessage: true,
})
mail = append(mail, Mail{
SenderID: s.charID,
SenderID: s.CharID,
RecipientID: pkt.LeaderID,
Subject: s.server.i18n.guild.invite.declined.title,
Body: fmt.Sprintf(s.server.i18n.guild.invite.declined.body, guild.Name),
Subject: s.Server.i18n.guild.invite.declined.title,
Body: fmt.Sprintf(s.Server.i18n.guild.invite.declined.body, guild.Name),
IsSystemMessage: true,
})
}
if err != nil {
bf.WriteUint32(7)
bf.WriteUint32(guild.ID)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
} else {
bf.WriteUint32(0)
bf.WriteUint32(guild.ID)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
for _, m := range mail {
m.Send(s, nil)
}
}
}
func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildScoutList)
guildInfo, err := GetGuildInfoByCharacterId(s, s.charID)
guildInfo, err := GetGuildInfoByCharacterId(s, s.CharID)
if guildInfo == nil && s.prevGuildID == 0 {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} else {
guildInfo, err = GetGuildInfoByID(s, s.prevGuildID)
if guildInfo == nil || err != nil {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
}
rows, err := s.server.db.Queryx(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`
SELECT c.id, c.name, c.hr, c.gr, ga.actor_id
FROM guild_applications ga
JOIN characters c ON c.id = ga.character_id
@@ -213,8 +220,8 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
`, guildInfo.ID)
if err != nil {
s.logger.Error("failed to retrieve scouted characters", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("failed to retrieve scouted characters", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
@@ -237,7 +244,7 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
err = rows.Scan(&charID, &charName, &HR, &GR, &actorID)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, nil)
DoAckSimpleFail(s, pkt.AckHandle, nil)
continue
}
@@ -262,25 +269,28 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(count)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfGetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetRejectGuildScout)
row := s.server.db.QueryRow("SELECT restrict_guild_scout FROM characters WHERE id=$1", s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := database.QueryRow("SELECT restrict_guild_scout FROM characters WHERE id=$1", s.CharID)
var currentStatus bool
err := row.Scan(&currentStatus)
err = row.Scan(&currentStatus)
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to retrieve character guild scout status",
zap.Error(err),
zap.Uint32("charID", s.charID),
zap.Uint32("charID", s.CharID),
)
doAckSimpleFail(s, pkt.AckHandle, nil)
DoAckSimpleFail(s, pkt.AckHandle, nil)
return
}
@@ -290,23 +300,26 @@ func handleMsgMhfGetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
response = 0x01
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, response})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, response})
}
func handleMsgMhfSetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfSetRejectGuildScout(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSetRejectGuildScout)
_, err := s.server.db.Exec("UPDATE characters SET restrict_guild_scout=$1 WHERE id=$2", pkt.Reject, s.charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = database.Exec("UPDATE characters SET restrict_guild_scout=$1 WHERE id=$2", pkt.Reject, s.CharID)
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to update character guild scout status",
zap.Error(err),
zap.Uint32("charID", s.charID),
zap.Uint32("charID", s.CharID),
)
doAckSimpleFail(s, pkt.AckHandle, nil)
DoAckSimpleFail(s, pkt.AckHandle, nil)
return
}
doAckSimpleSucceed(s, pkt.AckHandle, nil)
DoAckSimpleSucceed(s, pkt.AckHandle, nil)
}

View File

@@ -1,10 +1,13 @@
package channelserver
import (
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
"fmt"
"time"
)
@@ -21,24 +24,27 @@ type TreasureHunt struct {
Claimed bool `db:"claimed"`
}
func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil || guild == nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
var hunts []TreasureHunt
var hunt TreasureHunt
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.MaxHunts {
case 1:
err = s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt)
err = database.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.CharID).StructScan(&hunt)
if err == nil {
hunts = append(hunts, hunt)
}
case 30:
rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data,
rows, err := database.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data,
(SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters,
CASE
WHEN ghc.character_id IS NOT NULL THEN true
@@ -47,15 +53,15 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
FROM guild_hunts gh
LEFT JOIN guild_hunts_claimed ghc ON gh.id = ghc.hunt_id AND ghc.character_id = $1
WHERE gh.guild_id=$2 AND gh.level=2 AND gh.acquired=TRUE
`, s.charID, guild.ID)
`, s.CharID, guild.ID)
if err != nil {
rows.Close()
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} else {
for rows.Next() {
err = rows.StructScan(&hunt)
if err == nil && hunt.Start.Add(time.Second*time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry)).After(gametime.TimeAdjusted()) {
if err == nil && hunt.Start.Add(time.Second*time.Duration(config.GetConfig().GameplayOptions.TreasureHuntExpiry)).After(gametime.TimeAdjusted()) {
hunts = append(hunts, hunt)
}
}
@@ -77,22 +83,22 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
bf.WriteBool(h.Claimed)
bf.WriteBytes(h.HuntData)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildTresure)
bf := byteframe.NewByteFrameFromBytes(pkt.Data)
huntData := byteframe.NewByteFrame()
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil || guild == nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
guildCats := getGuildAirouList(s)
destination := bf.ReadUint32()
level := bf.ReadUint32()
huntData.WriteUint32(s.charID)
huntData.WriteUint32(s.CharID)
huntData.WriteBytes(stringsupport.PaddedString(s.Name, 18, true))
catsUsed := ""
for i := 0; i < 5; i++ {
@@ -109,29 +115,41 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
huntData.WriteBytes(bf.ReadBytes(9))
}
}
s.server.db.Exec(`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6)
`, guild.ID, s.charID, destination, level, huntData.Data(), catsUsed)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6)
`, guild.ID, s.CharID, destination, level, huntData.Data(), catsUsed)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure)
s.server.db.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, pkt.HuntID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, pkt.HuntID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.State {
case 0: // Report registration
s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.charID)
database.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.CharID)
case 1: // Collected by hunter
s.server.db.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, pkt.HuntID)
s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID)
database.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, pkt.HuntID)
database.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID)
case 2: // Claim treasure
s.server.db.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, pkt.HuntID, s.charID)
database.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, pkt.HuntID, s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type TreasureSouvenir struct {
@@ -139,7 +157,7 @@ type TreasureSouvenir struct {
Quantity uint32
}
func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir)
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
@@ -149,10 +167,10 @@ func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(souvenir.Destination)
bf.WriteUint32(souvenir.Quantity)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
func HandleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresureSouvenir)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -1,9 +1,10 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/mhfitem"
ps "erupe-ce/utils/pascalstring"
"erupe-ce/utils/stringsupport"
@@ -42,8 +43,12 @@ FROM warehouse
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
s.server.db.Exec(`UPDATE user_binary SET house_furniture=$1 WHERE id=$2`, pkt.InteriorData, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec(`UPDATE user_binary SET house_furniture=$1 WHERE id=$2`, pkt.InteriorData, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type HouseData struct {
@@ -57,6 +62,10 @@ type HouseData struct {
func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateHouse)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
var houses []HouseData
@@ -65,18 +74,18 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
switch pkt.Method {
case 1:
var friendsList string
s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&friendsList)
database.QueryRow("SELECT friends FROM characters WHERE id=$1", s.CharID).Scan(&friendsList)
cids := stringsupport.CSVElems(friendsList)
for _, cid := range cids {
house := HouseData{}
row := s.server.db.QueryRowx(houseQuery, cid)
row := database.QueryRowx(houseQuery, cid)
err := row.StructScan(&house)
if err == nil {
houses = append(houses, house)
}
}
case 2:
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil || guild == nil {
break
}
@@ -86,7 +95,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
}
for _, member := range guildMembers {
house := HouseData{}
row := s.server.db.QueryRowx(houseQuery, member.CharID)
row := database.QueryRowx(houseQuery, member.CharID)
err = row.StructScan(&house)
if err == nil {
houses = append(houses, house)
@@ -96,7 +105,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
houseQuery = `SELECT c.id, hr, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
house := HouseData{}
rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
rows, _ := database.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
for rows.Next() {
err := rows.StructScan(&house)
if err == nil {
@@ -105,7 +114,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
}
case 4:
house := HouseData{}
row := s.server.db.QueryRowx(houseQuery, pkt.CharID)
row := database.QueryRowx(houseQuery, pkt.CharID)
err := row.StructScan(&house)
if err == nil {
houses = append(houses, house)
@@ -122,39 +131,47 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(0)
}
bf.WriteUint16(house.HR)
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
bf.WriteUint16(house.GR)
}
ps.Uint8(bf, house.Name, true)
}
bf.Seek(0, 0)
bf.WriteUint16(uint16(len(houses)))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateHouse)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
// 01 = closed
// 02 = open anyone
// 03 = open friends
// 04 = open guild
// 05 = open friends+guild
s.server.db.Exec(`UPDATE user_binary SET house_state=$1, house_password=$2 WHERE id=$3`, pkt.State, pkt.Password, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database.Exec(`UPDATE user_binary SET house_state=$1, house_password=$2 WHERE id=$3`, pkt.State, pkt.Password, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHouse)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
bf := byteframe.NewByteFrame()
var state uint8
var password string
s.server.db.QueryRow(`SELECT COALESCE(house_state, 2) as house_state, COALESCE(house_password, '') as house_password FROM user_binary WHERE id=$1
database.QueryRow(`SELECT COALESCE(house_state, 2) as house_state, COALESCE(house_password, '') as house_password FROM user_binary WHERE id=$1
`, pkt.CharID).Scan(&state, &password)
if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass {
if pkt.Password != password {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
}
@@ -165,10 +182,10 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
// Friends list verification
if state == 3 || state == 5 {
var friendsList string
s.server.db.QueryRow(`SELECT friends FROM characters WHERE id=$1`, pkt.CharID).Scan(&friendsList)
database.QueryRow(`SELECT friends FROM characters WHERE id=$1`, pkt.CharID).Scan(&friendsList)
cids := stringsupport.CSVElems(friendsList)
for _, cid := range cids {
if uint32(cid) == s.charID {
if uint32(cid) == s.CharID {
allowed = true
break
}
@@ -177,8 +194,8 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
// Guild verification
if state > 3 {
ownGuild, err := GetGuildInfoByCharacterId(s, s.charID)
isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.charID)
ownGuild, err := GetGuildInfoByCharacterId(s, s.CharID)
isApplicant, _ := ownGuild.HasApplicationForCharID(s, s.CharID)
if err == nil && ownGuild != nil {
othersGuild, err := GetGuildInfoByCharacterId(s, pkt.CharID)
if err == nil && othersGuild != nil {
@@ -190,13 +207,13 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
}
if !allowed {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
}
var houseTier, houseData, houseFurniture, bookshelf, gallery, tore, garden []byte
s.server.db.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1
database.QueryRow(`SELECT house_tier, house_data, house_furniture, bookshelf, gallery, tore, garden FROM user_binary WHERE id=$1
`, pkt.CharID).Scan(&houseTier, &houseData, &houseFurniture, &bookshelf, &gallery, &tore, &garden)
if houseFurniture == nil {
houseFurniture = make([]byte, 20)
@@ -226,59 +243,76 @@ func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
}
}
if len(bf.Data()) == 0 {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
}
func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetMyhouseInfo)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
s.server.db.QueryRow(`SELECT mission FROM user_binary WHERE id=$1`, s.charID).Scan(&data)
database.QueryRow(`SELECT mission FROM user_binary WHERE id=$1`, s.CharID).Scan(&data)
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 9))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 9))
}
}
func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateMyhouseInfo)
s.server.db.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Data, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("UPDATE user_binary SET mission=$1 WHERE id=$2", pkt.Data, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadDecoMyset)
var data []byte
err := s.server.db.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load decomyset", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
err = database.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load decomyset", zap.Error(err))
}
if len(data) == 0 {
data = []byte{0x01, 0x00}
if s.server.erupeConfig.ClientID < _config.G10 {
if config.GetConfig().ClientID < config.G10 {
data = []byte{0x00, 0x00}
}
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveDecoMyset)
var temp []byte
err := s.server.db.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.charID).Scan(&temp)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load decomyset", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var temp []byte
err = database.QueryRow("SELECT decomyset FROM characters WHERE id = $1", s.CharID).Scan(&temp)
if err != nil {
s.Logger.Error("Failed to load decomyset", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
// Version handling
bf := byteframe.NewByteFrame()
var size uint
if s.server.erupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
size = 76
bf.WriteUint8(1)
} else {
@@ -314,8 +348,8 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
}
dumpSaveData(s, bf.Data(), "decomyset")
s.server.db.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", bf.Data(), s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database.Exec("UPDATE characters SET decomyset=$1 WHERE id=$2", bf.Data(), s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type Title struct {
@@ -326,13 +360,17 @@ type Title struct {
func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateTitle)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var count uint16
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
bf.WriteUint16(0) // Unk
rows, err := s.server.db.Queryx("SELECT id, unlocked_at, updated_at FROM titles WHERE char_id=$1", s.charID)
rows, err := database.Queryx("SELECT id, unlocked_at, updated_at FROM titles WHERE char_id=$1", s.CharID)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
for rows.Next() {
@@ -349,35 +387,47 @@ func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {
}
bf.Seek(0, io.SeekStart)
bf.WriteUint16(count)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireTitle)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for _, title := range pkt.TitleIDs {
var exists int
err := s.server.db.QueryRow(`SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2`, title, s.charID).Scan(&exists)
err := database.QueryRow(`SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2`, title, s.CharID).Scan(&exists)
if err != nil || exists == 0 {
s.server.db.Exec(`INSERT INTO titles VALUES ($1, $2, now(), now())`, title, s.charID)
database.Exec(`INSERT INTO titles VALUES ($1, $2, now(), now())`, title, s.CharID)
} else {
s.server.db.Exec(`UPDATE titles SET updated_at=now() WHERE id=$1 AND char_id=$2`, title, s.charID)
database.Exec(`UPDATE titles SET updated_at=now() WHERE id=$1 AND char_id=$2`, title, s.CharID)
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
func initializeWarehouse(s *Session) {
var t int
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
database, err := db.GetDB()
if err != nil {
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var t int
err = database.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.CharID).Scan(&t)
if err != nil {
database.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.CharID)
}
}
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
initializeWarehouse(s)
bf := byteframe.NewByteFrame()
bf.WriteUint8(pkt.Operation)
@@ -386,7 +436,7 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
var count uint8
itemNames := make([]string, 10)
equipNames := make([]string, 10)
s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0],
database.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.CharID).Scan(&itemNames[0],
&itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0],
&equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9])
bf.WriteUint32(0)
@@ -415,9 +465,9 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
case 2:
switch pkt.BoxType {
case 0:
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
database.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.CharID)
case 1:
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
database.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.CharID)
}
case 3:
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
@@ -433,28 +483,40 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
// 2 = Rename
// 3 = Get usage limit
// 4 = Get gift box names (doesn't do anything?)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func addWarehouseItem(s *Session, item mhfitem.MHFItemStack) {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
giftBox := warehouseGetItems(s, 10)
item.WarehouseID = token.RNG.Uint32()
giftBox = append(giftBox, item)
s.server.db.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.charID)
database.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.CharID)
}
func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
giftBox := warehouseGetEquipment(s, 10)
equipment.WarehouseID = token.RNG.Uint32()
giftBox = append(giftBox, equipment)
s.server.db.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.charID)
database.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.CharID)
}
func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
initializeWarehouse(s)
var data []byte
var items []mhfitem.MHFItemStack
s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
database.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.CharID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
@@ -467,9 +529,13 @@ func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
}
func warehouseGetEquipment(s *Session, index uint8) []mhfitem.MHFEquipment {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
var equipment []mhfitem.MHFEquipment
s.server.db.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
database.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.CharID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
@@ -493,18 +559,22 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
bf.WriteBytes(mhfitem.SerializeWarehouseEquipment(equipment))
}
if bf.Index() > 0 {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
}
}
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.BoxType {
case 0:
newStacks := mhfitem.DiffItemStacks(warehouseGetItems(s, pkt.BoxIndex), pkt.UpdatedItems)
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(newStacks), s.charID)
database.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(newStacks), s.CharID)
case 1:
var fEquip []mhfitem.MHFEquipment
oEquips := warehouseGetEquipment(s, pkt.BoxIndex)
@@ -528,7 +598,7 @@ func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
fEquip = append(fEquip, oEquip)
}
}
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.charID)
database.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -3,6 +3,8 @@ package channelserver
import (
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"fmt"
"go.uber.org/zap"
)
@@ -10,37 +12,49 @@ import (
func handleMsgMhfAddKouryouPoint(s *Session, p mhfpacket.MHFPacket) {
// hunting with both ranks maxed gets you these
pkt := p.(*mhfpacket.MsgMhfAddKouryouPoint)
var points int
err := s.server.db.QueryRow("UPDATE characters SET kouryou_point=COALESCE(kouryou_point + $1, $1) WHERE id=$2 RETURNING kouryou_point", pkt.KouryouPoints, s.charID).Scan(&points)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to update KouryouPoint in db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var points int
err = database.QueryRow("UPDATE characters SET kouryou_point=COALESCE(kouryou_point + $1, $1) WHERE id=$2 RETURNING kouryou_point", pkt.KouryouPoints, s.CharID).Scan(&points)
if err != nil {
s.Logger.Error("Failed to update KouryouPoint in db", zap.Error(err))
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(uint32(points))
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfGetKouryouPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetKouryouPoint)
var points int
err := s.server.db.QueryRow("SELECT COALESCE(kouryou_point, 0) FROM characters WHERE id = $1", s.charID).Scan(&points)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to get kouryou_point savedata from db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var points int
err = database.QueryRow("SELECT COALESCE(kouryou_point, 0) FROM characters WHERE id = $1", s.CharID).Scan(&points)
if err != nil {
s.Logger.Error("Failed to get kouryou_point savedata from db", zap.Error(err))
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(uint32(points))
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfExchangeKouryouPoint(s *Session, p mhfpacket.MHFPacket) {
// spent at the guildmaster, 10000 a roll
var points int
pkt := p.(*mhfpacket.MsgMhfExchangeKouryouPoint)
err := s.server.db.QueryRow("UPDATE characters SET kouryou_point=kouryou_point - $1 WHERE id=$2 RETURNING kouryou_point", pkt.KouryouPoints, s.charID).Scan(&points)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to update platemyset savedata in db", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("UPDATE characters SET kouryou_point=kouryou_point - $1 WHERE id=$2 RETURNING kouryou_point", pkt.KouryouPoints, s.CharID).Scan(&points)
if err != nil {
s.Logger.Error("Failed to update platemyset savedata in db", zap.Error(err))
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(uint32(points))
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}

View File

@@ -2,7 +2,9 @@ package channelserver
import (
"database/sql"
"erupe-ce/utils/db"
"erupe-ce/utils/stringsupport"
"fmt"
"time"
"erupe-ce/network/binpacket"
@@ -31,21 +33,23 @@ type Mail struct {
}
func (m *Mail) Send(s *Session, transaction *sql.Tx) error {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
query := `
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite, is_sys_message)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`
var err error
if transaction == nil {
_, err = s.server.db.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
_, err = database.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
} else {
_, err = transaction.Exec(query, m.SenderID, m.RecipientID, m.Subject, m.Body, m.AttachedItemID, m.AttachedItemAmount, m.IsGuildInvite, m.IsSystemMessage)
}
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to send mail",
zap.Error(err),
zap.Uint32("senderID", m.SenderID),
@@ -64,12 +68,16 @@ func (m *Mail) Send(s *Session, transaction *sql.Tx) error {
}
func (m *Mail) MarkRead(s *Session) error {
_, err := s.server.db.Exec(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_, err = database.Exec(`
UPDATE mail SET read = true WHERE id = $1
`, m.ID)
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to mark mail as read",
zap.Error(err),
zap.Int("mailID", m.ID),
@@ -81,7 +89,11 @@ func (m *Mail) MarkRead(s *Session) error {
}
func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
rows, err := s.server.db.Queryx(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`
SELECT
m.id,
m.sender_id,
@@ -105,7 +117,7 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
`, charID)
if err != nil {
s.logger.Error("failed to get mail for character", zap.Error(err), zap.Uint32("charID", charID))
s.Logger.Error("failed to get mail for character", zap.Error(err), zap.Uint32("charID", charID))
return nil, err
}
@@ -129,7 +141,11 @@ func GetMailListForCharacter(s *Session, charID uint32) ([]Mail, error) {
}
func GetMailByID(s *Session, ID int) (*Mail, error) {
row := s.server.db.QueryRowx(`
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := database.QueryRowx(`
SELECT
m.id,
m.sender_id,
@@ -154,10 +170,10 @@ func GetMailByID(s *Session, ID int) (*Mail, error) {
mail := &Mail{}
err := row.StructScan(mail)
err = row.StructScan(mail)
if err != nil {
s.logger.Error(
s.Logger.Error(
"failed to retrieve mail",
zap.Error(err),
zap.Int("mailID", ID),
@@ -190,11 +206,15 @@ func SendMailNotification(s *Session, m *Mail, recipient *Session) {
}
func getCharacterName(s *Session, charID uint32) string {
row := s.server.db.QueryRow("SELECT name FROM characters WHERE id = $1", charID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
row := database.QueryRow("SELECT name FROM characters WHERE id = $1", charID)
charName := ""
err := row.Scan(&charName)
err = row.Scan(&charName)
if err != nil {
return ""
@@ -204,32 +224,35 @@ func getCharacterName(s *Session, charID uint32) string {
func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadMail)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
mailId := s.mailList[pkt.AccIndex]
if mailId == 0 {
doAckBufSucceed(s, pkt.AckHandle, []byte{0})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0})
return
}
mail, err := GetMailByID(s, mailId)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, []byte{0})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0})
return
}
s.server.db.Exec(`UPDATE mail SET read = true WHERE id = $1`, mail.ID)
database.Exec(`UPDATE mail SET read = true WHERE id = $1`, mail.ID)
bf := byteframe.NewByteFrame()
body := stringsupport.UTF8ToSJIS(mail.Body)
bf.WriteNullTerminatedBytes(body)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfListMail)
mail, err := GetMailListForCharacter(s, s.charID)
mail, err := GetMailListForCharacter(s, s.CharID)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, []byte{0})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0})
return
}
@@ -290,64 +313,71 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
}
}
doAckBufSucceed(s, pkt.AckHandle, msg.Data())
DoAckBufSucceed(s, pkt.AckHandle, msg.Data())
}
func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOprtMail)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
mail, err := GetMailByID(s, s.mailList[pkt.AccIndex])
if err != nil {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
switch pkt.Operation {
case mhfpacket.OperateMailDelete:
s.server.db.Exec(`UPDATE mail SET deleted = true WHERE id = $1`, mail.ID)
database.Exec(`UPDATE mail SET deleted = true WHERE id = $1`, mail.ID)
case mhfpacket.OperateMailLock:
s.server.db.Exec(`UPDATE mail SET locked = TRUE WHERE id = $1`, mail.ID)
database.Exec(`UPDATE mail SET locked = TRUE WHERE id = $1`, mail.ID)
case mhfpacket.OperateMailUnlock:
s.server.db.Exec(`UPDATE mail SET locked = FALSE WHERE id = $1`, mail.ID)
database.Exec(`UPDATE mail SET locked = FALSE WHERE id = $1`, mail.ID)
case mhfpacket.OperateMailAcquireItem:
s.server.db.Exec(`UPDATE mail SET attached_item_received = TRUE WHERE id = $1`, mail.ID)
database.Exec(`UPDATE mail SET attached_item_received = TRUE WHERE id = $1`, mail.ID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfSendMail(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSendMail)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
query := `
INSERT INTO mail (sender_id, recipient_id, subject, body, attached_item, attached_item_amount, is_guild_invite)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`
if pkt.RecipientID == 0 { // Guild mail
g, err := GetGuildInfoByCharacterId(s, s.charID)
g, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil {
s.logger.Error("Failed to get guild info for mail")
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to get guild info for mail")
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
gm, err := GetGuildMembers(s, g.ID, false)
if err != nil {
s.logger.Error("Failed to get guild members for mail")
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to get guild members for mail")
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
for i := 0; i < len(gm); i++ {
_, err := s.server.db.Exec(query, s.charID, gm[i].CharID, pkt.Subject, pkt.Body, 0, 0, false)
_, err := database.Exec(query, s.CharID, gm[i].CharID, pkt.Subject, pkt.Body, 0, 0, false)
if err != nil {
s.logger.Error("Failed to send mail")
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to send mail")
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
}
} else {
_, err := s.server.db.Exec(query, s.charID, pkt.RecipientID, pkt.Subject, pkt.Body, pkt.ItemID, pkt.Quantity, false)
_, err := database.Exec(query, s.CharID, pkt.RecipientID, pkt.Subject, pkt.Body, pkt.ItemID, pkt.Quantity, false)
if err != nil {
s.logger.Error("Failed to send mail")
s.Logger.Error("Failed to send mail")
}
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -1,13 +1,16 @@
package channelserver
import (
_config "erupe-ce/config"
config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp"
"erupe-ce/server/channelserver/compression/nullcomp"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
"fmt"
"io"
"time"
@@ -17,22 +20,30 @@ import (
func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadPartner)
var data []byte
err := s.server.db.QueryRow("SELECT partner FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT partner FROM characters WHERE id = $1", s.CharID).Scan(&data)
if len(data) == 0 {
s.logger.Error("Failed to load partner", zap.Error(err))
s.Logger.Error("Failed to load partner", zap.Error(err))
data = make([]byte, 9)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePartner)
dumpSaveData(s, pkt.RawDataPayload, "partner")
_, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to save partner", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
dumpSaveData(s, pkt.RawDataPayload, "partner")
_, err = database.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.Logger.Error("Failed to save partner", zap.Error(err))
}
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
@@ -51,36 +62,44 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(dispatch.Unk)
bf.WriteUint32(dispatch.Timestamp)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi)
naviLength := 552
if s.server.erupeConfig.ClientID <= _config.G7 {
if config.GetConfig().ClientID <= config.G7 {
naviLength = 280
}
var data []byte
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.CharID).Scan(&data)
if len(data) == 0 {
s.logger.Error("Failed to load hunternavi", zap.Error(err))
s.Logger.Error("Failed to load hunternavi", zap.Error(err))
data = make([]byte, naviLength)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.IsDataDiff {
naviLength := 552
if s.server.erupeConfig.ClientID <= _config.G7 {
if config.GetConfig().ClientID <= config.G7 {
naviLength = 280
}
var data []byte
// Load existing save
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
err := database.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.logger.Error("Failed to load hunternavi", zap.Error(err))
s.Logger.Error("Failed to load hunternavi", zap.Error(err))
}
// Check if we actually had any hunternavi data, using a blank buffer if not.
@@ -90,22 +109,22 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
}
// Perform diff and compress it to write back to db
s.logger.Info("Diffing...")
s.Logger.Info("Diffing...")
saveOutput := deltacomp.ApplyDataDiff(pkt.RawDataPayload, data)
_, err = s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", saveOutput, s.charID)
_, err = database.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", saveOutput, s.CharID)
if err != nil {
s.logger.Error("Failed to save hunternavi", zap.Error(err))
s.Logger.Error("Failed to save hunternavi", zap.Error(err))
}
s.logger.Info("Wrote recompressed hunternavi back to DB")
s.Logger.Info("Wrote recompressed hunternavi back to DB")
} else {
dumpSaveData(s, pkt.RawDataPayload, "hunternavi")
// simply update database, no extra processing
_, err := s.server.db.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
_, err := database.Exec("UPDATE characters SET hunternavi=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.logger.Error("Failed to save hunternavi", zap.Error(err))
s.Logger.Error("Failed to save hunternavi", zap.Error(err))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {
@@ -116,9 +135,9 @@ func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {
// struct Hunt
// uint32 HuntID
// uint32 MonID
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 0))
}
}
@@ -132,39 +151,51 @@ func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {
// []byte Name (len 18)
// uint8 Unk
// uint8 Unk
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
bf := byteframe.NewByteFrame()
var nextID uint32
_ = s.server.db.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&nextID)
s.server.db.Exec("UPDATE characters SET rasta_id=$1 WHERE id=$2", nextID, s.charID)
_ = database.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&nextID)
database.Exec("UPDATE characters SET rasta_id=$1 WHERE id=$2", nextID, s.CharID)
bf.WriteUint32(nextID)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveMercenary)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
dumpSaveData(s, pkt.MercData, "mercenary")
if len(pkt.MercData) > 0 {
temp := byteframe.NewByteFrameFromBytes(pkt.MercData)
s.server.db.Exec("UPDATE characters SET savemercenary=$1, rasta_id=$2 WHERE id=$3", pkt.MercData, temp.ReadUint32(), s.charID)
database.Exec("UPDATE characters SET savemercenary=$1, rasta_id=$2 WHERE id=$3", pkt.MercData, temp.ReadUint32(), s.CharID)
}
s.server.db.Exec("UPDATE characters SET gcp=$1, pact_id=$2 WHERE id=$3", pkt.GCP, pkt.PactMercID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
database.Exec("UPDATE characters SET gcp=$1, pact_id=$2 WHERE id=$3", pkt.GCP, pkt.PactMercID, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadMercenaryW)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
bf := byteframe.NewByteFrame()
var pactID, cid uint32
var name string
s.server.db.QueryRow("SELECT pact_id FROM characters WHERE id=$1", s.charID).Scan(&pactID)
database.QueryRow("SELECT pact_id FROM characters WHERE id=$1", s.CharID).Scan(&pactID)
if pactID > 0 {
s.server.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id = $1", pactID).Scan(&name, &cid)
database.QueryRow("SELECT name, id FROM characters WHERE rasta_id = $1", pactID).Scan(&name, &cid)
bf.WriteUint8(1) // numLends
bf.WriteUint32(pactID)
bf.WriteUint32(cid)
@@ -179,7 +210,7 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
var loans uint8
temp := byteframe.NewByteFrame()
if pkt.Op < 2 {
rows, _ := s.server.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", s.charID)
rows, _ := database.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", s.CharID)
for rows.Next() {
err := rows.Scan(&name, &cid, &pactID)
if err != nil {
@@ -199,8 +230,8 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
if pkt.Op < 1 {
var data []byte
var gcp uint32
s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id=$1", s.charID).Scan(&data)
s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id=$1", s.charID).Scan(&gcp)
database.QueryRow("SELECT savemercenary FROM characters WHERE id=$1", s.CharID).Scan(&data)
database.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id=$1", s.CharID).Scan(&gcp)
if len(data) == 0 {
bf.WriteBool(false)
@@ -211,53 +242,69 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(gcp)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadMercenaryM)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
database.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
resp := byteframe.NewByteFrame()
if len(data) == 0 {
resp.WriteBool(false)
} else {
resp.WriteBytes(data)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfContractMercenary)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.Op {
case 0: // Form loan
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, pkt.CID)
database.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, pkt.CID)
case 1: // Cancel lend
s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.charID)
database.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.CharID)
case 2: // Cancel loan
s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", pkt.CID)
database.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", pkt.CID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadOtomoAirou)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var data []byte
err := s.server.db.QueryRow("SELECT otomoairou FROM characters WHERE id = $1", s.charID).Scan(&data)
err = database.QueryRow("SELECT otomoairou FROM characters WHERE id = $1", s.CharID).Scan(&data)
if len(data) == 0 {
s.logger.Error("Failed to load otomoairou", zap.Error(err))
s.Logger.Error("Failed to load otomoairou", zap.Error(err))
data = make([]byte, 10)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
dumpSaveData(s, pkt.RawDataPayload, "otomoairou")
decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:])
if err != nil {
s.logger.Error("Failed to decompress airou", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to decompress airou", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf := byteframe.NewByteFrameFromBytes(decomp)
@@ -270,7 +317,7 @@ func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
dataLen := bf.ReadUint32()
catID := bf.ReadUint32()
if catID == 0 {
_ = s.server.db.QueryRow("SELECT nextval('airou_id_seq')").Scan(&catID)
_ = database.QueryRow("SELECT nextval('airou_id_seq')").Scan(&catID)
}
exists := bf.ReadBool()
data := bf.ReadBytes(uint(dataLen) - 5)
@@ -287,12 +334,12 @@ func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
save.WriteUint8(catsExist)
comp, err := nullcomp.Compress(save.Data())
if err != nil {
s.logger.Error("Failed to compress airou", zap.Error(err))
s.Logger.Error("Failed to compress airou", zap.Error(err))
} else {
comp = append([]byte{0x01}, comp...)
s.server.db.Exec("UPDATE characters SET otomoairou=$1 WHERE id=$2", comp, s.charID)
database.Exec("UPDATE characters SET otomoairou=$1 WHERE id=$2", comp, s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfEnumerateAiroulist(s *Session, p mhfpacket.MHFPacket) {
@@ -311,7 +358,7 @@ func handleMsgMhfEnumerateAiroulist(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint16(cat.WeaponID)
resp.WriteUint32(0) // 32 bit unix timestamp, either time at which the cat stops being fatigued or the time at which it started
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
type Airou struct {
@@ -326,17 +373,21 @@ type Airou struct {
}
func getGuildAirouList(s *Session) []Airou {
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var guildCats []Airou
bannedCats := make(map[uint32]int)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
guild, err := GetGuildInfoByCharacterId(s, s.CharID)
if err != nil {
return guildCats
}
rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh
rows, err := database.Query(`SELECT cats_used FROM guild_hunts gh
INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1
`, s.charID)
`, s.CharID)
if err != nil {
s.logger.Warn("Failed to get recently used airous", zap.Error(err))
s.Logger.Warn("Failed to get recently used airous", zap.Error(err))
return guildCats
}
@@ -347,19 +398,19 @@ func getGuildAirouList(s *Session) []Airou {
if err != nil {
continue
}
if startTemp.Add(time.Second * time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntPartnyaCooldown)).Before(gametime.TimeAdjusted()) {
if startTemp.Add(time.Second * time.Duration(config.GetConfig().GameplayOptions.TreasureHuntPartnyaCooldown)).Before(gametime.TimeAdjusted()) {
for i, j := range stringsupport.CSVElems(csvTemp) {
bannedCats[uint32(j)] = i
}
}
}
rows, err = s.server.db.Query(`SELECT c.otomoairou FROM characters c
rows, err = database.Query(`SELECT c.otomoairou FROM characters c
INNER JOIN guild_characters gc ON gc.character_id = c.id
WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL
ORDER BY c.id LIMIT 60`, guild.ID)
if err != nil {
s.logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err))
s.Logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err))
return guildCats
}
@@ -373,7 +424,7 @@ func getGuildAirouList(s *Session) []Airou {
if data[0] == 1 {
decomp, err := nullcomp.Decompress(data[1:])
if err != nil {
s.logger.Warn("decomp failure", zap.Error(err))
s.Logger.Warn("decomp failure", zap.Error(err))
continue
}
bf := byteframe.NewByteFrameFromBytes(decomp)

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"fmt"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
)
@@ -13,18 +14,18 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
s.stage.Lock()
newObj := &Object{
id: s.NextObjectID(),
ownerCharID: s.charID,
ownerCharID: s.CharID,
x: pkt.X,
y: pkt.Y,
z: pkt.Z,
}
s.stage.objects[s.charID] = newObj
s.stage.objects[s.CharID] = newObj
s.stage.Unlock()
// Response to our requesting client.
resp := byteframe.NewByteFrame()
resp.WriteUint32(newObj.id) // New local obj handle.
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
// Duplicate the object creation to all sessions in the same stage.
dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{
ObjID: newObj.id,
@@ -34,7 +35,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
OwnerCharID: newObj.ownerCharID,
}
s.logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, newObj.id))
s.Logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, newObj.id))
s.stage.BroadcastMHF(dupObjUpdate, s)
}
@@ -42,11 +43,11 @@ func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysPositionObject)
if s.server.erupeConfig.DebugOptions.LogInboundMessages {
if config.GetConfig().DebugOptions.LogInboundMessages {
fmt.Printf("[%s] with objectID [%d] move to (%f,%f,%f)\n\n", s.Name, pkt.ObjID, pkt.X, pkt.Y, pkt.Z)
}
s.stage.Lock()
object, ok := s.stage.objects[s.charID]
object, ok := s.stage.objects[s.CharID]
if ok {
object.x = pkt.X
object.y = pkt.Y
@@ -64,16 +65,16 @@ func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
_ = p.(*mhfpacket.MsgSysSetObjectBinary)
/* This causes issues with PS3 as this actually sends with endiness!
for _, session := range s.server.sessions {
if session.charID == s.charID {
s.server.userBinaryPartsLock.Lock()
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 3}] = pkt.RawDataPayload
s.server.userBinaryPartsLock.Unlock()
for _, session := range s.Server.sessions {
if session.CharID == s.CharID {
s.Server.userBinaryPartsLock.Lock()
s.Server.userBinaryParts[userBinaryPartID{charID: s.CharID, index: 3}] = pkt.RawDataPayload
s.Server.userBinaryPartsLock.Unlock()
msg := &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
CharID: s.CharID,
BinaryType: 3,
}
s.server.BroadcastMHF(msg, s)
s.Server.BroadcastMHF(msg, s)
}
}
*/

View File

@@ -4,40 +4,50 @@ import (
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp"
"erupe-ce/server/channelserver/compression/nullcomp"
"erupe-ce/utils/db"
"fmt"
"go.uber.org/zap"
)
func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadPlateData)
var data []byte
err := s.server.db.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load platedata", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckBufSucceed(s, pkt.AckHandle, data)
err = database.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load platedata", zap.Error(err))
}
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateData)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.IsDataDiff {
var data []byte
// Load existing save
err := s.server.db.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.charID).Scan(&data)
err := database.QueryRow("SELECT platedata FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.logger.Error("Failed to load platedata", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to load platedata", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
if len(data) > 0 {
// Decompress
s.logger.Info("Decompressing...")
s.Logger.Info("Decompressing...")
data, err = nullcomp.Decompress(data)
if err != nil {
s.logger.Error("Failed to decompress platedata", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to decompress platedata", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
} else {
@@ -46,66 +56,73 @@ func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {
}
// Perform diff and compress it to write back to db
s.logger.Info("Diffing...")
s.Logger.Info("Diffing...")
saveOutput, err := nullcomp.Compress(deltacomp.ApplyDataDiff(pkt.RawDataPayload, data))
if err != nil {
s.logger.Error("Failed to diff and compress platedata", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to diff and compress platedata", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
_, err = s.server.db.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", saveOutput, s.charID)
_, err = database.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", saveOutput, s.CharID)
if err != nil {
s.logger.Error("Failed to save platedata", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to save platedata", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
s.logger.Info("Wrote recompressed platedata back to DB")
s.Logger.Info("Wrote recompressed platedata back to DB")
} else {
dumpSaveData(s, pkt.RawDataPayload, "platedata")
// simply update database, no extra processing
_, err := s.server.db.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
_, err := database.Exec("UPDATE characters SET platedata=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.logger.Error("Failed to save platedata", zap.Error(err))
s.Logger.Error("Failed to save platedata", zap.Error(err))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadPlateBox)
var data []byte
err := s.server.db.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load platebox", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
doAckBufSucceed(s, pkt.AckHandle, data)
err = database.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load platebox", zap.Error(err))
}
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateBox)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.IsDataDiff {
var data []byte
// Load existing save
err := s.server.db.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.charID).Scan(&data)
err := database.QueryRow("SELECT platebox FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.logger.Error("Failed to load platebox", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to load platebox", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
// Decompress
if len(data) > 0 {
// Decompress
s.logger.Info("Decompressing...")
s.Logger.Info("Decompressing...")
data, err = nullcomp.Decompress(data)
if err != nil {
s.logger.Error("Failed to decompress platebox", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to decompress platebox", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
} else {
@@ -114,51 +131,59 @@ func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {
}
// Perform diff and compress it to write back to db
s.logger.Info("Diffing...")
s.Logger.Info("Diffing...")
saveOutput, err := nullcomp.Compress(deltacomp.ApplyDataDiff(pkt.RawDataPayload, data))
if err != nil {
s.logger.Error("Failed to diff and compress platebox", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to diff and compress platebox", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
_, err = s.server.db.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", saveOutput, s.charID)
_, err = database.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", saveOutput, s.CharID)
if err != nil {
s.logger.Error("Failed to save platebox", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Logger.Error("Failed to save platebox", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
return
}
s.logger.Info("Wrote recompressed platebox back to DB")
s.Logger.Info("Wrote recompressed platebox back to DB")
} else {
dumpSaveData(s, pkt.RawDataPayload, "platebox")
// simply update database, no extra processing
_, err := s.server.db.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
_, err := database.Exec("UPDATE characters SET platebox=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.logger.Error("Failed to save platebox", zap.Error(err))
s.Logger.Error("Failed to save platebox", zap.Error(err))
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadPlateMyset)
var data []byte
err := s.server.db.QueryRow("SELECT platemyset FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT platemyset FROM characters WHERE id = $1", s.CharID).Scan(&data)
if len(data) == 0 {
s.logger.Error("Failed to load platemyset", zap.Error(err))
s.Logger.Error("Failed to load platemyset", zap.Error(err))
data = make([]byte, 1920)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfSavePlateMyset(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSavePlateMyset)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
// looks to always return the full thing, simply update database, no extra processing
dumpSaveData(s, pkt.RawDataPayload, "platemyset")
_, err := s.server.db.Exec("UPDATE characters SET platemyset=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
_, err = database.Exec("UPDATE characters SET platemyset=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.logger.Error("Failed to save platemyset", zap.Error(err))
s.Logger.Error("Failed to save platemyset", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}

View File

@@ -4,10 +4,11 @@ import (
"database/sql"
"encoding/binary"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/decryption"
"erupe-ce/utils/gametime"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
ps "erupe-ce/utils/pascalstring"
"fmt"
@@ -62,16 +63,16 @@ func BackportQuest(data []byte) []byte {
}
fillLength := uint32(108)
if _config.ErupeConfig.ClientID <= _config.S6 {
if config.GetConfig().ClientID <= config.S6 {
fillLength = 44
} else if _config.ErupeConfig.ClientID <= _config.F5 {
} else if config.GetConfig().ClientID <= config.F5 {
fillLength = 52
} else if _config.ErupeConfig.ClientID <= _config.G101 {
} else if config.GetConfig().ClientID <= config.G101 {
fillLength = 76
}
copy(data[wp:wp+fillLength], data[rp:rp+fillLength])
if _config.ErupeConfig.ClientID <= _config.G91 {
if config.GetConfig().ClientID <= config.G91 {
patterns := [][]byte{
{0x0A, 0x00, 0x01, 0x33, 0xD7, 0x00}, // 10% Armor Sphere -> Stone
{0x06, 0x00, 0x02, 0x33, 0xD8, 0x00}, // 6% Armor Sphere+ -> Iron Ore
@@ -94,8 +95,8 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysGetFile)
if pkt.IsScenario {
if s.server.erupeConfig.DebugOptions.QuestTools {
s.logger.Debug(
if config.GetConfig().DebugOptions.QuestTools {
s.Logger.Debug(
"Scenario",
zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID),
zap.Uint32("MainID", pkt.ScenarioIdentifer.MainID),
@@ -105,49 +106,49 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
}
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 := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("scenarios/%s.bin", filename)))
data, err := os.ReadFile(filepath.Join(config.GetConfig().BinPath, fmt.Sprintf("scenarios/%s.bin", filename)))
if err != nil {
s.logger.Error(fmt.Sprintf("Failed to open file: %s/scenarios/%s.bin", s.server.erupeConfig.BinPath, filename))
s.Logger.Error(fmt.Sprintf("Failed to open file: %s/scenarios/%s.bin", config.GetConfig().BinPath, filename))
// This will crash the game.
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
return
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
} else {
if s.server.erupeConfig.DebugOptions.QuestTools {
s.logger.Debug(
if config.GetConfig().DebugOptions.QuestTools {
s.Logger.Debug(
"Quest",
zap.String("Filename", pkt.Filename),
)
}
if s.server.erupeConfig.GameplayOptions.SeasonOverride {
if config.GetConfig().GameplayOptions.SeasonOverride {
pkt.Filename = seasonConversion(s, pkt.Filename)
}
data, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
data, err := os.ReadFile(filepath.Join(config.GetConfig().BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
if err != nil {
s.logger.Error(fmt.Sprintf("Failed to open file: %s/quests/%s.bin", s.server.erupeConfig.BinPath, pkt.Filename))
s.Logger.Error(fmt.Sprintf("Failed to open file: %s/quests/%s.bin", config.GetConfig().BinPath, pkt.Filename))
// This will crash the game.
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
return
}
if _config.ErupeConfig.ClientID <= _config.Z1 && s.server.erupeConfig.DebugOptions.AutoQuestBackport {
if config.GetConfig().ClientID <= config.Z1 && config.GetConfig().DebugOptions.AutoQuestBackport {
data = BackportQuest(decryption.UnpackSimple(data))
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
}
func seasonConversion(s *Session, questFile string) string {
filename := fmt.Sprintf("%s%d", questFile[:6], s.server.Season())
filename := fmt.Sprintf("%s%d", questFile[:6], s.Server.Season())
// Return the seasonal file
if _, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", filename))); err == nil {
if _, err := os.Stat(filepath.Join(config.GetConfig().BinPath, fmt.Sprintf("quests/%s.bin", filename))); err == nil {
return filename
} else {
// Attempt to return the requested quest file if the seasonal file doesn't exist
if _, err = os.Stat(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", questFile))); err == nil {
if _, err = os.Stat(filepath.Join(config.GetConfig().BinPath, fmt.Sprintf("quests/%s.bin", questFile))); err == nil {
return questFile
}
@@ -169,34 +170,42 @@ func seasonConversion(s *Session, questFile string) string {
func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadFavoriteQuest)
var data []byte
err := s.server.db.QueryRow("SELECT savefavoritequest FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT savefavoritequest FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err == nil && len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
} else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
}
func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveFavoriteQuest)
dumpSaveData(s, pkt.Data, "favquest")
s.server.db.Exec("UPDATE characters SET savefavoritequest=$1 WHERE id=$2", pkt.Data, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("UPDATE characters SET savefavoritequest=$1 WHERE id=$2", pkt.Data, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func loadQuestFile(s *Session, questId int) []byte {
data, exists := s.server.questCacheData[questId]
if exists && s.server.questCacheTime[questId].Add(time.Duration(s.server.erupeConfig.QuestCacheExpiry)*time.Second).After(time.Now()) {
data, exists := s.Server.questCacheData[questId]
if exists && s.Server.questCacheTime[questId].Add(time.Duration(config.GetConfig().QuestCacheExpiry)*time.Second).After(time.Now()) {
return data
}
file, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%05dd0.bin", questId)))
file, err := os.ReadFile(filepath.Join(config.GetConfig().BinPath, fmt.Sprintf("quests/%05dd0.bin", questId)))
if err != nil {
return nil
}
decrypted := decryption.UnpackSimple(file)
if _config.ErupeConfig.ClientID <= _config.Z1 && s.server.erupeConfig.DebugOptions.AutoQuestBackport {
if config.GetConfig().ClientID <= config.Z1 && config.GetConfig().DebugOptions.AutoQuestBackport {
decrypted = BackportQuest(decrypted)
}
fileBytes := byteframe.NewByteFrameFromBytes(decrypted)
@@ -204,13 +213,13 @@ func loadQuestFile(s *Session, questId int) []byte {
fileBytes.Seek(int64(fileBytes.ReadUint32()), 0)
bodyLength := 320
if _config.ErupeConfig.ClientID <= _config.S6 {
if config.GetConfig().ClientID <= config.S6 {
bodyLength = 160
} else if _config.ErupeConfig.ClientID <= _config.F5 {
} else if config.GetConfig().ClientID <= config.F5 {
bodyLength = 168
} else if _config.ErupeConfig.ClientID <= _config.G101 {
} else if config.GetConfig().ClientID <= config.G101 {
bodyLength = 192
} else if _config.ErupeConfig.ClientID <= _config.Z1 {
} else if config.GetConfig().ClientID <= config.Z1 {
bodyLength = 224
}
@@ -240,8 +249,8 @@ func loadQuestFile(s *Session, questId int) []byte {
}
questBody.WriteBytes(newStrings.Data())
s.server.questCacheData[questId] = questBody.Data()
s.server.questCacheTime[questId] = time.Now()
s.Server.questCacheData[questId] = questBody.Data()
s.Server.questCacheTime[questId] = time.Now()
return questBody.Data()
}
@@ -263,15 +272,15 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
bf.WriteUint8(0) // Unk
switch questType {
case 16:
bf.WriteUint8(s.server.erupeConfig.GameplayOptions.RegularRavienteMaxPlayers)
bf.WriteUint8(config.GetConfig().GameplayOptions.RegularRavienteMaxPlayers)
case 22:
bf.WriteUint8(s.server.erupeConfig.GameplayOptions.ViolentRavienteMaxPlayers)
bf.WriteUint8(config.GetConfig().GameplayOptions.ViolentRavienteMaxPlayers)
case 40:
bf.WriteUint8(s.server.erupeConfig.GameplayOptions.BerserkRavienteMaxPlayers)
bf.WriteUint8(config.GetConfig().GameplayOptions.BerserkRavienteMaxPlayers)
case 50:
bf.WriteUint8(s.server.erupeConfig.GameplayOptions.ExtremeRavienteMaxPlayers)
bf.WriteUint8(config.GetConfig().GameplayOptions.ExtremeRavienteMaxPlayers)
case 51:
bf.WriteUint8(s.server.erupeConfig.GameplayOptions.SmallBerserkRavienteMaxPlayers)
bf.WriteUint8(config.GetConfig().GameplayOptions.SmallBerserkRavienteMaxPlayers)
default:
bf.WriteUint8(maxPlayers)
}
@@ -282,7 +291,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
bf.WriteBool(true)
}
bf.WriteUint16(0) // Unk
if _config.ErupeConfig.ClientID >= _config.G2 {
if config.GetConfig().ClientID >= config.G2 {
bf.WriteUint32(mark)
}
bf.WriteUint16(0) // Unk
@@ -295,7 +304,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
bf.Seek(25, 0)
flagByte := bf.ReadUint8()
bf.Seek(25, 0)
if s.server.erupeConfig.GameplayOptions.SeasonOverride {
if config.GetConfig().GameplayOptions.SeasonOverride {
bf.WriteUint8(flagByte & 0b11100000)
} else {
// Allow for seasons to be specified in database, otherwise use the one in the file.
@@ -326,11 +335,14 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
var totalCount, returnedCount uint16
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
rows, err := s.server.db.Query("SELECT id, COALESCE(max_players, 4) AS max_players, quest_type, quest_id, COALESCE(mark, 0) AS mark, COALESCE(flags, -1), start_time, COALESCE(active_days, 0) AS active_days, COALESCE(inactive_days, 0) AS inactive_days FROM event_quests ORDER BY quest_id")
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Query("SELECT id, COALESCE(max_players, 4) AS max_players, quest_type, quest_id, COALESCE(mark, 0) AS mark, COALESCE(flags, -1), start_time, COALESCE(active_days, 0) AS active_days, COALESCE(inactive_days, 0) AS inactive_days FROM event_quests ORDER BY quest_id")
if err == nil {
currentTime := time.Now()
tx, _ := s.server.db.Begin()
tx, _ := database.Begin()
for rows.Next() {
var id, mark uint32
@@ -340,7 +352,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
err = rows.Scan(&id, &maxPlayers, &questType, &questId, &mark, &flags, &startTime, &activeDays, &inactiveDays)
if err != nil {
s.logger.Error("Failed to scan event quest row", zap.Error(err))
s.Logger.Error("Failed to scan event quest row", zap.Error(err))
continue
}
@@ -375,11 +387,11 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
data, err := makeEventQuest(s, rows)
if err != nil {
s.logger.Error("Failed to make event quest", zap.Error(err))
s.Logger.Error("Failed to make event quest", zap.Error(err))
continue
} else {
if len(data) > 896 || len(data) < 352 {
s.logger.Error("Invalid quest data length", zap.Int("len", len(data)))
s.Logger.Error("Invalid quest data length", zap.Int("len", len(data)))
continue
} else {
totalCount++
@@ -504,54 +516,54 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
{ID: 1180, Value: 5},
}
tuneValues = append(tuneValues, tuneValue{1020, uint16(s.server.erupeConfig.GameplayOptions.GCPMultiplier * 100)})
tuneValues = append(tuneValues, tuneValue{1020, uint16(config.GetConfig().GameplayOptions.GCPMultiplier * 100)})
tuneValues = append(tuneValues, tuneValue{1029, uint16(s.server.erupeConfig.GameplayOptions.GUrgentRate * 100)})
tuneValues = append(tuneValues, tuneValue{1029, uint16(config.GetConfig().GameplayOptions.GUrgentRate * 100)})
if s.server.erupeConfig.GameplayOptions.DisableHunterNavi {
if config.GetConfig().GameplayOptions.DisableHunterNavi {
tuneValues = append(tuneValues, tuneValue{1037, 1})
}
if s.server.erupeConfig.GameplayOptions.EnableKaijiEvent {
if config.GetConfig().GameplayOptions.EnableKaijiEvent {
tuneValues = append(tuneValues, tuneValue{1106, 1})
}
if s.server.erupeConfig.GameplayOptions.EnableHiganjimaEvent {
if config.GetConfig().GameplayOptions.EnableHiganjimaEvent {
tuneValues = append(tuneValues, tuneValue{1144, 1})
}
if s.server.erupeConfig.GameplayOptions.EnableNierEvent {
if config.GetConfig().GameplayOptions.EnableNierEvent {
tuneValues = append(tuneValues, tuneValue{1153, 1})
}
if s.server.erupeConfig.GameplayOptions.DisableRoad {
if config.GetConfig().GameplayOptions.DisableRoad {
tuneValues = append(tuneValues, tuneValue{1155, 1})
}
// get_hrp_rate_from_rank
tuneValues = append(tuneValues, getTuneValueRange(3000, uint16(s.server.erupeConfig.GameplayOptions.HRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3338, uint16(s.server.erupeConfig.GameplayOptions.HRPMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3000, uint16(config.GetConfig().GameplayOptions.HRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3338, uint16(config.GetConfig().GameplayOptions.HRPMultiplierNC*100))...)
// get_srp_rate_from_rank
tuneValues = append(tuneValues, getTuneValueRange(3013, uint16(s.server.erupeConfig.GameplayOptions.SRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3351, uint16(s.server.erupeConfig.GameplayOptions.SRPMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3013, uint16(config.GetConfig().GameplayOptions.SRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3351, uint16(config.GetConfig().GameplayOptions.SRPMultiplierNC*100))...)
// get_grp_rate_from_rank
tuneValues = append(tuneValues, getTuneValueRange(3026, uint16(s.server.erupeConfig.GameplayOptions.GRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3364, uint16(s.server.erupeConfig.GameplayOptions.GRPMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3026, uint16(config.GetConfig().GameplayOptions.GRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3364, uint16(config.GetConfig().GameplayOptions.GRPMultiplierNC*100))...)
// get_gsrp_rate_from_rank
tuneValues = append(tuneValues, getTuneValueRange(3039, uint16(s.server.erupeConfig.GameplayOptions.GSRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3377, uint16(s.server.erupeConfig.GameplayOptions.GSRPMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3039, uint16(config.GetConfig().GameplayOptions.GSRPMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3377, uint16(config.GetConfig().GameplayOptions.GSRPMultiplierNC*100))...)
// get_zeny_rate_from_hrank
tuneValues = append(tuneValues, getTuneValueRange(3052, uint16(s.server.erupeConfig.GameplayOptions.ZennyMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3390, uint16(s.server.erupeConfig.GameplayOptions.ZennyMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3052, uint16(config.GetConfig().GameplayOptions.ZennyMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3390, uint16(config.GetConfig().GameplayOptions.ZennyMultiplierNC*100))...)
// get_zeny_rate_from_grank
tuneValues = append(tuneValues, getTuneValueRange(3078, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3416, uint16(s.server.erupeConfig.GameplayOptions.GZennyMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3078, uint16(config.GetConfig().GameplayOptions.GZennyMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3416, uint16(config.GetConfig().GameplayOptions.GZennyMultiplierNC*100))...)
// get_reward_rate_from_hrank
tuneValues = append(tuneValues, getTuneValueRange(3104, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3442, uint16(s.server.erupeConfig.GameplayOptions.MaterialMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3104, uint16(config.GetConfig().GameplayOptions.MaterialMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3442, uint16(config.GetConfig().GameplayOptions.MaterialMultiplierNC*100))...)
// get_reward_rate_from_grank
tuneValues = append(tuneValues, getTuneValueRange(3130, uint16(s.server.erupeConfig.GameplayOptions.GMaterialMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3468, uint16(s.server.erupeConfig.GameplayOptions.GMaterialMultiplierNC*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3130, uint16(config.GetConfig().GameplayOptions.GMaterialMultiplier*100))...)
tuneValues = append(tuneValues, getTuneValueRange(3468, uint16(config.GetConfig().GameplayOptions.GMaterialMultiplierNC*100))...)
// get_lottery_rate_from_hrank
tuneValues = append(tuneValues, getTuneValueRange(3156, 0)...)
tuneValues = append(tuneValues, getTuneValueRange(3494, 0)...)
@@ -559,11 +571,11 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
tuneValues = append(tuneValues, getTuneValueRange(3182, 0)...)
tuneValues = append(tuneValues, getTuneValueRange(3520, 0)...)
// get_hagi_rate_from_hrank
tuneValues = append(tuneValues, getTuneValueRange(3208, s.server.erupeConfig.GameplayOptions.ExtraCarves)...)
tuneValues = append(tuneValues, getTuneValueRange(3546, s.server.erupeConfig.GameplayOptions.ExtraCarvesNC)...)
tuneValues = append(tuneValues, getTuneValueRange(3208, config.GetConfig().GameplayOptions.ExtraCarves)...)
tuneValues = append(tuneValues, getTuneValueRange(3546, config.GetConfig().GameplayOptions.ExtraCarvesNC)...)
// get_hagi_rate_from_grank
tuneValues = append(tuneValues, getTuneValueRange(3234, s.server.erupeConfig.GameplayOptions.GExtraCarves)...)
tuneValues = append(tuneValues, getTuneValueRange(3572, s.server.erupeConfig.GameplayOptions.GExtraCarvesNC)...)
tuneValues = append(tuneValues, getTuneValueRange(3234, config.GetConfig().GameplayOptions.GExtraCarves)...)
tuneValues = append(tuneValues, getTuneValueRange(3572, config.GetConfig().GameplayOptions.GExtraCarvesNC)...)
// get_nboost_transcend_rate_from_hrank
tuneValues = append(tuneValues, getTuneValueRange(3286, 200)...)
tuneValues = append(tuneValues, getTuneValueRange(3312, 300)...)
@@ -580,23 +592,23 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
tuneValues = temp
tuneLimit := 770
if _config.ErupeConfig.ClientID <= _config.G1 {
if config.GetConfig().ClientID <= config.G1 {
tuneLimit = 256
} else if _config.ErupeConfig.ClientID <= _config.G3 {
} else if config.GetConfig().ClientID <= config.G3 {
tuneLimit = 283
} else if _config.ErupeConfig.ClientID <= _config.GG {
} else if config.GetConfig().ClientID <= config.GG {
tuneLimit = 315
} else if _config.ErupeConfig.ClientID <= _config.G61 {
} else if config.GetConfig().ClientID <= config.G61 {
tuneLimit = 332
} else if _config.ErupeConfig.ClientID <= _config.G7 {
} else if config.GetConfig().ClientID <= config.G7 {
tuneLimit = 339
} else if _config.ErupeConfig.ClientID <= _config.G81 {
} else if config.GetConfig().ClientID <= config.G81 {
tuneLimit = 396
} else if _config.ErupeConfig.ClientID <= _config.G91 {
} else if config.GetConfig().ClientID <= config.G91 {
tuneLimit = 694
} else if _config.ErupeConfig.ClientID <= _config.G101 {
} else if config.GetConfig().ClientID <= config.G101 {
tuneLimit = 704
} else if _config.ErupeConfig.ClientID <= _config.Z2 {
} else if config.GetConfig().ClientID <= config.Z2 {
tuneLimit = 750
}
if len(tuneValues) > tuneLimit {
@@ -644,7 +656,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
bf.Seek(0, io.SeekStart)
bf.WriteUint16(returnedCount)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func getTuneValueRange(start uint16, value uint16) []tuneValue {
@@ -682,5 +694,5 @@ func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint8(q.Unk6)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"strings"
@@ -10,14 +11,14 @@ func handleMsgMhfRegisterEvent(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegisterEvent)
bf := byteframe.NewByteFrame()
// Some kind of check if there's already a session
if pkt.Unk1 && s.server.getRaviSemaphore() == nil {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
if pkt.Unk1 && s.Server.getRaviSemaphore() == nil {
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf.WriteUint8(uint8(pkt.WorldID))
bf.WriteUint8(uint8(pkt.LandID))
bf.WriteUint16(s.server.raviente.id)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
bf.WriteUint16(s.Server.raviente.id)
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) {
@@ -71,23 +72,23 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
bf = byteframe.NewByteFrame()
var _old, _new uint32
s.server.raviente.Lock()
s.Server.raviente.Lock()
for _, update := range raviUpdates {
switch update.Op {
case 2:
_old, _new = s.server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, true)
_old, _new = s.Server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, true)
case 13, 14:
_old, _new = s.server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, false)
_old, _new = s.Server.UpdateRavi(pkt.SemaphoreID, update.Dest, update.Data, false)
}
bf.WriteUint8(1)
bf.WriteUint8(update.Dest)
bf.WriteUint32(_old)
bf.WriteUint32(_new)
}
s.server.raviente.Unlock()
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
s.Server.raviente.Unlock()
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
if s.server.erupeConfig.GameplayOptions.LowLatencyRaviente {
if config.GetConfig().GameplayOptions.LowLatencyRaviente {
s.notifyRavi()
}
}
@@ -100,31 +101,31 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {
for i := uint8(0); i < pkt.Values; i++ {
switch pkt.RegisterID {
case 0x40000:
bf.WriteUint32(s.server.raviente.state[i])
bf.WriteUint32(s.Server.raviente.state[i])
case 0x50000:
bf.WriteUint32(s.server.raviente.support[i])
bf.WriteUint32(s.Server.raviente.support[i])
case 0x60000:
bf.WriteUint32(s.server.raviente.register[i])
bf.WriteUint32(s.Server.raviente.register[i])
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func (s *Session) notifyRavi() {
sema := s.server.getRaviSemaphore()
sema := s.Server.getRaviSemaphore()
if sema == nil {
return
}
var temp mhfpacket.MHFPacket
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysLoadRegister{RegisterID: uint32(0x40000 + i*0x10000)}
if s.server.erupeConfig.GameplayOptions.LowLatencyRaviente {
if config.GetConfig().GameplayOptions.LowLatencyRaviente {
for session := range sema.clients {
session.QueueSendMHF(temp)
}
} else {
for session := range sema.clients {
if session.charID == s.charID {
if session.CharID == s.CharID {
session.QueueSendMHF(temp)
}
}
@@ -132,7 +133,7 @@ func (s *Session) notifyRavi() {
}
}
func (server *Server) getRaviSemaphore() *Semaphore {
func (server *ChannelServer) getRaviSemaphore() *Semaphore {
for _, semaphore := range server.semaphore {
if strings.HasPrefix(semaphore.name, "hs_l0") && strings.HasSuffix(semaphore.name, "3") {
return semaphore

View File

@@ -1,6 +1,8 @@
package channelserver
import (
"erupe-ce/config"
"erupe-ce/utils/db"
ps "erupe-ce/utils/pascalstring"
"fmt"
"os"
@@ -18,11 +20,15 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
// saved every floor on road, holds values such as floors progressed, points etc.
// can be safely handled by the client
pkt := p.(*mhfpacket.MsgMhfSaveRengokuData)
dumpSaveData(s, pkt.RawDataPayload, "rengoku")
_, err := s.server.db.Exec("UPDATE characters SET rengokudata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to save rengokudata", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
dumpSaveData(s, pkt.RawDataPayload, "rengoku")
_, err = database.Exec("UPDATE characters SET rengokudata=$1 WHERE id=$2", pkt.RawDataPayload, s.CharID)
if err != nil {
s.Logger.Error("Failed to save rengokudata", zap.Error(err))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
@@ -33,23 +39,27 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
maxStageSp := bf.ReadUint32()
maxScoreSp := bf.ReadUint32()
var t int
err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t)
err = database.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.CharID).Scan(&t)
if err != nil {
s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID)
database.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.CharID)
}
s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadRengokuData(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadRengokuData)
var data []byte
err := s.server.db.QueryRow("SELECT rengokudata FROM characters WHERE id = $1", s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.logger.Error("Failed to load rengokudata", zap.Error(err))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT rengokudata FROM characters WHERE id = $1", s.CharID).Scan(&data)
if err != nil {
s.Logger.Error("Failed to load rengokudata", zap.Error(err))
}
if len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
} else {
resp := byteframe.NewByteFrame()
resp.WriteUint32(0)
@@ -87,18 +97,18 @@ func handleMsgMhfLoadRengokuData(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(0)
resp.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
}
func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetRengokuBinary)
// a (massively out of date) version resides in the game's /dat/ folder or up to date can be pulled from packets
data, err := os.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, "rengoku_data.bin"))
data, err := os.ReadFile(filepath.Join(config.GetConfig().BinPath, "rengoku_data.bin"))
if err != nil {
panic(err)
}
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
const rengokuScoreQuery = `, c.name FROM rengoku_score rs
@@ -113,15 +123,15 @@ type RengokuScore struct {
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking)
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
guild, _ := GetGuildInfoByCharacterId(s, s.CharID)
isApplicant, _ := guild.HasApplicationForCharID(s, s.CharID)
if isApplicant {
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))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 11))
return
}
}
@@ -131,25 +141,28 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
i := uint32(1)
bf := byteframe.NewByteFrame()
scoreData := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var rows *sqlx.Rows
switch pkt.Leaderboard {
case 0:
rows, _ = s.server.db.Queryx(fmt.Sprintf("SELECT max_stages_mp AS score %s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
rows, _ = database.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))
rows, _ = database.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)
rows, _ = database.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)
rows, _ = database.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))
rows, _ = database.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))
rows, _ = database.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)
rows, _ = database.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)
rows, _ = database.Queryx(fmt.Sprintf("SELECT max_points_sp AS score %s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
}
for rows.Next() {
@@ -177,7 +190,7 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint8(uint8(i) - 1)
bf.WriteBytes(scoreData.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
@@ -186,5 +199,5 @@ func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(0) // Max stage overall MP rank
bf.WriteUint32(0) // Max RdP overall MP rank
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -6,14 +6,14 @@ func handleMsgSysReserve188(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysReserve188)
// Left as raw bytes because I couldn't easily find the request or resp parser function in the binary.
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgSysReserve18B(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysReserve18B)
// Left as raw bytes because I couldn't easily find the request or resp parser function in the binary.
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x3C})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x3C})
}
func handleMsgSysReserve55(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -12,21 +12,21 @@ func handleMsgMhfGetAdditionalBeatReward(s *Session, p mhfpacket.MHFPacket) {
// Actual response in packet captures are all just giant batches of null bytes
// I'm assuming this is because it used to be tied to an actual event and
// they never bothered killing off the packet when they made it static
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x104))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x104))
}
func handleMsgMhfGetUdRankingRewardList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdRankingRewardList)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfGetRewardSong(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetRewardSong)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfUseRewardSong(s *Session, p mhfpacket.MHFPacket) {}
@@ -39,7 +39,7 @@ func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) {
resp := byteframe.NewByteFrame()
resp.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfAcceptReadReward(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -12,7 +12,7 @@ func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type WeeklySeibatuRankingReward struct {
@@ -40,7 +40,7 @@ func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket
bf.WriteInt32(reward.Unk5)
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket) {
@@ -49,7 +49,7 @@ func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteBytes(make([]byte, 32))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
@@ -65,7 +65,7 @@ func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
resp.WriteUint32(1)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {
@@ -75,13 +75,13 @@ func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel)
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {
@@ -96,11 +96,11 @@ func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(0)
bf.WriteBytes(make([]byte, 32))
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReadBeatLevelMyRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadBeatLevelMyRanking)
bf := byteframe.NewByteFrame()
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -11,39 +11,39 @@ import (
)
func removeSessionFromSemaphore(s *Session) {
s.server.semaphoreLock.Lock()
for _, semaphore := range s.server.semaphore {
s.Server.semaphoreLock.Lock()
for _, semaphore := range s.Server.semaphore {
if _, exists := semaphore.clients[s]; exists {
delete(semaphore.clients, s)
}
}
s.server.semaphoreLock.Unlock()
s.Server.semaphoreLock.Unlock()
}
func handleMsgSysCreateSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateSemaphore)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x03, 0x00, 0x0d})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x03, 0x00, 0x0d})
}
func destructEmptySemaphores(s *Session) {
s.server.semaphoreLock.Lock()
for id, sema := range s.server.semaphore {
s.Server.semaphoreLock.Lock()
for id, sema := range s.Server.semaphore {
if len(sema.clients) == 0 {
delete(s.server.semaphore, id)
delete(s.Server.semaphore, id)
if strings.HasPrefix(id, "hs_l0") {
s.server.resetRaviente()
s.Server.resetRaviente()
}
s.logger.Debug("Destructed semaphore", zap.String("sema.name", id))
s.Logger.Debug("Destructed semaphore", zap.String("sema.name", id))
}
}
s.server.semaphoreLock.Unlock()
s.Server.semaphoreLock.Unlock()
}
func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysDeleteSemaphore)
destructEmptySemaphores(s)
s.server.semaphoreLock.Lock()
for id, sema := range s.server.semaphore {
s.Server.semaphoreLock.Lock()
for id, sema := range s.Server.semaphore {
if sema.id == pkt.SemaphoreID {
for session := range sema.clients {
if s == session {
@@ -51,22 +51,22 @@ func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) {
}
}
if len(sema.clients) == 0 {
delete(s.server.semaphore, id)
delete(s.Server.semaphore, id)
if strings.HasPrefix(id, "hs_l0") {
s.server.resetRaviente()
s.Server.resetRaviente()
}
s.logger.Debug("Destructed semaphore", zap.String("sema.name", id))
s.Logger.Debug("Destructed semaphore", zap.String("sema.name", id))
}
}
}
s.server.semaphoreLock.Unlock()
s.Server.semaphoreLock.Unlock()
}
func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateAcquireSemaphore)
SemaphoreID := pkt.SemaphoreID
if s.server.HasSemaphore(s) {
if s.Server.HasSemaphore(s) {
s.semaphoreMode = !s.semaphoreMode
}
if s.semaphoreMode {
@@ -75,22 +75,22 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
s.semaphoreID[0]++
}
newSemaphore, exists := s.server.semaphore[SemaphoreID]
newSemaphore, exists := s.Server.semaphore[SemaphoreID]
if !exists {
s.server.semaphoreLock.Lock()
s.Server.semaphoreLock.Lock()
if strings.HasPrefix(SemaphoreID, "hs_l0") {
suffix, _ := strconv.Atoi(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:])
s.server.semaphore[SemaphoreID] = &Semaphore{
s.Server.semaphore[SemaphoreID] = &Semaphore{
name: pkt.SemaphoreID,
id: uint32((suffix + 1) * 0x10000),
clients: make(map[*Session]uint32),
maxPlayers: 127,
}
} else {
s.server.semaphore[SemaphoreID] = NewSemaphore(s, SemaphoreID, 1)
s.Server.semaphore[SemaphoreID] = NewSemaphore(s, SemaphoreID, 1)
}
newSemaphore = s.server.semaphore[SemaphoreID]
s.server.semaphoreLock.Unlock()
newSemaphore = s.Server.semaphore[SemaphoreID]
s.Server.semaphoreLock.Unlock()
}
newSemaphore.Lock()
@@ -99,7 +99,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
if _, exists := newSemaphore.clients[s]; exists {
bf.WriteUint32(newSemaphore.id)
} else if uint16(len(newSemaphore.clients)) < newSemaphore.maxPlayers {
newSemaphore.clients[s] = s.charID
newSemaphore.clients[s] = s.CharID
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
@@ -107,18 +107,18 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
} else {
bf.WriteUint32(0)
}
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysAcquireSemaphore)
if sema, exists := s.server.semaphore[pkt.SemaphoreID]; exists {
if sema, exists := s.Server.semaphore[pkt.SemaphoreID]; exists {
sema.host = s
bf := byteframe.NewByteFrame()
bf.WriteUint32(sema.id)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
}
@@ -129,10 +129,10 @@ func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCheckSemaphore)
resp := []byte{0x00, 0x00, 0x00, 0x00}
s.server.semaphoreLock.Lock()
if _, exists := s.server.semaphore[pkt.SemaphoreID]; exists {
s.Server.semaphoreLock.Lock()
if _, exists := s.Server.semaphore[pkt.SemaphoreID]; exists {
resp = []byte{0x00, 0x00, 0x00, 0x01}
}
s.server.semaphoreLock.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, resp)
s.Server.semaphoreLock.Unlock()
DoAckSimpleSucceed(s, pkt.AckHandle, resp)
}

View File

@@ -1,11 +1,13 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
ps "erupe-ce/utils/pascalstring"
"fmt"
"math/rand"
)
@@ -62,7 +64,7 @@ func writeShopItems(bf *byteframe.ByteFrame, items []ShopItem) {
bf.WriteUint16(uint16(len(items)))
bf.WriteUint16(uint16(len(items)))
for _, item := range items {
if _config.ErupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
bf.WriteUint32(item.ID)
}
bf.WriteUint32(item.ItemID)
@@ -70,19 +72,19 @@ func writeShopItems(bf *byteframe.ByteFrame, items []ShopItem) {
bf.WriteUint16(item.Quantity)
bf.WriteUint16(item.MinHR)
bf.WriteUint16(item.MinSR)
if _config.ErupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
bf.WriteUint16(item.MinGR)
}
bf.WriteUint8(0) // Unk
bf.WriteUint8(item.StoreLevel)
if _config.ErupeConfig.ClientID >= _config.Z2 {
if config.GetConfig().ClientID >= config.Z2 {
bf.WriteUint16(item.MaxQuantity)
bf.WriteUint16(item.UsedQuantity)
}
if _config.ErupeConfig.ClientID == _config.Z1 {
if config.GetConfig().ClientID == config.Z1 {
bf.WriteUint8(uint8(item.RoadFloors))
bf.WriteUint8(uint8(item.RoadFatalis))
} else if _config.ErupeConfig.ClientID >= _config.Z2 {
} else if config.GetConfig().ClientID >= config.Z2 {
bf.WriteUint16(item.RoadFloors)
bf.WriteUint16(item.RoadFatalis)
}
@@ -92,10 +94,14 @@ func writeShopItems(bf *byteframe.ByteFrame, items []ShopItem) {
func getShopItems(s *Session, shopType uint8, shopID uint32) []ShopItem {
var items []ShopItem
var temp ShopItem
rows, err := s.server.db.Queryx(`SELECT id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity,
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`SELECT id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity,
COALESCE((SELECT bought FROM shop_items_bought WHERE shop_item_id=si.id AND character_id=$3), 0) as used_quantity,
road_floors, road_fatalis FROM shop_items si WHERE shop_type=$1 AND shop_id=$2
`, shopType, shopID, s.charID)
`, shopType, shopID, s.CharID)
if err == nil {
for rows.Next() {
err = rows.StructScan(&temp)
@@ -110,6 +116,10 @@ func getShopItems(s *Session, shopType uint8, shopID uint32) []ShopItem {
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
// Generic Shop IDs
// 0: basic item
// 1: gatherables
@@ -123,14 +133,14 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
switch pkt.ShopType {
case 1: // Running gachas
// Fundamentally, gacha works completely differently, just hide it for now.
if _config.ErupeConfig.ClientID <= _config.G7 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
if config.GetConfig().ClientID <= config.G7 {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
rows, err := s.server.db.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop")
rows, err := database.Queryx("SELECT id, min_gr, min_hr, name, url_banner, url_feature, url_thumbnail, wide, recommended, gacha_type, hidden FROM gacha_shop")
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
bf := byteframe.NewByteFrame()
@@ -156,7 +166,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
ps.Uint8(bf, g.Name, true)
ps.Uint8(bf, g.URLBanner, false)
ps.Uint8(bf, g.URLFeature, false)
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
bf.WriteBool(g.Wide)
ps.Uint8(bf, g.URLThumbnail, false)
}
@@ -166,23 +176,23 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(0)
}
bf.WriteUint8(g.GachaType)
if _config.ErupeConfig.ClientID >= _config.G10 {
if config.GetConfig().ClientID >= config.G10 {
bf.WriteBool(g.Hidden)
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
case 2: // Actual gacha
bf := byteframe.NewByteFrame()
bf.WriteUint32(pkt.ShopID)
var gachaType int
s.server.db.QueryRow(`SELECT gacha_type FROM gacha_shop WHERE id = $1`, pkt.ShopID).Scan(&gachaType)
rows, err := s.server.db.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points, COALESCE(name, '') AS name FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID)
database.QueryRow(`SELECT gacha_type FROM gacha_shop WHERE id = $1`, pkt.ShopID).Scan(&gachaType)
rows, err := database.Queryx(`SELECT entry_type, id, item_type, item_number, item_quantity, weight, rarity, rolls, daily_limit, frontier_points, COALESCE(name, '') AS name FROM gacha_entries WHERE gacha_id = $1 ORDER BY weight DESC`, pkt.ShopID)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
var divisor float64
s.server.db.QueryRow(`SELECT COALESCE(SUM(weight) / 100000.0, 0) AS chance FROM gacha_entries WHERE gacha_id = $1`, pkt.ShopID).Scan(&divisor)
database.QueryRow(`SELECT COALESCE(SUM(weight) / 100000.0, 0) AS chance FROM gacha_entries WHERE gacha_id = $1`, pkt.ShopID).Scan(&divisor)
var entry GachaEntry
var entries []GachaEntry
@@ -209,7 +219,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(ge.Rarity)
bf.WriteUint8(ge.Rolls)
rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, ge.ID)
rows, err = database.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id=$1`, ge.ID)
if err != nil {
bf.WriteUint8(0)
} else {
@@ -235,7 +245,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(gi.Quantity)
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
case 3: // Hunting Festival Exchange
fallthrough
case 4: // N Points, 0-6
@@ -257,7 +267,7 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
items = items[:pkt.Limit]
}
writeShopItems(bf, items)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
}
@@ -265,57 +275,73 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
exchanges := int(bf.ReadUint16())
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for i := 0; i < exchanges; i++ {
itemHash := bf.ReadUint32()
if itemHash == 0 {
continue
}
buyCount := bf.ReadUint32()
s.server.db.Exec(`INSERT INTO shop_items_bought (character_id, shop_item_id, bought)
database.Exec(`INSERT INTO shop_items_bought (character_id, shop_item_id, bought)
VALUES ($1,$2,$3) ON CONFLICT (character_id, shop_item_id)
DO UPDATE SET bought = bought + $3
WHERE EXCLUDED.character_id=$1 AND EXCLUDED.shop_item_id=$2
`, s.charID, itemHash, buyCount)
`, s.CharID, itemHash, buyCount)
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
bf := byteframe.NewByteFrame()
bf.WriteUint8(1)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGachaPoint)
var fp, gp, gt uint32
s.server.db.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_premium, 0), COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.charID).Scan(&fp, &gp, &gt)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow("SELECT COALESCE(frontier_points, 0), COALESCE(gacha_premium, 0), COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)", s.CharID).Scan(&fp, &gp, &gt)
resp := byteframe.NewByteFrame()
resp.WriteUint32(gp)
resp.WriteUint32(gt)
resp.WriteUint32(fp)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUseGachaPoint)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if pkt.TrialCoins > 0 {
s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.TrialCoins, s.charID)
database.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.TrialCoins, s.CharID)
}
if pkt.PremiumCoins > 0 {
s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.PremiumCoins, s.charID)
database.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, pkt.PremiumCoins, s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func spendGachaCoin(s *Session, quantity uint16) {
var gt uint16
s.server.db.QueryRow(`SELECT COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&gt)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow(`SELECT COALESCE(gacha_trial, 0) FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&gt)
if quantity <= gt {
s.server.db.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID)
database.Exec(`UPDATE users u SET gacha_trial=gacha_trial-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.CharID)
} else {
s.server.db.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.charID)
database.Exec(`UPDATE users u SET gacha_premium=gacha_premium-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, quantity, s.CharID)
}
}
@@ -323,7 +349,11 @@ func transactGacha(s *Session, gachaID uint32, rollID uint8) (error, int) {
var itemType uint8
var itemNumber uint16
var rolls int
err := s.server.db.QueryRowx(`SELECT item_type, item_number, rolls FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, gachaID, rollID).Scan(&itemType, &itemNumber, &rolls)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRowx(`SELECT item_type, item_number, rolls FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, gachaID, rollID).Scan(&itemType, &itemNumber, &rolls)
if err != nil {
return err, 0
}
@@ -344,7 +374,7 @@ func transactGacha(s *Session, gachaID uint32, rollID uint8) (error, int) {
case 20:
spendGachaCoin(s, itemNumber)
case 21:
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", itemNumber, s.charID)
database.Exec("UPDATE users u SET frontier_points=frontier_points-$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)", itemNumber, s.CharID)
}
return nil, rolls
}
@@ -352,7 +382,11 @@ func transactGacha(s *Session, gachaID uint32, rollID uint8) (error, int) {
func getGuaranteedItems(s *Session, gachaID uint32, rollID uint8) []GachaItem {
var rewards []GachaItem
var reward GachaItem
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = (SELECT id FROM gacha_entries WHERE entry_type = $1 AND gacha_id = $2)`, rollID, gachaID)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
items, err := database.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = (SELECT id FROM gacha_entries WHERE entry_type = $1 AND gacha_id = $2)`, rollID, gachaID)
if err == nil {
for items.Next() {
items.StructScan(&reward)
@@ -364,7 +398,11 @@ func getGuaranteedItems(s *Session, gachaID uint32, rollID uint8) []GachaItem {
func addGachaItem(s *Session, items []GachaItem) {
var data []byte
s.server.db.QueryRow(`SELECT gacha_items FROM characters WHERE id = $1`, s.charID).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow(`SELECT gacha_items FROM characters WHERE id = $1`, s.CharID).Scan(&data)
if len(data) > 0 {
numItems := int(data[0])
data = data[1:]
@@ -384,7 +422,7 @@ func addGachaItem(s *Session, items []GachaItem) {
newItem.WriteUint16(items[i].ItemID)
newItem.WriteUint16(items[i].Quantity)
}
s.server.db.Exec(`UPDATE characters SET gacha_items = $1 WHERE id = $2`, newItem.Data(), s.charID)
database.Exec(`UPDATE characters SET gacha_items = $1 WHERE id = $2`, newItem.Data(), s.CharID)
}
func getRandomEntries(entries []GachaEntry, rolls int, isBox bool) ([]GachaEntry, error) {
@@ -419,7 +457,11 @@ func getRandomEntries(entries []GachaEntry, rolls int, isBox bool) ([]GachaEntry
func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReceiveGachaItem)
var data []byte
err := s.server.db.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.charID, []byte{0x00}).Scan(&data)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT COALESCE(gacha_items, $2) FROM characters WHERE id = $1", s.CharID, []byte{0x00}).Scan(&data)
if err != nil {
data = []byte{0x00}
}
@@ -429,9 +471,9 @@ func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) {
resp := byteframe.NewByteFrame()
resp.WriteUint8(36)
resp.WriteBytes(data[1:181])
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
DoAckBufSucceed(s, pkt.AckHandle, resp.Data())
} else {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
if !pkt.Freeze {
@@ -439,9 +481,9 @@ func handleMsgMhfReceiveGachaItem(s *Session, p mhfpacket.MHFPacket) {
update := byteframe.NewByteFrame()
update.WriteUint8(uint8(len(data[181:]) / 5))
update.WriteBytes(data[181:])
s.server.db.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", update.Data(), s.charID)
database.Exec("UPDATE characters SET gacha_items = $1 WHERE id = $2", update.Data(), s.CharID)
} else {
s.server.db.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.charID)
database.Exec("UPDATE characters SET gacha_items = null WHERE id = $1", s.CharID)
}
}
}
@@ -453,15 +495,19 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) {
var entry GachaEntry
var rewards []GachaItem
var reward GachaItem
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
rows, err := database.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
for rows.Next() {
@@ -475,7 +521,7 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) {
rewardEntries, err := getRandomEntries(entries, rolls, false)
temp := byteframe.NewByteFrame()
for i := range rewardEntries {
rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
rows, err = database.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
if err != nil {
continue
}
@@ -494,7 +540,7 @@ func handleMsgMhfPlayNormalGacha(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(uint8(len(rewards)))
bf.WriteBytes(temp.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
addGachaItem(s, rewards)
}
@@ -507,16 +553,20 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
var reward GachaItem
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
s.server.db.Exec("UPDATE users u SET frontier_points=frontier_points+(SELECT frontier_points FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.GachaID, pkt.RollType, s.charID)
s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
s.server.db.Exec(`INSERT INTO gacha_stepup (gacha_id, step, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, pkt.RollType+1, s.charID)
rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
database, err := db.GetDB()
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("UPDATE users u SET frontier_points=frontier_points+(SELECT frontier_points FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$3)", pkt.GachaID, pkt.RollType, s.CharID)
database.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.CharID)
database.Exec(`INSERT INTO gacha_stepup (gacha_id, step, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, pkt.RollType+1, s.CharID)
rows, err := database.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
if err != nil {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
for rows.Next() {
@@ -531,7 +581,7 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
rewardEntries, err := getRandomEntries(entries, rolls, false)
temp := byteframe.NewByteFrame()
for i := range rewardEntries {
rows, err = s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
rows, err = database.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
if err != nil {
continue
}
@@ -557,7 +607,7 @@ func handleMsgMhfPlayStepupGacha(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(0)
}
bf.WriteBytes(temp.Data())
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
addGachaItem(s, rewards)
addGachaItem(s, guaranteedItems)
}
@@ -566,24 +616,32 @@ func handleMsgMhfGetStepupStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetStepupStatus)
// TODO: Reset daily (noon)
var step uint8
s.server.db.QueryRow(`SELECT step FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID).Scan(&step)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow(`SELECT step FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.CharID).Scan(&step)
var stepCheck int
s.server.db.QueryRow(`SELECT COUNT(1) FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, pkt.GachaID, step).Scan(&stepCheck)
database.QueryRow(`SELECT COUNT(1) FROM gacha_entries WHERE gacha_id = $1 AND entry_type = $2`, pkt.GachaID, step).Scan(&stepCheck)
if stepCheck == 0 {
s.server.db.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
database.Exec(`DELETE FROM gacha_stepup WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.CharID)
step = 0
}
bf := byteframe.NewByteFrame()
bf.WriteUint8(step)
bf.WriteUint32(uint32(gametime.TimeAdjusted().Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBoxGachaInfo)
entries, err := s.server.db.Queryx(`SELECT entry_id FROM gacha_box WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.charID)
database, err := db.GetDB()
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
entries, err := database.Queryx(`SELECT entry_id FROM gacha_box WHERE gacha_id = $1 AND character_id = $2`, pkt.GachaID, s.CharID)
if err != nil {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
var entryIDs []uint32
@@ -598,7 +656,7 @@ func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(entryIDs[i])
bf.WriteBool(true)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
@@ -610,12 +668,16 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
var reward GachaItem
err, rolls := transactGacha(s, pkt.GachaID, pkt.RollType)
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
rows, err := s.server.db.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
database, err := db.GetDB()
if err != nil {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`SELECT id, weight, rarity FROM gacha_entries WHERE gacha_id = $1 AND entry_type = 100 ORDER BY weight DESC`, pkt.GachaID)
if err != nil {
DoAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
return
}
for rows.Next() {
@@ -626,11 +688,11 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
}
rewardEntries, err := getRandomEntries(entries, rolls, true)
for i := range rewardEntries {
items, err := s.server.db.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
items, err := database.Queryx(`SELECT item_type, item_id, quantity FROM gacha_items WHERE entry_id = $1`, rewardEntries[i].ID)
if err != nil {
continue
}
s.server.db.Exec(`INSERT INTO gacha_box (gacha_id, entry_id, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, rewardEntries[i].ID, s.charID)
database.Exec(`INSERT INTO gacha_box (gacha_id, entry_id, character_id) VALUES ($1, $2, $3)`, pkt.GachaID, rewardEntries[i].ID, s.CharID)
for items.Next() {
err = items.StructScan(&reward)
if err == nil {
@@ -645,38 +707,50 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(r.Quantity)
bf.WriteUint8(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
addGachaItem(s, rewards)
}
func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfResetBoxGachaInfo)
s.server.db.Exec("DELETE FROM gacha_box WHERE gacha_id = $1 AND character_id = $2", pkt.GachaID, s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.Exec("DELETE FROM gacha_box WHERE gacha_id = $1 AND character_id = $2", pkt.GachaID, s.CharID)
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfExchangeFpoint2Item(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeFpoint2Item)
var balance uint32
var itemValue, quantity int
s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
cost := (int(pkt.Quantity) * quantity) * itemValue
s.server.db.QueryRow("UPDATE users u SET frontier_points=frontier_points::int - $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance)
database.QueryRow("UPDATE users u SET frontier_points=frontier_points::int - $1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.CharID).Scan(&balance)
bf := byteframe.NewByteFrame()
bf.WriteUint32(balance)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfExchangeItem2Fpoint(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeItem2Fpoint)
var balance uint32
var itemValue, quantity int
s.server.db.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow("SELECT quantity, fpoints FROM fpoint_items WHERE id=$1", pkt.TradeID).Scan(&quantity, &itemValue)
cost := (int(pkt.Quantity) / quantity) * itemValue
s.server.db.QueryRow("UPDATE users u SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.charID).Scan(&balance)
database.QueryRow("UPDATE users u SET frontier_points=COALESCE(frontier_points::int + $1, $1) WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2) RETURNING frontier_points", cost, s.CharID).Scan(&balance)
bf := byteframe.NewByteFrame()
bf.WriteUint32(balance)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
type FPointExchange struct {
@@ -695,7 +769,11 @@ func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) {
var exchange FPointExchange
var exchanges []FPointExchange
var buyables uint16
rows, err := s.server.db.Queryx(`SELECT id, item_type, item_id, quantity, fpoints, buyable FROM fpoint_items ORDER BY buyable DESC`)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, err := database.Queryx(`SELECT id, item_type, item_id, quantity, fpoints, buyable FROM fpoint_items ORDER BY buyable DESC`)
if err == nil {
for rows.Next() {
err = rows.StructScan(&exchange)
@@ -708,7 +786,7 @@ func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) {
exchanges = append(exchanges, exchange)
}
}
if _config.ErupeConfig.ClientID <= _config.Z2 {
if config.GetConfig().ClientID <= config.Z2 {
bf.WriteUint8(uint8(len(exchanges)))
bf.WriteUint8(uint8(buyables))
} else {
@@ -726,12 +804,12 @@ func handleMsgMhfGetFpointExchangeList(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(e.FPoints)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfPlayFreeGacha(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPlayFreeGacha)
bf := byteframe.NewByteFrame()
bf.WriteUint32(1)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -14,38 +14,38 @@ import (
func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateStage)
s.server.Lock()
defer s.server.Unlock()
if _, exists := s.server.stages[pkt.StageID]; exists {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
s.Server.Lock()
defer s.Server.Unlock()
if _, exists := s.Server.stages[pkt.StageID]; exists {
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})
s.Server.stages[stage.id] = stage
DoAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
}
func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {}
func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
s.server.Lock()
stage, exists := s.server.stages[stageID]
s.server.Unlock()
s.Server.Lock()
stage, exists := s.Server.stages[stageID]
s.Server.Unlock()
if exists {
stage.Lock()
stage.clients[s] = s.charID
stage.clients[s] = s.CharID
stage.Unlock()
} else { // Create new stage object
s.server.Lock()
s.server.stages[stageID] = NewStage(stageID)
stage = s.server.stages[stageID]
s.server.Unlock()
s.Server.Lock()
s.Server.stages[stageID] = NewStage(stageID)
stage = s.Server.stages[stageID]
s.Server.Unlock()
stage.Lock()
stage.host = s
stage.clients[s] = s.charID
stage.clients[s] = s.CharID
stage.Unlock()
}
@@ -56,14 +56,14 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
// Save our new stage ID and pointer to the new stage itself.
s.Lock()
s.stage = s.server.stages[stageID]
s.stage = s.Server.stages[stageID]
s.Unlock()
// Tell the client to cleanup its current stage objects.
s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{})
// Confirm the stage entry.
doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00})
DoAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00})
var temp mhfpacket.MHFPacket
@@ -71,15 +71,15 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
if !s.userEnteredStage {
s.userEnteredStage = true
for _, session := range s.server.sessions {
for _, session := range s.Server.sessions {
if s == session {
continue
}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.CharID}
s.QueueSendMHF(temp)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
CharID: session.CharID,
BinaryType: uint8(i + 1),
}
s.QueueSendMHF(temp)
@@ -89,10 +89,10 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
if s.stage != nil { // avoids lock up when using bed for dream quests
// Notify the client to duplicate the existing objects.
s.logger.Info(fmt.Sprintf("Sending existing stage objects to %s", s.Name))
s.Logger.Info(fmt.Sprintf("Sending existing stage objects to %s", s.Name))
s.stage.RLock()
for _, obj := range s.stage.objects {
if obj.ownerCharID == s.charID {
if obj.ownerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDuplicateObject{
@@ -110,14 +110,14 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
}
func destructEmptyStages(s *Session) {
s.server.Lock()
defer s.server.Unlock()
for _, stage := range s.server.stages {
s.Server.Lock()
defer s.Server.Unlock()
for _, stage := range s.Server.stages {
// Destroy empty Quest/My series/Guild stages.
if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" || stage.id[3:5] == "Ls" {
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
delete(s.server.stages, stage.id)
s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
delete(s.Server.stages, stage.id)
s.Logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
}
}
}
@@ -128,9 +128,9 @@ func removeSessionFromStage(s *Session) {
delete(s.stage.clients, s)
// Delete old stage objects owned by the client.
s.logger.Info("Sending notification to old stage clients")
s.Logger.Info("Sending notification to old stage clients")
for _, object := range s.stage.objects {
if object.ownerCharID == s.charID {
if object.ownerCharID == s.CharID {
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ObjID: object.id}, s)
delete(s.stage.objects, object.ownerCharID)
}
@@ -140,8 +140,8 @@ func removeSessionFromStage(s *Session) {
}
func isStageFull(s *Session, StageID string) bool {
if stage, exists := s.server.stages[StageID]; exists {
if _, exists := stage.reservedClientSlots[s.charID]; exists {
if stage, exists := s.Server.stages[StageID]; exists {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
return false
}
return len(stage.reservedClientSlots)+len(stage.clients) >= int(stage.maxPlayers)
@@ -153,14 +153,14 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage)
if isStageFull(s, pkt.StageID) {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
DoAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
// Push our current stage ID to the movement stack before entering another one.
if s.stage != nil {
s.stage.Lock()
s.stage.reservedClientSlots[s.charID] = false
s.stage.reservedClientSlots[s.CharID] = false
s.stage.Unlock()
s.stageMoveStack.Push(s.stage.id)
}
@@ -183,16 +183,16 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
if isStageFull(s, backStage) {
s.stageMoveStack.Push(backStage)
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
DoAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
delete(s.stage.reservedClientSlots, s.charID)
if _, exists := s.stage.reservedClientSlots[s.CharID]; exists {
delete(s.stage.reservedClientSlots, s.CharID)
}
if _, exists := s.server.stages[backStage].reservedClientSlots[s.charID]; exists {
delete(s.server.stages[backStage].reservedClientSlots, s.charID)
if _, exists := s.Server.stages[backStage].reservedClientSlots[s.CharID]; exists {
delete(s.Server.stages[backStage].reservedClientSlots, s.CharID)
}
doStageTransfer(s, pkt.AckHandle, backStage)
@@ -202,7 +202,7 @@ func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysMoveStage)
if isStageFull(s, pkt.StageID) {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
DoAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
@@ -213,12 +213,12 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLockStage)
if stage, exists := s.server.stages[pkt.StageID]; exists {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
stage.locked = true
stage.Unlock()
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
@@ -227,13 +227,13 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
defer s.reservationStage.RUnlock()
for charID := range s.reservationStage.reservedClientSlots {
session := s.server.FindSessionByCharID(charID)
session := s.Server.FindSessionByCharID(charID)
if session != nil {
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
}
delete(s.server.stages, s.reservationStage.id)
delete(s.Server.stages, s.reservationStage.id)
}
destructEmptyStages(s)
@@ -241,40 +241,40 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysReserveStage)
if stage, exists := s.server.stages[pkt.StageID]; exists {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
defer stage.Unlock()
if _, exists := stage.reservedClientSlots[s.charID]; exists {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
switch pkt.Ready {
case 1: // 0x01
stage.reservedClientSlots[s.charID] = false
stage.reservedClientSlots[s.CharID] = false
case 17: // 0x11
stage.reservedClientSlots[s.charID] = true
stage.reservedClientSlots[s.CharID] = true
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
if stage.locked {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
if len(stage.password) > 0 {
if stage.password != s.stagePass {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
}
stage.reservedClientSlots[s.charID] = false
stage.reservedClientSlots[s.CharID] = false
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
s.Lock()
s.reservationStage = stage
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
} else {
s.logger.Error("Failed to get stage", zap.String("StageID", pkt.StageID))
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
s.Logger.Error("Failed to get stage", zap.String("StageID", pkt.StageID))
DoAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
}
@@ -285,8 +285,8 @@ func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {
s.Unlock()
if stage != nil {
stage.Lock()
if _, exists := stage.reservedClientSlots[s.charID]; exists {
delete(stage.reservedClientSlots, s.charID)
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
delete(stage.reservedClientSlots, s.CharID)
}
stage.Unlock()
}
@@ -300,7 +300,7 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
if stage != nil {
stage.Lock()
// Will only exist if host.
if _, exists := stage.reservedClientSlots[s.charID]; exists {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
stage.password = pkt.Password
}
stage.Unlock()
@@ -314,78 +314,78 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetStageBinary)
if stage, exists := s.server.stages[pkt.StageID]; exists {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload
stage.Unlock()
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
s.Logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
}
func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysGetStageBinary)
if stage, exists := s.server.stages[pkt.StageID]; exists {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
if binaryData, exists := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]; exists {
doAckBufSucceed(s, pkt.AckHandle, binaryData)
DoAckBufSucceed(s, pkt.AckHandle, binaryData)
} else if pkt.BinaryType1 == 4 {
// Unknown binary type that is supposedly generated server side
// Temporary response
doAckBufSucceed(s, pkt.AckHandle, []byte{})
DoAckBufSucceed(s, pkt.AckHandle, []byte{})
} else {
s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
s.logger.Warn("Sending blank stage binary")
doAckBufSucceed(s, pkt.AckHandle, []byte{})
s.Logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
s.Logger.Warn("Sending blank stage binary")
DoAckBufSucceed(s, pkt.AckHandle, []byte{})
}
stage.Unlock()
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
s.Logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
s.logger.Debug("MsgSysGetStageBinary Done!")
s.Logger.Debug("MsgSysGetStageBinary Done!")
}
func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysWaitStageBinary)
if stage, exists := s.server.stages[pkt.StageID]; exists {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
if pkt.BinaryType0 == 1 && pkt.BinaryType1 == 12 {
// This might contain the hunter count, or max player count?
doAckBufSucceed(s, pkt.AckHandle, []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
DoAckBufSucceed(s, pkt.AckHandle, []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
return
}
for {
s.logger.Debug("MsgSysWaitStageBinary before lock and get stage")
s.Logger.Debug("MsgSysWaitStageBinary before lock and get stage")
stage.Lock()
stageBinary, gotBinary := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
stage.Unlock()
s.logger.Debug("MsgSysWaitStageBinary after lock and get stage")
s.Logger.Debug("MsgSysWaitStageBinary after lock and get stage")
if gotBinary {
doAckBufSucceed(s, pkt.AckHandle, stageBinary)
DoAckBufSucceed(s, pkt.AckHandle, stageBinary)
break
} else {
s.logger.Debug("Waiting stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
s.Logger.Debug("Waiting stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
time.Sleep(1 * time.Second)
continue
}
}
} else {
s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
s.Logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
}
s.logger.Debug("MsgSysWaitStageBinary Done!")
s.Logger.Debug("MsgSysWaitStageBinary Done!")
}
func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnumerateStage)
// Read-lock the server stage map.
s.server.stagesLock.RLock()
defer s.server.stagesLock.RUnlock()
s.Server.stagesLock.RLock()
defer s.Server.stagesLock.RUnlock()
// Build the response
bf := byteframe.NewByteFrame()
var joinable uint16
bf.WriteUint16(0)
for sid, stage := range s.server.stages {
for sid, stage := range s.Server.stages {
stage.RLock()
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
@@ -420,5 +420,5 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
bf.Seek(0, 0)
bf.WriteUint16(joinable)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

File diff suppressed because one or more lines are too long

View File

@@ -103,12 +103,12 @@ func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) {
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEntryTournament)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
type TournamentReward struct {
@@ -127,5 +127,5 @@ func handleMsgMhfAcquireTournament(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(reward.Unk1)
bf.WriteUint16(reward.Unk2)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
DoAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -1,7 +1,7 @@
package channelserver
import (
_config "erupe-ce/config"
"erupe-ce/config"
"fmt"
"strings"
"time"
@@ -10,6 +10,7 @@ import (
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/stringsupport"
)
@@ -59,15 +60,18 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
History: []TowerInfoHistory{{make([]int16, 5), make([]int16, 5)}},
Level: []TowerInfoLevel{{0, 0, 0, 0}, {0, 0, 0, 0}},
}
var tempSkills string
err := s.server.db.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), COALESCE(skills, $1) FROM tower WHERE char_id=$2
`, EmptyTowerCSV(64), s.charID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills)
database, err := db.GetDB()
if err != nil {
s.server.db.Exec(`INSERT INTO tower (char_id) VALUES ($1)`, s.charID)
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var tempSkills string
err = database.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), COALESCE(skills, $1) FROM tower WHERE char_id=$2
`, EmptyTowerCSV(64), s.CharID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills)
if err != nil {
database.Exec(`INSERT INTO tower (char_id) VALUES ($1)`, s.CharID)
}
if _config.ErupeConfig.ClientID <= _config.G7 {
if config.GetConfig().ClientID <= config.G7 {
towerInfo.Level = towerInfo.Level[:1]
}
@@ -113,14 +117,14 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostTowerInfo)
if s.server.erupeConfig.DebugOptions.QuestTools {
s.logger.Debug(
if config.GetConfig().DebugOptions.QuestTools {
s.Logger.Debug(
p.Opcode().String(),
zap.Uint32("InfoType", pkt.InfoType),
zap.Uint32("Unk1", pkt.Unk1),
@@ -134,17 +138,20 @@ func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
zap.Int64("Unk9", pkt.Unk9),
)
}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.InfoType {
case 2:
var skills string
s.server.db.QueryRow(`SELECT COALESCE(skills, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(64), s.charID).Scan(&skills)
s.server.db.Exec(`UPDATE tower SET skills=$1, tsp=tsp-$2 WHERE char_id=$3`, stringsupport.CSVSetIndex(skills, int(pkt.Skill), stringsupport.CSVGetIndex(skills, int(pkt.Skill))+1), pkt.Cost, s.charID)
database.QueryRow(`SELECT COALESCE(skills, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(64), s.CharID).Scan(&skills)
database.Exec(`UPDATE tower SET skills=$1, tsp=tsp-$2 WHERE char_id=$3`, stringsupport.CSVSetIndex(skills, int(pkt.Skill), stringsupport.CSVGetIndex(skills, int(pkt.Skill))+1), pkt.Cost, s.CharID)
case 1, 7:
// This might give too much TSP? No idea what the rate is supposed to be
s.server.db.Exec(`UPDATE tower SET tr=$1, trp=COALESCE(trp, 0)+$2, tsp=COALESCE(tsp, 0)+$3, block1=COALESCE(block1, 0)+$4 WHERE char_id=$5`, pkt.TR, pkt.TRP, pkt.Cost, pkt.Block1, s.charID)
database.Exec(`UPDATE tower SET tr=$1, trp=COALESCE(trp, 0)+$2, tsp=COALESCE(tsp, 0)+$3, block1=COALESCE(block1, 0)+$4 WHERE char_id=$5`, pkt.TR, pkt.TRP, pkt.Cost, pkt.Block1, s.CharID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
// Default missions
@@ -250,7 +257,10 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
Data: tenrouiraiData,
Ticket: []TenrouiraiTicket{{0, 0, 0}},
}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch pkt.Unk1 {
case 1:
for _, tdata := range tenrouirai.Data {
@@ -284,8 +294,8 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
case 4:
s.server.db.QueryRow(`SELECT tower_mission_page FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Page)
s.server.db.QueryRow(`SELECT SUM(tower_mission_1) AS _, SUM(tower_mission_2) AS _, SUM(tower_mission_3) AS _ FROM guild_characters WHERE guild_id=$1
database.QueryRow(`SELECT tower_mission_page FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Page)
database.QueryRow(`SELECT SUM(tower_mission_1) AS _, SUM(tower_mission_2) AS _, SUM(tower_mission_3) AS _ FROM guild_characters WHERE guild_id=$1
`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Mission1, &tenrouirai.Progress[0].Mission2, &tenrouirai.Progress[0].Mission3)
if tenrouirai.Progress[0].Mission1 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal {
@@ -313,7 +323,7 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
pkt.Unk3 = 3
}
}
rows, _ := s.server.db.Query(fmt.Sprintf(`SELECT name, tower_mission_%d FROM guild_characters gc INNER JOIN characters c ON gc.character_id = c.id WHERE guild_id=$1 AND tower_mission_%d IS NOT NULL ORDER BY tower_mission_%d DESC`, pkt.Unk3, pkt.Unk3, pkt.Unk3), pkt.GuildID)
rows, _ := database.Query(fmt.Sprintf(`SELECT name, tower_mission_%d FROM guild_characters gc INNER JOIN characters c ON gc.character_id = c.id WHERE guild_id=$1 AND tower_mission_%d IS NOT NULL ORDER BY tower_mission_%d DESC`, pkt.Unk3, pkt.Unk3, pkt.Unk3), pkt.GuildID)
for rows.Next() {
temp := TenrouiraiCharScore{}
rows.Scan(&temp.Name, &temp.Score)
@@ -326,7 +336,7 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
case 6:
s.server.db.QueryRow(`SELECT tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Ticket[0].RP)
database.QueryRow(`SELECT tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Ticket[0].RP)
for _, ticket := range tenrouirai.Ticket {
bf := byteframe.NewByteFrame()
bf.WriteUint8(ticket.Unk0)
@@ -336,14 +346,17 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
if s.server.erupeConfig.DebugOptions.QuestTools {
s.logger.Debug(
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if config.GetConfig().DebugOptions.QuestTools {
s.Logger.Debug(
p.Opcode().String(),
zap.Uint8("Unk0", pkt.Unk0),
zap.Uint8("Op", pkt.Op),
@@ -360,7 +373,7 @@ func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
if pkt.Op == 2 {
var page, requirement, donated int
s.server.db.QueryRow(`SELECT tower_mission_page, tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&page, &donated)
database.QueryRow(`SELECT tower_mission_page, tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&page, &donated)
for i := 0; i < (page*3)+1; i++ {
requirement += int(tenrouiraiData[i].Cost)
@@ -368,24 +381,24 @@ func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
sd, err := GetCharacterSaveData(s, s.charID)
sd, err := GetCharacterSaveData(s, s.CharID)
if err == nil && sd != nil {
sd.RP -= pkt.DonatedRP
sd.Save(s)
if donated+int(pkt.DonatedRP) >= requirement {
s.server.db.Exec(`UPDATE guilds SET tower_mission_page=tower_mission_page+1 WHERE id=$1`, pkt.GuildID)
s.server.db.Exec(`UPDATE guild_characters SET tower_mission_1=NULL, tower_mission_2=NULL, tower_mission_3=NULL WHERE guild_id=$1`, pkt.GuildID)
database.Exec(`UPDATE guilds SET tower_mission_page=tower_mission_page+1 WHERE id=$1`, pkt.GuildID)
database.Exec(`UPDATE guild_characters SET tower_mission_1=NULL, tower_mission_2=NULL, tower_mission_3=NULL WHERE guild_id=$1`, pkt.GuildID)
pkt.DonatedRP = uint16(requirement - donated)
}
bf.WriteUint32(uint32(pkt.DonatedRP))
s.server.db.Exec(`UPDATE guilds SET tower_rp=tower_rp+$1 WHERE id=$2`, pkt.DonatedRP, pkt.GuildID)
database.Exec(`UPDATE guilds SET tower_rp=tower_rp+$1 WHERE id=$2`, pkt.DonatedRP, pkt.GuildID)
} else {
bf.WriteUint32(0)
}
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
DoAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
}
@@ -405,7 +418,7 @@ func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
type GemInfo struct {
@@ -425,9 +438,12 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
var data []*byteframe.ByteFrame
gemInfo := []GemInfo{}
gemHistory := []GemHistory{}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var tempGems string
s.server.db.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.charID).Scan(&tempGems)
database.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.CharID).Scan(&tempGems)
for i, v := range stringsupport.CSVElems(tempGems) {
gemInfo = append(gemInfo, GemInfo{uint16((i / 5 << 8) + (i%5 + 1)), uint16(v)})
}
@@ -450,14 +466,14 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
DoAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostGemInfo)
if s.server.erupeConfig.DebugOptions.QuestTools {
s.logger.Debug(
if config.GetConfig().DebugOptions.QuestTools {
s.Logger.Debug(
p.Opcode().String(),
zap.Uint32("Op", pkt.Op),
zap.Uint32("Unk1", pkt.Unk1),
@@ -468,25 +484,28 @@ func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
zap.Int32("Unk6", pkt.Unk6),
)
}
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var gems string
s.server.db.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.charID).Scan(&gems)
database.QueryRow(`SELECT COALESCE(gems, $1) FROM tower WHERE char_id=$2`, EmptyTowerCSV(30), s.CharID).Scan(&gems)
switch pkt.Op {
case 1: // Add gem
i := int((pkt.Gem >> 8 * 5) + (pkt.Gem - pkt.Gem&0xFF00 - 1%5))
s.server.db.Exec(`UPDATE tower SET gems=$1 WHERE char_id=$2`, stringsupport.CSVSetIndex(gems, i, stringsupport.CSVGetIndex(gems, i)+int(pkt.Quantity)), s.charID)
database.Exec(`UPDATE tower SET gems=$1 WHERE char_id=$2`, stringsupport.CSVSetIndex(gems, i, stringsupport.CSVGetIndex(gems, i)+int(pkt.Quantity)), s.CharID)
case 2: // Transfer gem
// no way im doing this for now
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetNotice)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostNotice)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
DoAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/db"
)
func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {}
@@ -12,44 +13,50 @@ func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetUserBinary)
s.server.userBinaryPartsLock.Lock()
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
s.server.userBinaryPartsLock.Unlock()
var exists []byte
err := s.server.db.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.charID).Scan(&exists)
s.Server.userBinaryPartsLock.Lock()
s.Server.userBinaryParts[userBinaryPartID{charID: s.CharID, index: pkt.BinaryType}] = pkt.RawDataPayload
s.Server.userBinaryPartsLock.Unlock()
database, err := db.GetDB()
if err != nil {
s.server.db.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.charID)
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var exists []byte
err = database.QueryRow("SELECT type2 FROM user_binary WHERE id=$1", s.CharID).Scan(&exists)
if err != nil {
database.Exec("INSERT INTO user_binary (id) VALUES ($1)", s.CharID)
}
s.server.db.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.charID)
database.Exec(fmt.Sprintf("UPDATE user_binary SET type%d=$1 WHERE id=$2", pkt.BinaryType), pkt.RawDataPayload, s.CharID)
msg := &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
CharID: s.CharID,
BinaryType: pkt.BinaryType,
}
s.server.BroadcastMHF(msg, s)
s.Server.BroadcastMHF(msg, s)
}
func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysGetUserBinary)
// Try to get the data.
s.server.userBinaryPartsLock.RLock()
defer s.server.userBinaryPartsLock.RUnlock()
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
s.Server.userBinaryPartsLock.RLock()
defer s.Server.userBinaryPartsLock.RUnlock()
data, ok := s.Server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
// If we can't get the real data, try to get it from the database.
if !ok {
err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
err = database.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binary WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
if err != nil {
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
DoAckBufFail(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
} else {
doAckBufSucceed(s, pkt.AckHandle, data)
DoAckBufSucceed(s, pkt.AckHandle, data)
}
}

View File

@@ -10,7 +10,7 @@ import (
)
// BroadcastMHF queues a MHFPacket to be sent to all sessions.
func (server *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
func (server *ChannelServer) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
server.Lock()
defer server.Unlock()
@@ -22,7 +22,7 @@ func (server *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Sess
}
}
func (server *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *Server) {
func (server *ChannelServer) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *ChannelServer) {
for _, c := range server.Channels {
if c == ignoredChannel {
continue
@@ -32,7 +32,7 @@ func (server *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Sess
}
// BroadcastChatMessage broadcasts a simple chat message to all the sessions.
func (server *Server) BroadcastChatMessage(message string) {
func (server *ChannelServer) BroadcastChatMessage(message string) {
bf := byteframe.NewByteFrame()
bf.SetLE()
msgBinChat := &binpacket.MsgBinChat{
@@ -50,7 +50,7 @@ func (server *Server) BroadcastChatMessage(message string) {
}, nil)
}
func (server *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type uint8) {
func (server *ChannelServer) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type uint8) {
bf := byteframe.NewByteFrame()
bf.SetLE()
bf.WriteUint16(0) // Unk

View File

@@ -6,23 +6,21 @@ import (
"sync"
"time"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/server/discordbot"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/logger"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
// Config struct allows configuring the server.
type Config struct {
ID uint16
DB *sqlx.DB
DiscordBot *discordbot.DiscordBot
ErupeConfig *_config.Config
Name string
Enable bool
ID uint16
DiscordBot *discordbot.DiscordBot
Name string
Enable bool
}
// Map key type for a user binary part.
@@ -31,17 +29,16 @@ type userBinaryPartID struct {
index uint8
}
// Server is a MHF channel server.
type Server struct {
// ChannelServer is a MHF channel server.
type ChannelServer struct {
sync.Mutex
Channels []*Server
Channels []*ChannelServer
ID uint16
GlobalID string
IP string
Port uint16
logger logger.Logger
db *sqlx.DB
erupeConfig *_config.Config
erupeConfig *config.Config
acceptConns chan net.Conn
deleteConns chan net.Conn
sessions map[net.Conn]*Session
@@ -76,7 +73,7 @@ type Server struct {
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
func NewServer(config *Config) *ChannelServer {
stageNames := []string{
"sl1Ns200p0a0u0", // Mezeporta
"sl1Ns211p0a0u0", // Rasta bar
@@ -90,11 +87,9 @@ func NewServer(config *Config) *Server {
for _, name := range stageNames {
stages[name] = NewStage(name)
}
server := &Server{
server := &ChannelServer{
ID: config.ID,
logger: logger.Get().Named("channel-" + fmt.Sprint(config.ID)),
db: config.DB,
erupeConfig: config.ErupeConfig,
acceptConns: make(chan net.Conn),
deleteConns: make(chan net.Conn),
sessions: make(map[net.Conn]*Session),
@@ -121,7 +116,7 @@ func NewServer(config *Config) *Server {
}
// Start starts the server in a new goroutine.
func (server *Server) Start() error {
func (server *ChannelServer) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", server.Port))
if err != nil {
return err
@@ -130,9 +125,8 @@ func (server *Server) Start() error {
go server.acceptClients()
go server.manageSessions()
// Start the discord bot for chat integration.
if server.erupeConfig.Discord.Enabled && server.discordBot != nil {
if config.GetConfig().Discord.Enabled && server.discordBot != nil {
server.discordBot.Session.AddHandler(server.onDiscordMessage)
server.discordBot.Session.AddHandler(server.onInteraction)
}
@@ -141,7 +135,7 @@ func (server *Server) Start() error {
}
// Shutdown tries to shut down the server gracefully.
func (server *Server) Shutdown() {
func (server *ChannelServer) Shutdown() {
server.Lock()
server.isShuttingDown = true
server.Unlock()
@@ -151,7 +145,7 @@ func (server *Server) Shutdown() {
close(server.acceptConns)
}
func (server *Server) acceptClients() {
func (server *ChannelServer) acceptClients() {
for {
conn, err := server.listener.Accept()
if err != nil {
@@ -170,7 +164,7 @@ func (server *Server) acceptClients() {
}
}
func (server *Server) manageSessions() {
func (server *ChannelServer) manageSessions() {
for {
select {
case newConn := <-server.acceptConns:
@@ -201,10 +195,10 @@ func (server *Server) manageSessions() {
}
}
func (server *Server) FindSessionByCharID(charID uint32) *Session {
func (server *ChannelServer) FindSessionByCharID(charID uint32) *Session {
for _, c := range server.Channels {
for _, session := range c.sessions {
if session.charID == charID {
if session.CharID == charID {
return session
}
}
@@ -212,10 +206,14 @@ func (server *Server) FindSessionByCharID(charID uint32) *Session {
return nil
}
func (server *Server) DisconnectUser(uid uint32) {
func (server *ChannelServer) DisconnectUser(uid uint32) {
var cid uint32
var cids []uint32
rows, _ := server.db.Query(`SELECT id FROM characters WHERE user_id=$1`, uid)
database, err := db.GetDB()
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
rows, _ := database.Query(`SELECT id FROM characters WHERE user_id=$1`, uid)
for rows.Next() {
rows.Scan(&cid)
cids = append(cids, cid)
@@ -223,7 +221,7 @@ func (server *Server) DisconnectUser(uid uint32) {
for _, c := range server.Channels {
for _, session := range c.sessions {
for _, cid := range cids {
if session.charID == cid {
if session.CharID == cid {
session.rawConn.Close()
break
}
@@ -232,7 +230,7 @@ func (server *Server) DisconnectUser(uid uint32) {
}
}
func (server *Server) FindObjectByChar(charID uint32) *Object {
func (server *ChannelServer) FindObjectByChar(charID uint32) *Object {
server.stagesLock.RLock()
defer server.stagesLock.RUnlock()
for _, stage := range server.stages {
@@ -250,7 +248,7 @@ func (server *Server) FindObjectByChar(charID uint32) *Object {
return nil
}
func (server *Server) HasSemaphore(ses *Session) bool {
func (server *ChannelServer) HasSemaphore(ses *Session) bool {
for _, semaphore := range server.semaphore {
if semaphore.host == ses {
return true
@@ -259,7 +257,7 @@ func (server *Server) HasSemaphore(ses *Session) bool {
return false
}
func (server *Server) Season() uint8 {
func (server *ChannelServer) Season() uint8 {
sid := int64(((server.ID & 0xFF00) - 4096) / 256)
return uint8(((gametime.TimeAdjusted().Unix() / 86400) + sid) % 3)
}

View File

@@ -1,6 +1,7 @@
package channelserver
import (
"erupe-ce/utils/db"
"fmt"
"sort"
"strings"
@@ -15,7 +16,7 @@ type Player struct {
QuestID int
}
func getPlayerSlice(server *Server) []Player {
func getPlayerSlice(server *ChannelServer) []Player {
var p []Player
var questIndex int
@@ -40,7 +41,7 @@ func getPlayerSlice(server *Server) []Player {
return p
}
func getCharacterList(server *Server) string {
func getCharacterList(server *ChannelServer) string {
questEmojis := []string{
":person_in_lotus_position:",
":white_circle:",
@@ -69,11 +70,15 @@ func getCharacterList(server *Server) string {
}
// onInteraction handles slash commands
func (server *Server) onInteraction(ds *discordgo.Session, i *discordgo.InteractionCreate) {
func (server *ChannelServer) onInteraction(ds *discordgo.Session, i *discordgo.InteractionCreate) {
database, err := db.GetDB()
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
switch i.Interaction.ApplicationCommandData().Name {
case "link":
var temp string
err := server.db.QueryRow(`UPDATE users SET discord_id = $1 WHERE discord_token = $2 RETURNING discord_id`, i.Member.User.ID, i.ApplicationCommandData().Options[0].StringValue()).Scan(&temp)
err := database.QueryRow(`UPDATE users SET discord_id = $1 WHERE discord_token = $2 RETURNING discord_id`, i.Member.User.ID, i.ApplicationCommandData().Options[0].StringValue()).Scan(&temp)
if err == nil {
ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
@@ -93,7 +98,7 @@ func (server *Server) onInteraction(ds *discordgo.Session, i *discordgo.Interact
}
case "password":
password, _ := bcrypt.GenerateFromPassword([]byte(i.ApplicationCommandData().Options[0].StringValue()), 10)
_, err := server.db.Exec(`UPDATE users SET password = $1 WHERE discord_id = $2`, password, i.Member.User.ID)
_, err := database.Exec(`UPDATE users SET password = $1 WHERE discord_id = $2`, password, i.Member.User.ID)
if err == nil {
ds.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
@@ -115,7 +120,7 @@ func (server *Server) onInteraction(ds *discordgo.Session, i *discordgo.Interact
}
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
func (server *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
func (server *ChannelServer) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore messages from bots, or messages that are not in the correct channel.
if m.Author.Bot || m.ChannelID != server.erupeConfig.Discord.RelayChannel.RelayChannelID {
return
@@ -148,14 +153,14 @@ func (server *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.Messa
server.BroadcastChatMessage(messages[i])
}
}
func (server *Server) DiscordChannelSend(charName string, content string) {
func (server *ChannelServer) DiscordChannelSend(charName string, content string) {
if server.erupeConfig.Discord.Enabled && server.discordBot != nil {
message := fmt.Sprintf("**%s**: %s", charName, content)
server.discordBot.RealtimeChannelSend(message)
}
}
func (server *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) {
func (server *ChannelServer) DiscordScreenShotSend(charName string, title string, description string, articleToken string) {
if server.erupeConfig.Discord.Enabled && server.discordBot != nil {
imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", server.erupeConfig.Screenshots.Host, server.erupeConfig.Screenshots.Port, articleToken)
message := fmt.Sprintf("**%s**: %s - %s %s", charName, title, description, imageUrl)

View File

@@ -155,14 +155,14 @@ func init() {
handlerTable[network.MSG_MHF_CARAVAN_MY_SCORE] = handleMsgMhfCaravanMyScore
handlerTable[network.MSG_MHF_CARAVAN_RANKING] = handleMsgMhfCaravanRanking
handlerTable[network.MSG_MHF_CARAVAN_MY_RANK] = handleMsgMhfCaravanMyRank
handlerTable[network.MSG_MHF_CREATE_GUILD] = handleMsgMhfCreateGuild
handlerTable[network.MSG_MHF_OPERATE_GUILD] = handleMsgMhfOperateGuild
handlerTable[network.MSG_MHF_OPERATE_GUILD_MEMBER] = handleMsgMhfOperateGuildMember
handlerTable[network.MSG_MHF_INFO_GUILD] = handleMsgMhfInfoGuild
handlerTable[network.MSG_MHF_ENUMERATE_GUILD] = handleMsgMhfEnumerateGuild
handlerTable[network.MSG_MHF_UPDATE_GUILD] = handleMsgMhfUpdateGuild
handlerTable[network.MSG_MHF_ARRANGE_GUILD_MEMBER] = handleMsgMhfArrangeGuildMember
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MEMBER] = handleMsgMhfEnumerateGuildMember
handlerTable[network.MSG_MHF_CREATE_GUILD] = HandleMsgMhfCreateGuild
handlerTable[network.MSG_MHF_OPERATE_GUILD] = HandleMsgMhfOperateGuild
handlerTable[network.MSG_MHF_OPERATE_GUILD_MEMBER] = HandleMsgMhfOperateGuildMember
handlerTable[network.MSG_MHF_INFO_GUILD] = HandleMsgMhfInfoGuild
handlerTable[network.MSG_MHF_ENUMERATE_GUILD] = HandleMsgMhfEnumerateGuild
handlerTable[network.MSG_MHF_UPDATE_GUILD] = HandleMsgMhfUpdateGuild
handlerTable[network.MSG_MHF_ARRANGE_GUILD_MEMBER] = HandleMsgMhfArrangeGuildMember
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MEMBER] = HandleMsgMhfEnumerateGuildMember
handlerTable[network.MSG_MHF_ENUMERATE_CAMPAIGN] = handleMsgMhfEnumerateCampaign
handlerTable[network.MSG_MHF_STATE_CAMPAIGN] = handleMsgMhfStateCampaign
handlerTable[network.MSG_MHF_APPLY_CAMPAIGN] = handleMsgMhfApplyCampaign
@@ -170,7 +170,7 @@ func init() {
handlerTable[network.MSG_MHF_ACQUIRE_ITEM] = handleMsgMhfAcquireItem
handlerTable[network.MSG_MHF_TRANSFER_ITEM] = handleMsgMhfTransferItem
handlerTable[network.MSG_MHF_MERCENARY_HUNTDATA] = handleMsgMhfMercenaryHuntdata
handlerTable[network.MSG_MHF_ENTRY_ROOKIE_GUILD] = handleMsgMhfEntryRookieGuild
handlerTable[network.MSG_MHF_ENTRY_ROOKIE_GUILD] = HandleMsgMhfEntryRookieGuild
handlerTable[network.MSG_MHF_ENUMERATE_QUEST] = handleMsgMhfEnumerateQuest
handlerTable[network.MSG_MHF_ENUMERATE_EVENT] = handleMsgMhfEnumerateEvent
handlerTable[network.MSG_MHF_ENUMERATE_PRICE] = handleMsgMhfEnumeratePrice
@@ -187,14 +187,14 @@ func init() {
handlerTable[network.MSG_MHF_UPDATE_WAREHOUSE] = handleMsgMhfUpdateWarehouse
handlerTable[network.MSG_MHF_ACQUIRE_TITLE] = handleMsgMhfAcquireTitle
handlerTable[network.MSG_MHF_ENUMERATE_TITLE] = handleMsgMhfEnumerateTitle
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_ITEM] = handleMsgMhfEnumerateGuildItem
handlerTable[network.MSG_MHF_UPDATE_GUILD_ITEM] = handleMsgMhfUpdateGuildItem
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_ITEM] = HandleMsgMhfEnumerateGuildItem
handlerTable[network.MSG_MHF_UPDATE_GUILD_ITEM] = HandleMsgMhfUpdateGuildItem
handlerTable[network.MSG_MHF_ENUMERATE_UNION_ITEM] = handleMsgMhfEnumerateUnionItem
handlerTable[network.MSG_MHF_UPDATE_UNION_ITEM] = handleMsgMhfUpdateUnionItem
handlerTable[network.MSG_MHF_CREATE_JOINT] = handleMsgMhfCreateJoint
handlerTable[network.MSG_MHF_OPERATE_JOINT] = handleMsgMhfOperateJoint
handlerTable[network.MSG_MHF_INFO_JOINT] = handleMsgMhfInfoJoint
handlerTable[network.MSG_MHF_UPDATE_GUILD_ICON] = handleMsgMhfUpdateGuildIcon
handlerTable[network.MSG_MHF_CREATE_JOINT] = HandleMsgMhfCreateJoint
handlerTable[network.MSG_MHF_OPERATE_JOINT] = HandleMsgMhfOperateJoint
handlerTable[network.MSG_MHF_INFO_JOINT] = HandleMsgMhfInfoJoint
handlerTable[network.MSG_MHF_UPDATE_GUILD_ICON] = HandleMsgMhfUpdateGuildIcon
handlerTable[network.MSG_MHF_INFO_FESTA] = handleMsgMhfInfoFesta
handlerTable[network.MSG_MHF_ENTRY_FESTA] = handleMsgMhfEntryFesta
handlerTable[network.MSG_MHF_CHARGE_FESTA] = handleMsgMhfChargeFesta
@@ -207,8 +207,8 @@ func init() {
handlerTable[network.MSG_MHF_UPDATE_CAFEPOINT] = handleMsgMhfUpdateCafepoint
handlerTable[network.MSG_MHF_CHECK_DAILY_CAFEPOINT] = handleMsgMhfCheckDailyCafepoint
handlerTable[network.MSG_MHF_GET_COG_INFO] = handleMsgMhfGetCogInfo
handlerTable[network.MSG_MHF_CHECK_MONTHLY_ITEM] = handleMsgMhfCheckMonthlyItem
handlerTable[network.MSG_MHF_ACQUIRE_MONTHLY_ITEM] = handleMsgMhfAcquireMonthlyItem
handlerTable[network.MSG_MHF_CHECK_MONTHLY_ITEM] = HandleMsgMhfCheckMonthlyItem
handlerTable[network.MSG_MHF_ACQUIRE_MONTHLY_ITEM] = HandleMsgMhfAcquireMonthlyItem
handlerTable[network.MSG_MHF_CHECK_WEEKLY_STAMP] = handleMsgMhfCheckWeeklyStamp
handlerTable[network.MSG_MHF_EXCHANGE_WEEKLY_STAMP] = handleMsgMhfExchangeWeeklyStamp
handlerTable[network.MSG_MHF_CREATE_MERCENARY] = handleMsgMhfCreateMercenary
@@ -238,8 +238,8 @@ func init() {
handlerTable[network.MSG_MHF_GET_MYHOUSE_INFO] = handleMsgMhfGetMyhouseInfo
handlerTable[network.MSG_MHF_UPDATE_MYHOUSE_INFO] = handleMsgMhfUpdateMyhouseInfo
handlerTable[network.MSG_MHF_GET_WEEKLY_SCHEDULE] = handleMsgMhfGetWeeklySchedule
handlerTable[network.MSG_MHF_ENUMERATE_INV_GUILD] = handleMsgMhfEnumerateInvGuild
handlerTable[network.MSG_MHF_OPERATION_INV_GUILD] = handleMsgMhfOperationInvGuild
handlerTable[network.MSG_MHF_ENUMERATE_INV_GUILD] = HandleMsgMhfEnumerateInvGuild
handlerTable[network.MSG_MHF_OPERATION_INV_GUILD] = HandleMsgMhfOperationInvGuild
handlerTable[network.MSG_MHF_STAMPCARD_STAMP] = handleMsgMhfStampcardStamp
handlerTable[network.MSG_MHF_STAMPCARD_PRIZE] = handleMsgMhfStampcardPrize
handlerTable[network.MSG_MHF_UNRESERVE_SRG] = handleMsgMhfUnreserveSrg
@@ -247,8 +247,8 @@ func init() {
handlerTable[network.MSG_MHF_SAVE_PLATE_DATA] = handleMsgMhfSavePlateData
handlerTable[network.MSG_MHF_LOAD_PLATE_BOX] = handleMsgMhfLoadPlateBox
handlerTable[network.MSG_MHF_SAVE_PLATE_BOX] = handleMsgMhfSavePlateBox
handlerTable[network.MSG_MHF_READ_GUILDCARD] = handleMsgMhfReadGuildcard
handlerTable[network.MSG_MHF_UPDATE_GUILDCARD] = handleMsgMhfUpdateGuildcard
handlerTable[network.MSG_MHF_READ_GUILDCARD] = HandleMsgMhfReadGuildcard
handlerTable[network.MSG_MHF_UPDATE_GUILDCARD] = HandleMsgMhfUpdateGuildcard
handlerTable[network.MSG_MHF_READ_BEAT_LEVEL] = handleMsgMhfReadBeatLevel
handlerTable[network.MSG_MHF_UPDATE_BEAT_LEVEL] = handleMsgMhfUpdateBeatLevel
handlerTable[network.MSG_MHF_READ_BEAT_LEVEL_ALL_RANKING] = handleMsgMhfReadBeatLevelAllRanking
@@ -264,38 +264,38 @@ func init() {
handlerTable[network.MSG_MHF_GET_EARTH_STATUS] = handleMsgMhfGetEarthStatus
handlerTable[network.MSG_MHF_LOAD_PARTNER] = handleMsgMhfLoadPartner
handlerTable[network.MSG_MHF_SAVE_PARTNER] = handleMsgMhfSavePartner
handlerTable[network.MSG_MHF_GET_GUILD_MISSION_LIST] = handleMsgMhfGetGuildMissionList
handlerTable[network.MSG_MHF_GET_GUILD_MISSION_RECORD] = handleMsgMhfGetGuildMissionRecord
handlerTable[network.MSG_MHF_ADD_GUILD_MISSION_COUNT] = handleMsgMhfAddGuildMissionCount
handlerTable[network.MSG_MHF_SET_GUILD_MISSION_TARGET] = handleMsgMhfSetGuildMissionTarget
handlerTable[network.MSG_MHF_CANCEL_GUILD_MISSION_TARGET] = handleMsgMhfCancelGuildMissionTarget
handlerTable[network.MSG_MHF_GET_GUILD_MISSION_LIST] = HandleMsgMhfGetGuildMissionList
handlerTable[network.MSG_MHF_GET_GUILD_MISSION_RECORD] = HandleMsgMhfGetGuildMissionRecord
handlerTable[network.MSG_MHF_ADD_GUILD_MISSION_COUNT] = HandleMsgMhfAddGuildMissionCount
handlerTable[network.MSG_MHF_SET_GUILD_MISSION_TARGET] = HandleMsgMhfSetGuildMissionTarget
handlerTable[network.MSG_MHF_CANCEL_GUILD_MISSION_TARGET] = HandleMsgMhfCancelGuildMissionTarget
handlerTable[network.MSG_MHF_LOAD_OTOMO_AIROU] = handleMsgMhfLoadOtomoAirou
handlerTable[network.MSG_MHF_SAVE_OTOMO_AIROU] = handleMsgMhfSaveOtomoAirou
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_TRESURE] = handleMsgMhfEnumerateGuildTresure
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_TRESURE] = HandleMsgMhfEnumerateGuildTresure
handlerTable[network.MSG_MHF_ENUMERATE_AIROULIST] = handleMsgMhfEnumerateAiroulist
handlerTable[network.MSG_MHF_REGIST_GUILD_TRESURE] = handleMsgMhfRegistGuildTresure
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_TRESURE] = handleMsgMhfAcquireGuildTresure
handlerTable[network.MSG_MHF_OPERATE_GUILD_TRESURE_REPORT] = handleMsgMhfOperateGuildTresureReport
handlerTable[network.MSG_MHF_GET_GUILD_TRESURE_SOUVENIR] = handleMsgMhfGetGuildTresureSouvenir
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_TRESURE_SOUVENIR] = handleMsgMhfAcquireGuildTresureSouvenir
handlerTable[network.MSG_MHF_REGIST_GUILD_TRESURE] = HandleMsgMhfRegistGuildTresure
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_TRESURE] = HandleMsgMhfAcquireGuildTresure
handlerTable[network.MSG_MHF_OPERATE_GUILD_TRESURE_REPORT] = HandleMsgMhfOperateGuildTresureReport
handlerTable[network.MSG_MHF_GET_GUILD_TRESURE_SOUVENIR] = HandleMsgMhfGetGuildTresureSouvenir
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_TRESURE_SOUVENIR] = HandleMsgMhfAcquireGuildTresureSouvenir
handlerTable[network.MSG_MHF_ENUMERATE_FESTA_INTERMEDIATE_PRIZE] = handleMsgMhfEnumerateFestaIntermediatePrize
handlerTable[network.MSG_MHF_ACQUIRE_FESTA_INTERMEDIATE_PRIZE] = handleMsgMhfAcquireFestaIntermediatePrize
handlerTable[network.MSG_MHF_LOAD_DECO_MYSET] = handleMsgMhfLoadDecoMyset
handlerTable[network.MSG_MHF_SAVE_DECO_MYSET] = handleMsgMhfSaveDecoMyset
handlerTable[network.MSG_MHF_reserve10F] = handleMsgMhfReserve10F
handlerTable[network.MSG_MHF_LOAD_GUILD_COOKING] = handleMsgMhfLoadGuildCooking
handlerTable[network.MSG_MHF_REGIST_GUILD_COOKING] = handleMsgMhfRegistGuildCooking
handlerTable[network.MSG_MHF_LOAD_GUILD_ADVENTURE] = handleMsgMhfLoadGuildAdventure
handlerTable[network.MSG_MHF_REGIST_GUILD_ADVENTURE] = handleMsgMhfRegistGuildAdventure
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_ADVENTURE] = handleMsgMhfAcquireGuildAdventure
handlerTable[network.MSG_MHF_CHARGE_GUILD_ADVENTURE] = handleMsgMhfChargeGuildAdventure
handlerTable[network.MSG_MHF_LOAD_GUILD_COOKING] = HandleMsgMhfLoadGuildCooking
handlerTable[network.MSG_MHF_REGIST_GUILD_COOKING] = HandleMsgMhfRegistGuildCooking
handlerTable[network.MSG_MHF_LOAD_GUILD_ADVENTURE] = HandleMsgMhfLoadGuildAdventure
handlerTable[network.MSG_MHF_REGIST_GUILD_ADVENTURE] = HandleMsgMhfRegistGuildAdventure
handlerTable[network.MSG_MHF_ACQUIRE_GUILD_ADVENTURE] = HandleMsgMhfAcquireGuildAdventure
handlerTable[network.MSG_MHF_CHARGE_GUILD_ADVENTURE] = HandleMsgMhfChargeGuildAdventure
handlerTable[network.MSG_MHF_LOAD_LEGEND_DISPATCH] = handleMsgMhfLoadLegendDispatch
handlerTable[network.MSG_MHF_LOAD_HUNTER_NAVI] = handleMsgMhfLoadHunterNavi
handlerTable[network.MSG_MHF_SAVE_HUNTER_NAVI] = handleMsgMhfSaveHunterNavi
handlerTable[network.MSG_MHF_REGIST_SPABI_TIME] = handleMsgMhfRegistSpabiTime
handlerTable[network.MSG_MHF_GET_GUILD_WEEKLY_BONUS_MASTER] = handleMsgMhfGetGuildWeeklyBonusMaster
handlerTable[network.MSG_MHF_GET_GUILD_WEEKLY_BONUS_ACTIVE_COUNT] = handleMsgMhfGetGuildWeeklyBonusActiveCount
handlerTable[network.MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER] = handleMsgMhfAddGuildWeeklyBonusExceptionalUser
handlerTable[network.MSG_MHF_GET_GUILD_WEEKLY_BONUS_MASTER] = HandleMsgMhfGetGuildWeeklyBonusMaster
handlerTable[network.MSG_MHF_GET_GUILD_WEEKLY_BONUS_ACTIVE_COUNT] = HandleMsgMhfGetGuildWeeklyBonusActiveCount
handlerTable[network.MSG_MHF_ADD_GUILD_WEEKLY_BONUS_EXCEPTIONAL_USER] = HandleMsgMhfAddGuildWeeklyBonusExceptionalUser
handlerTable[network.MSG_MHF_GET_TOWER_INFO] = handleMsgMhfGetTowerInfo
handlerTable[network.MSG_MHF_POST_TOWER_INFO] = handleMsgMhfPostTowerInfo
handlerTable[network.MSG_MHF_GET_GEM_INFO] = handleMsgMhfGetGemInfo
@@ -328,7 +328,7 @@ func init() {
handlerTable[network.MSG_MHF_GET_TINY_BIN] = handleMsgMhfGetTinyBin
handlerTable[network.MSG_MHF_POST_TINY_BIN] = handleMsgMhfPostTinyBin
handlerTable[network.MSG_MHF_GET_SENYU_DAILY_COUNT] = handleMsgMhfGetSenyuDailyCount
handlerTable[network.MSG_MHF_GET_GUILD_TARGET_MEMBER_NUM] = handleMsgMhfGetGuildTargetMemberNum
handlerTable[network.MSG_MHF_GET_GUILD_TARGET_MEMBER_NUM] = HandleMsgMhfGetGuildTargetMemberNum
handlerTable[network.MSG_MHF_GET_BOOST_RIGHT] = handleMsgMhfGetBoostRight
handlerTable[network.MSG_MHF_START_BOOST_TIME] = handleMsgMhfStartBoostTime
handlerTable[network.MSG_MHF_POST_BOOST_TIME_QUEST_RETURN] = handleMsgMhfPostBoostTimeQuestReturn
@@ -341,19 +341,19 @@ func init() {
handlerTable[network.MSG_MHF_POST_RYOUDAMA] = handleMsgMhfPostRyoudama
handlerTable[network.MSG_MHF_GET_TENROUIRAI] = handleMsgMhfGetTenrouirai
handlerTable[network.MSG_MHF_POST_TENROUIRAI] = handleMsgMhfPostTenrouirai
handlerTable[network.MSG_MHF_POST_GUILD_SCOUT] = handleMsgMhfPostGuildScout
handlerTable[network.MSG_MHF_CANCEL_GUILD_SCOUT] = handleMsgMhfCancelGuildScout
handlerTable[network.MSG_MHF_ANSWER_GUILD_SCOUT] = handleMsgMhfAnswerGuildScout
handlerTable[network.MSG_MHF_GET_GUILD_SCOUT_LIST] = handleMsgMhfGetGuildScoutList
handlerTable[network.MSG_MHF_GET_GUILD_MANAGE_RIGHT] = handleMsgMhfGetGuildManageRight
handlerTable[network.MSG_MHF_SET_GUILD_MANAGE_RIGHT] = handleMsgMhfSetGuildManageRight
handlerTable[network.MSG_MHF_POST_GUILD_SCOUT] = HandleMsgMhfPostGuildScout
handlerTable[network.MSG_MHF_CANCEL_GUILD_SCOUT] = HandleMsgMhfCancelGuildScout
handlerTable[network.MSG_MHF_ANSWER_GUILD_SCOUT] = HandleMsgMhfAnswerGuildScout
handlerTable[network.MSG_MHF_GET_GUILD_SCOUT_LIST] = HandleMsgMhfGetGuildScoutList
handlerTable[network.MSG_MHF_GET_GUILD_MANAGE_RIGHT] = HandleMsgMhfGetGuildManageRight
handlerTable[network.MSG_MHF_SET_GUILD_MANAGE_RIGHT] = HandleMsgMhfSetGuildManageRight
handlerTable[network.MSG_MHF_PLAY_NORMAL_GACHA] = handleMsgMhfPlayNormalGacha
handlerTable[network.MSG_MHF_GET_DAILY_MISSION_MASTER] = handleMsgMhfGetDailyMissionMaster
handlerTable[network.MSG_MHF_GET_DAILY_MISSION_PERSONAL] = handleMsgMhfGetDailyMissionPersonal
handlerTable[network.MSG_MHF_SET_DAILY_MISSION_PERSONAL] = handleMsgMhfSetDailyMissionPersonal
handlerTable[network.MSG_MHF_GET_GACHA_PLAY_HISTORY] = handleMsgMhfGetGachaPlayHistory
handlerTable[network.MSG_MHF_GET_REJECT_GUILD_SCOUT] = handleMsgMhfGetRejectGuildScout
handlerTable[network.MSG_MHF_SET_REJECT_GUILD_SCOUT] = handleMsgMhfSetRejectGuildScout
handlerTable[network.MSG_MHF_GET_REJECT_GUILD_SCOUT] = HandleMsgMhfGetRejectGuildScout
handlerTable[network.MSG_MHF_SET_REJECT_GUILD_SCOUT] = HandleMsgMhfSetRejectGuildScout
handlerTable[network.MSG_MHF_GET_CA_ACHIEVEMENT_HIST] = handleMsgMhfGetCaAchievementHist
handlerTable[network.MSG_MHF_SET_CA_ACHIEVEMENT_HIST] = handleMsgMhfSetCaAchievementHist
handlerTable[network.MSG_MHF_GET_KEEP_LOGIN_BOOST_STATUS] = handleMsgMhfGetKeepLoginBoostStatus
@@ -378,8 +378,8 @@ func init() {
handlerTable[network.MSG_MHF_GET_UD_RANKING] = handleMsgMhfGetUdRanking
handlerTable[network.MSG_MHF_GET_UD_MY_RANKING] = handleMsgMhfGetUdMyRanking
handlerTable[network.MSG_MHF_ACQUIRE_MONTHLY_REWARD] = handleMsgMhfAcquireMonthlyReward
handlerTable[network.MSG_MHF_GET_UD_GUILD_MAP_INFO] = handleMsgMhfGetUdGuildMapInfo
handlerTable[network.MSG_MHF_GENERATE_UD_GUILD_MAP] = handleMsgMhfGenerateUdGuildMap
handlerTable[network.MSG_MHF_GET_UD_GUILD_MAP_INFO] = HandleMsgMhfGetUdGuildMapInfo
handlerTable[network.MSG_MHF_GENERATE_UD_GUILD_MAP] = HandleMsgMhfGenerateUdGuildMap
handlerTable[network.MSG_MHF_GET_UD_TACTICS_POINT] = handleMsgMhfGetUdTacticsPoint
handlerTable[network.MSG_MHF_ADD_UD_TACTICS_POINT] = handleMsgMhfAddUdTacticsPoint
handlerTable[network.MSG_MHF_GET_UD_TACTICS_RANKING] = handleMsgMhfGetUdTacticsRanking
@@ -396,7 +396,7 @@ func init() {
handlerTable[network.MSG_MHF_SEX_CHANGER] = handleMsgMhfSexChanger
handlerTable[network.MSG_MHF_GET_LOBBY_CROWD] = handleMsgMhfGetLobbyCrowd
handlerTable[network.MSG_SYS_reserve180] = handleMsgSysReserve180
handlerTable[network.MSG_MHF_GUILD_HUNTDATA] = handleMsgMhfGuildHuntdata
handlerTable[network.MSG_MHF_GUILD_HUNTDATA] = HandleMsgMhfGuildHuntdata
handlerTable[network.MSG_MHF_ADD_KOURYOU_POINT] = handleMsgMhfAddKouryouPoint
handlerTable[network.MSG_MHF_GET_KOURYOU_POINT] = handleMsgMhfGetKouryouPoint
handlerTable[network.MSG_MHF_EXCHANGE_KOURYOU_POINT] = handleMsgMhfExchangeKouryouPoint
@@ -427,12 +427,12 @@ func init() {
handlerTable[network.MSG_MHF_LOAD_MEZFES_DATA] = handleMsgMhfLoadMezfesData
handlerTable[network.MSG_SYS_reserve19E] = handleMsgSysReserve19E
handlerTable[network.MSG_SYS_reserve19F] = handleMsgSysReserve19F
handlerTable[network.MSG_MHF_UPDATE_FORCE_GUILD_RANK] = handleMsgMhfUpdateForceGuildRank
handlerTable[network.MSG_MHF_UPDATE_FORCE_GUILD_RANK] = HandleMsgMhfUpdateForceGuildRank
handlerTable[network.MSG_MHF_RESET_TITLE] = handleMsgMhfResetTitle
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD] = handleMsgMhfEnumerateGuildMessageBoard
handlerTable[network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD] = handleMsgMhfUpdateGuildMessageBoard
handlerTable[network.MSG_MHF_ENUMERATE_GUILD_MESSAGE_BOARD] = HandleMsgMhfEnumerateGuildMessageBoard
handlerTable[network.MSG_MHF_UPDATE_GUILD_MESSAGE_BOARD] = HandleMsgMhfUpdateGuildMessageBoard
handlerTable[network.MSG_SYS_reserve1A4] = handleMsgSysReserve1A4
handlerTable[network.MSG_MHF_REGIST_GUILD_ADVENTURE_DIVA] = handleMsgMhfRegistGuildAdventureDiva
handlerTable[network.MSG_MHF_REGIST_GUILD_ADVENTURE_DIVA] = HandleMsgMhfRegistGuildAdventureDiva
handlerTable[network.MSG_SYS_reserve1A6] = handleMsgSysReserve1A6
handlerTable[network.MSG_SYS_reserve1A7] = handleMsgSysReserve1A7
handlerTable[network.MSG_SYS_reserve1A8] = handleMsgSysReserve1A8

View File

@@ -1,5 +1,9 @@
package channelserver
import (
"erupe-ce/config"
)
type i18n struct {
language string
cafe struct {
@@ -101,9 +105,9 @@ type i18n struct {
}
}
func getLangStrings(server *Server) i18n {
func getLangStrings(server *ChannelServer) i18n {
var i i18n
switch server.erupeConfig.Language {
switch config.GetConfig().Language {
case "jp":
i.language = "日本語"
i.cafe.reset = "%d/%dにリセット"

View File

@@ -13,7 +13,7 @@ type Raviente struct {
support []uint32
}
func (server *Server) resetRaviente() {
func (server *ChannelServer) resetRaviente() {
for _, semaphore := range server.semaphore {
if strings.HasPrefix(semaphore.name, "hs_l0") {
return
@@ -26,7 +26,7 @@ func (server *Server) resetRaviente() {
server.raviente.support = make([]uint32, 30)
}
func (server *Server) GetRaviMultiplier() float64 {
func (server *ChannelServer) GetRaviMultiplier() float64 {
raviSema := server.getRaviSemaphore()
if raviSema != nil {
var minPlayers int
@@ -43,7 +43,7 @@ func (server *Server) GetRaviMultiplier() float64 {
return 0
}
func (server *Server) UpdateRavi(semaID uint32, index uint8, value uint32, update bool) (uint32, uint32) {
func (server *ChannelServer) UpdateRavi(semaID uint32, index uint8, value uint32, update bool) (uint32, uint32) {
var prev uint32
var dest *[]uint32
switch semaID {

View File

@@ -3,29 +3,29 @@ package channelserver
import (
"encoding/binary"
"encoding/hex"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/logger"
"erupe-ce/utils/mhfcourse"
"erupe-ce/utils/stringstack"
"fmt"
"io"
"net"
"sync"
"time"
"erupe-ce/network"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/logger"
"erupe-ce/utils/stringstack"
"go.uber.org/zap"
)
// Session holds state for the channel server connection.
type Session struct {
sync.Mutex
logger logger.Logger
server *Server
Logger logger.Logger
Server *ChannelServer
rawConn net.Conn
cryptConn *network.CryptConn
sendPackets chan mhfpacket.MHFPacket
@@ -37,7 +37,7 @@ type Session struct {
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
stagePass string // Temporary storage
prevGuildID uint32 // Stores the last GuildID used in InfoGuild
charID uint32
CharID uint32
logKey []byte
sessionStart int64
courses []mhfcourse.Course
@@ -66,10 +66,10 @@ type Session struct {
}
// NewSession creates a new Session type.
func NewSession(server *Server, conn net.Conn) *Session {
func NewSession(server *ChannelServer, conn net.Conn) *Session {
s := &Session{
logger: server.logger.Named(conn.RemoteAddr().String()),
server: server,
Logger: server.logger.Named(conn.RemoteAddr().String()),
Server: server,
rawConn: conn,
cryptConn: network.NewCryptConn(conn),
sendPackets: make(chan mhfpacket.MHFPacket, 20),
@@ -85,7 +85,7 @@ func NewSession(server *Server, conn net.Conn) *Session {
// Start starts the session packet send and recv loop(s).
func (s *Session) Start() {
s.logger.Debug("New 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()
@@ -97,7 +97,7 @@ func (s *Session) QueueSendMHF(pkt mhfpacket.MHFPacket) {
select {
case s.sendPackets <- pkt:
default:
s.logger.Warn("Packet queue too full, dropping!")
s.Logger.Warn("Packet queue too full, dropping!")
}
}
@@ -123,11 +123,11 @@ func (s *Session) sendLoop() {
if len(buffer) > 0 {
err := s.cryptConn.SendPacket(buffer)
if err != nil {
s.logger.Warn("Failed to send packet")
s.Logger.Warn("Failed to send packet")
}
buffer = buffer[:0]
}
time.Sleep(time.Duration(_config.ErupeConfig.LoopDelay) * time.Millisecond)
time.Sleep(time.Duration(config.GetConfig().LoopDelay) * time.Millisecond)
}
}
@@ -143,16 +143,16 @@ func (s *Session) recvLoop() {
}
pkt, err := s.cryptConn.ReadPacket()
if err == io.EOF {
s.logger.Info(fmt.Sprintf("[%s] Disconnected", s.Name))
s.Logger.Info(fmt.Sprintf("[%s] Disconnected", s.Name))
logoutPlayer(s)
return
} else if err != nil {
s.logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err))
s.Logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err))
logoutPlayer(s)
return
}
s.handlePacketGroup(pkt)
time.Sleep(time.Duration(_config.ErupeConfig.LoopDelay) * time.Millisecond)
time.Sleep(time.Duration(config.GetConfig().LoopDelay) * time.Millisecond)
}
}
@@ -220,9 +220,9 @@ func ignored(opcode network.PacketID) bool {
}
func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipient string) {
if sender == "Server" && !s.server.erupeConfig.DebugOptions.LogOutboundMessages {
if sender == "Server" && !config.GetConfig().DebugOptions.LogOutboundMessages {
return
} else if sender != "Server" && !s.server.erupeConfig.DebugOptions.LogInboundMessages {
} else if sender != "Server" && !config.GetConfig().DebugOptions.LogInboundMessages {
return
}
@@ -240,8 +240,8 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
fmt.Printf("[%s] -> [%s]\n", sender, recipient)
}
fmt.Printf("Opcode: (Dec: %d Hex: 0x%04X Name: %s) \n", opcode, opcode, opcodePID)
if s.server.erupeConfig.DebugOptions.LogMessageData {
if len(data) <= s.server.erupeConfig.DebugOptions.MaxHexdumpLength {
if config.GetConfig().DebugOptions.LogMessageData {
if len(data) <= config.GetConfig().DebugOptions.MaxHexdumpLength {
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
} else {
fmt.Printf("Data [%d bytes]: (Too long!)\n\n", len(data))
@@ -254,23 +254,23 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
func (s *Session) SetObjectID() {
for i := uint16(1); i < 127; i++ {
exists := false
for _, j := range s.server.objectIDs {
for _, j := range s.Server.objectIDs {
if i == j {
exists = true
break
}
}
if !exists {
s.server.objectIDs[s] = i
s.Server.objectIDs[s] = i
return
}
}
s.server.objectIDs[s] = 0
s.Server.objectIDs[s] = 0
}
func (s *Session) NextObjectID() uint32 {
bf := byteframe.NewByteFrame()
bf.WriteUint16(s.server.objectIDs[s])
bf.WriteUint16(s.Server.objectIDs[s])
s.objectIndex++
bf.WriteUint16(s.objectIndex)
bf.Seek(0, 0)
@@ -287,7 +287,11 @@ func (s *Session) GetSemaphoreID() uint32 {
func (s *Session) isOp() bool {
var op bool
err := s.server.db.QueryRow(`SELECT op FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&op)
database, err := db.GetDB()
if err != nil {
s.Logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow(`SELECT op FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.CharID).Scan(&op)
if err == nil && op {
return true
}

View File

@@ -1,7 +1,7 @@
package discordbot
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/logger"
"regexp"
@@ -38,18 +38,14 @@ var Commands = []*discordgo.ApplicationCommand{
type DiscordBot struct {
Session *discordgo.Session
config *_config.Config
config *config.Config
logger logger.Logger
MainGuild *discordgo.Guild
RelayChannel *discordgo.Channel
}
type Options struct {
Config *_config.Config
}
func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
func NewDiscordBot() (discordBot *DiscordBot, err error) {
session, err := discordgo.New("Bot " + config.GetConfig().Discord.BotToken)
discordLogger := logger.Get().Named("discord")
if err != nil {
discordLogger.Fatal("Discord failed", zap.Error(err))
@@ -58,8 +54,8 @@ func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
var relayChannel *discordgo.Channel
if options.Config.Discord.RelayChannel.Enabled {
relayChannel, err = session.Channel(options.Config.Discord.RelayChannel.RelayChannelID)
if config.GetConfig().Discord.RelayChannel.Enabled {
relayChannel, err = session.Channel(config.GetConfig().Discord.RelayChannel.RelayChannelID)
}
if err != nil {
@@ -68,7 +64,7 @@ func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
}
discordBot = &DiscordBot{
config: options.Config,
config: config.GetConfig(),
logger: discordLogger,
Session: session,
RelayChannel: relayChannel,

View File

@@ -1,4 +1,4 @@
package entranceserver
package entrance
import (
"encoding/hex"
@@ -8,45 +8,33 @@ import (
"strings"
"sync"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/logger"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
// Server is a MHF entrance server.
type Server struct {
// EntranceServer is a MHF entrance server.
type EntranceServer struct {
sync.Mutex
logger logger.Logger
erupeConfig *_config.Config
db *sqlx.DB
listener net.Listener
isShuttingDown bool
}
// Config struct allows configuring the server.
type Config struct {
Logger logger.Logger
DB *sqlx.DB
ErupeConfig *_config.Config
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
server := &Server{
logger: config.Logger,
erupeConfig: config.ErupeConfig,
db: config.DB,
func NewServer() *EntranceServer {
server := &EntranceServer{
logger: logger.Get().Named("entrance"),
}
return server
}
// Start starts the server in a new goroutine.
func (server *Server) Start() error {
func (server *EntranceServer) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", server.erupeConfig.Entrance.Port))
l, err := net.Listen("tcp", fmt.Sprintf(":%d", config.GetConfig().Entrance.Port))
if err != nil {
return err
}
@@ -59,7 +47,7 @@ func (server *Server) Start() error {
}
// Shutdown exits the server gracefully.
func (server *Server) Shutdown() {
func (server *EntranceServer) Shutdown() {
server.logger.Debug("Shutting down...")
server.Lock()
@@ -71,7 +59,7 @@ func (server *Server) Shutdown() {
}
// acceptClients handles accepting new clients in a loop.
func (server *Server) acceptClients() {
func (server *EntranceServer) acceptClients() {
for {
conn, err := server.listener.Accept()
if err != nil {
@@ -92,7 +80,7 @@ func (server *Server) acceptClients() {
}
}
func (server *Server) handleEntranceServerConnection(conn net.Conn) {
func (server *EntranceServer) handleEntranceServerConnection(conn net.Conn) {
defer conn.Close()
// Client initalizes the connection with a one-time buffer of 8 NULL bytes.
nullInit := make([]byte, 8)
@@ -113,7 +101,7 @@ func (server *Server) handleEntranceServerConnection(conn net.Conn) {
return
}
if server.erupeConfig.DebugOptions.LogInboundMessages {
if config.GetConfig().DebugOptions.LogInboundMessages {
fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
@@ -121,7 +109,7 @@ func (server *Server) handleEntranceServerConnection(conn net.Conn) {
if strings.Split(conn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
local = true
}
data := makeSv2Resp(server.erupeConfig, server, local)
data := makeSv2Resp(server, local)
if len(pkt) > 5 {
data = append(data, makeUsrResp(pkt, server)...)
}

View File

@@ -1,39 +1,41 @@
package entranceserver
package entrance
import (
"encoding/binary"
"encoding/hex"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network/crypto/bin8"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/stringsupport"
"fmt"
"net"
)
func encodeServerInfo(config *_config.Config, server *Server, local bool) []byte {
serverInfos := config.Entrance.Entries
func encodeServerInfo(server *EntranceServer, local bool) []byte {
serverInfos := config.GetConfig().Entrance.Entries
bf := byteframe.NewByteFrame()
for serverIdx, si := range serverInfos {
// Prevent MezFes Worlds displaying on Z1
if config.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
if si.Type == 6 {
continue
}
}
if config.ClientID <= _config.G6 {
if config.GetConfig().ClientID <= config.G6 {
if si.Type == 5 {
continue
}
}
if si.IP == "" {
si.IP = config.Host
si.IP = config.GetConfig().Host
}
if local {
bf.WriteUint32(0x0100007F) // 127.0.0.1
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP("127.0.0.1").To4()))
} else {
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
}
@@ -42,36 +44,40 @@ func encodeServerInfo(config *_config.Config, server *Server, local bool) []byte
bf.WriteUint16(uint16(len(si.Channels)))
bf.WriteUint8(si.Type)
bf.WriteUint8(uint8(((gametime.TimeAdjusted().Unix() / 86400) + int64(serverIdx)) % 3))
if server.erupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
bf.WriteUint8(si.Recommended)
}
fullName := append(append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...), stringsupport.UTF8ToSJIS(si.Description)...)
if server.erupeConfig.ClientID >= _config.G1 && server.erupeConfig.ClientID <= _config.G5 {
if config.GetConfig().ClientID >= config.G1 && config.GetConfig().ClientID <= config.G5 {
bf.WriteUint8(uint8(len(fullName)))
bf.WriteBytes(fullName)
} else {
if server.erupeConfig.ClientID >= _config.G51 {
if config.GetConfig().ClientID >= config.G51 {
bf.WriteUint8(0) // Ignored
}
bf.WriteBytes(stringsupport.PaddedString(string(fullName), 65, false))
}
if server.erupeConfig.ClientID >= _config.GG {
if config.GetConfig().ClientID >= config.GG {
bf.WriteUint32(si.AllowedClientFlags)
}
for channelIdx, ci := range si.Channels {
sid := (serverIdx<<8 | 4096) + (channelIdx | 16)
if _config.ErupeConfig.DebugOptions.ProxyPort != 0 {
bf.WriteUint16(_config.ErupeConfig.DebugOptions.ProxyPort)
if config.GetConfig().DebugOptions.ProxyPort != 0 {
bf.WriteUint16(config.GetConfig().DebugOptions.ProxyPort)
} else {
bf.WriteUint16(ci.Port)
}
bf.WriteUint16(uint16(channelIdx | 16))
bf.WriteUint16(ci.MaxPlayers)
var currentPlayers uint16
server.db.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(&currentPlayers)
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
database.QueryRow("SELECT current_players FROM servers WHERE server_id=$1", sid).Scan(&currentPlayers)
bf.WriteUint16(currentPlayers)
bf.WriteUint16(0)
bf.WriteUint16(0)
@@ -86,7 +92,7 @@ func encodeServerInfo(config *_config.Config, server *Server, local bool) []byte
}
}
bf.WriteUint32(uint32(gametime.TimeAdjusted().Unix()))
bf.WriteUint32(uint32(server.erupeConfig.GameplayOptions.ClanMemberLimits[len(server.erupeConfig.GameplayOptions.ClanMemberLimits)-1][1]))
bf.WriteUint32(uint32(config.GetConfig().GameplayOptions.ClanMemberLimits[len(config.GetConfig().GameplayOptions.ClanMemberLimits)-1][1]))
return bf.Data()
}
@@ -108,11 +114,11 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
return bf.Data()
}
func makeSv2Resp(config *_config.Config, server *Server, local bool) []byte {
serverInfos := config.Entrance.Entries
func makeSv2Resp(server *EntranceServer, local bool) []byte {
serverInfos := config.GetConfig().Entrance.Entries
// Decrease by the number of MezFes Worlds
var mf int
if config.ClientID <= _config.Z1 {
if config.GetConfig().ClientID <= config.Z1 {
for _, si := range serverInfos {
if si.Type == 6 {
mf++
@@ -121,21 +127,21 @@ func makeSv2Resp(config *_config.Config, server *Server, local bool) []byte {
}
// and Return Worlds
var ret int
if config.ClientID <= _config.G6 {
if config.GetConfig().ClientID <= config.G6 {
for _, si := range serverInfos {
if si.Type == 5 {
ret++
}
}
}
rawServerData := encodeServerInfo(config, server, local)
rawServerData := encodeServerInfo(server, local)
if server.erupeConfig.DebugOptions.LogOutboundMessages {
if config.GetConfig().DebugOptions.LogOutboundMessages {
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
}
respType := "SV2"
if config.ClientID <= _config.G32 {
if config.GetConfig().ClientID <= config.G32 {
respType = "SVR"
}
@@ -144,16 +150,20 @@ func makeSv2Resp(config *_config.Config, server *Server, local bool) []byte {
return bf.Data()
}
func makeUsrResp(pkt []byte, server *Server) []byte {
func makeUsrResp(pkt []byte, server *EntranceServer) []byte {
bf := byteframe.NewByteFrameFromBytes(pkt)
_ = bf.ReadUint32() // ALL+
_ = bf.ReadUint8() // 0x00
userEntries := bf.ReadUint16()
resp := byteframe.NewByteFrame()
database, err := db.GetDB()
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
for i := 0; i < int(userEntries); i++ {
cid := bf.ReadUint32()
var sid uint16
err := server.db.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid)
err := database.QueryRow("SELECT(SELECT server_id FROM sign_sessions WHERE char_id=$1) AS _", cid).Scan(&sid)
if err != nil {
resp.WriteUint16(0)
} else {
@@ -162,7 +172,7 @@ func makeUsrResp(pkt []byte, server *Server) []byte {
resp.WriteUint16(0)
}
if server.erupeConfig.DebugOptions.LogOutboundMessages {
if config.GetConfig().DebugOptions.LogOutboundMessages {
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data()))
}

318
server/sign/dbutils.go Normal file
View File

@@ -0,0 +1,318 @@
package sign
import (
"database/sql"
"errors"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/mhfcourse"
"erupe-ce/utils/token"
"fmt"
"strings"
"time"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
)
func (server *SignServer) newUserChara(uid uint32) error {
var numNewChars int
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", uid).Scan(&numNewChars)
if err != nil {
return err
}
// prevent users with an uninitialised character from creating more
if numNewChars >= 1 {
return err
}
_, err = database.Exec(`
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hr, gr, weapon_type, last_login)
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
uid,
uint32(time.Now().Unix()),
)
if err != nil {
return err
}
return nil
}
func (server *SignServer) registerDBAccount(username string, password string) (uint32, error) {
var uid uint32
server.logger.Info("Creating user", zap.String("User", username))
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
// Create salted hash of user password
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return 0, err
}
err = database.QueryRow("INSERT INTO users (username, password, return_expires) VALUES ($1, $2, $3) RETURNING id", username, string(passwordHash), time.Now().Add(time.Hour*24*30)).Scan(&uid)
if err != nil {
return 0, err
}
return uid, nil
}
type character struct {
ID uint32 `db:"id"`
IsFemale bool `db:"is_female"`
IsNewCharacter bool `db:"is_new_character"`
Name string `db:"name"`
UnkDescString string `db:"unk_desc_string"`
HR uint16 `db:"hr"`
GR uint16 `db:"gr"`
WeaponType uint16 `db:"weapon_type"`
LastLogin uint32 `db:"last_login"`
}
func (server *SignServer) getCharactersForUser(uid uint32) ([]character, error) {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
characters := make([]character, 0)
err = database.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
if err != nil {
return nil, err
}
return characters, nil
}
func (server *SignServer) getReturnExpiry(uid uint32) time.Time {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var returnExpiry, lastLogin time.Time
database.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
returnExpiry = time.Now().Add(time.Hour * 24 * 30)
database.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
} else {
err := database.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid)
if err != nil {
returnExpiry = time.Now()
database.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
}
}
database.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid)
return returnExpiry
}
func (server *SignServer) getLastCID(uid uint32) uint32 {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var lastPlayed uint32
_ = database.QueryRow("SELECT last_character FROM users WHERE id=$1", uid).Scan(&lastPlayed)
return lastPlayed
}
func (server *SignServer) getUserRights(uid uint32) uint32 {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var rights uint32
if uid != 0 {
_ = database.QueryRow("SELECT rights FROM users WHERE id=$1", uid).Scan(&rights)
_, rights = mhfcourse.GetCourseStruct(rights)
}
return rights
}
type members struct {
CID uint32 // Local character ID
ID uint32 `db:"id"`
Name string `db:"name"`
}
func (server *SignServer) getFriendsForCharacters(chars []character) []members {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
friends := make([]members, 0)
for _, char := range chars {
friendsCSV := ""
err := database.QueryRow("SELECT friends FROM characters WHERE id=$1", char.ID).Scan(&friendsCSV)
friendsSlice := strings.Split(friendsCSV, ",")
friendQuery := "SELECT id, name FROM characters WHERE id="
for i := 0; i < len(friendsSlice); i++ {
friendQuery += friendsSlice[i]
if i+1 != len(friendsSlice) {
friendQuery += " OR id="
}
}
charFriends := make([]members, 0)
err = database.Select(&charFriends, friendQuery)
if err != nil {
continue
}
for i := range charFriends {
charFriends[i].CID = char.ID
}
friends = append(friends, charFriends...)
}
return friends
}
func (server *SignServer) getGuildmatesForCharacters(chars []character) []members {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
guildmates := make([]members, 0)
for _, char := range chars {
var inGuild int
_ = database.QueryRow("SELECT count(*) FROM guild_characters WHERE character_id=$1", char.ID).Scan(&inGuild)
if inGuild > 0 {
var guildID int
err := database.QueryRow("SELECT guild_id FROM guild_characters WHERE character_id=$1", char.ID).Scan(&guildID)
if err != nil {
continue
}
charGuildmates := make([]members, 0)
err = database.Select(&charGuildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, char.ID)
if err != nil {
continue
}
for i := range charGuildmates {
charGuildmates[i].CID = char.ID
}
guildmates = append(guildmates, charGuildmates...)
}
}
return guildmates
}
func (server *SignServer) deleteCharacter(cid int, token string, tokenID uint32) error {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if !server.validateToken(token, tokenID) {
return errors.New("invalid token")
}
var isNew bool
err = database.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", cid).Scan(&isNew)
if isNew {
_, err = database.Exec("DELETE FROM characters WHERE id = $1", cid)
} else {
_, err = database.Exec("UPDATE characters SET deleted = true WHERE id = $1", cid)
}
if err != nil {
return err
}
return nil
}
// Unused
func (server *SignServer) checkToken(uid uint32) (bool, error) {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var exists int
err = database.QueryRow("SELECT count(*) FROM sign_sessions WHERE user_id = $1", uid).Scan(&exists)
if err != nil {
return false, err
}
if exists > 0 {
return true, nil
}
return false, nil
}
func (server *SignServer) registerUidToken(uid uint32) (uint32, string, error) {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_token := token.Generate(16)
var tid uint32
err = database.QueryRow(`INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id`, uid, _token).Scan(&tid)
return tid, _token, err
}
func (server *SignServer) registerPsnToken(psn string) (uint32, string, error) {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
_token := token.Generate(16)
var tid uint32
err = database.QueryRow(`INSERT INTO sign_sessions (psn_id, token) VALUES ($1, $2) RETURNING id`, psn, _token).Scan(&tid)
return tid, _token, err
}
func (server *SignServer) validateToken(token string, tokenID uint32) bool {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
query := `SELECT count(*) FROM sign_sessions WHERE token = $1`
if tokenID > 0 {
query += ` AND id = $2`
}
var exists int
err = database.QueryRow(query, token, tokenID).Scan(&exists)
if err != nil || exists == 0 {
return false
}
return true
}
func (server *SignServer) validateLogin(user string, pass string) (uint32, RespID) {
database, err := db.GetDB() // Capture both return values
if err != nil {
server.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
var uid uint32
var passDB string
err = database.QueryRow(`SELECT id, password FROM users WHERE username = $1`, user).Scan(&uid, &passDB)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
server.logger.Info("User not found", zap.String("User", user))
if config.GetConfig().AutoCreateAccount {
uid, err = server.registerDBAccount(user, pass)
if err == nil {
return uid, SIGN_SUCCESS
} else {
return 0, SIGN_EABORT
}
}
return 0, SIGN_EAUTH
}
return 0, SIGN_EABORT
} else {
if bcrypt.CompareHashAndPassword([]byte(passDB), []byte(pass)) == nil {
var bans int
err = database.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires IS NULL`, uid).Scan(&bans)
if err == nil && bans > 0 {
return uid, SIGN_EELIMINATE
}
err = database.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires > now()`, uid).Scan(&bans)
if err == nil && bans > 0 {
return uid, SIGN_ESUSPEND
}
return uid, SIGN_SUCCESS
}
return 0, SIGN_EPASS
}
}

View File

@@ -1,12 +1,13 @@
package signserver
package sign
import (
"erupe-ce/utils/byteframe"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
ps "erupe-ce/utils/pascalstring"
"erupe-ce/utils/stringsupport"
_config "erupe-ce/config"
"erupe-ce/config"
"fmt"
"strings"
"time"
@@ -40,7 +41,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
return bf.Data()
}
if s.client == PS3 && (s.server.erupeConfig.PatchServerFile == "" || s.server.erupeConfig.PatchServerManifest == "") {
if s.client == PS3 && (config.GetConfig().PatchServerFile == "" || config.GetConfig().PatchServerManifest == "") {
bf.WriteUint8(uint8(SIGN_EABORT))
return bf.Data()
}
@@ -53,16 +54,16 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteBytes([]byte(sessToken))
bf.WriteUint32(uint32(gametime.TimeAdjusted().Unix()))
if s.client == PS3 {
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerManifest), false)
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerFile), false)
ps.Uint8(bf, fmt.Sprintf("%s/ps3", config.GetConfig().PatchServerManifest), false)
ps.Uint8(bf, fmt.Sprintf("%s/ps3", config.GetConfig().PatchServerFile), false)
} else {
ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false)
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
ps.Uint8(bf, config.GetConfig().PatchServerManifest, false)
ps.Uint8(bf, config.GetConfig().PatchServerFile, false)
}
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", config.GetConfig().Entrance.Port), false)
} else {
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
ps.Uint8(bf, fmt.Sprintf("%s:%d", config.GetConfig().Host, config.GetConfig().Entrance.Port), false)
}
lastPlayed := uint32(0)
@@ -71,7 +72,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
lastPlayed = char.ID
}
bf.WriteUint32(char.ID)
if s.server.erupeConfig.DebugOptions.MaxLauncherHR {
if config.GetConfig().DebugOptions.MaxLauncherHR {
bf.WriteUint16(999)
} else {
bf.WriteUint16(char.HR)
@@ -84,7 +85,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteBool(true) // Use uint16 GR, no reason not to
bf.WriteBytes(stringsupport.PaddedString(char.Name, 16, true)) // Character name
bf.WriteBytes(stringsupport.PaddedString(char.UnkDescString, 32, false)) // unk str
if s.server.erupeConfig.ClientID >= _config.G7 {
if config.GetConfig().ClientID >= config.G7 {
bf.WriteUint16(char.GR)
bf.WriteUint8(0) // Unk
bf.WriteUint8(0) // Unk
@@ -125,13 +126,13 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
}
}
if s.server.erupeConfig.HideLoginNotice {
if config.GetConfig().HideLoginNotice {
bf.WriteBool(false)
} else {
bf.WriteBool(true)
bf.WriteUint8(0)
bf.WriteUint8(0)
ps.Uint16(bf, strings.Join(s.server.erupeConfig.LoginNotices[:], "<PAGE>"), true)
ps.Uint16(bf, strings.Join(config.GetConfig().LoginNotices[:], "<PAGE>"), true)
}
bf.WriteUint32(s.server.getLastCID(uid))
@@ -332,18 +333,21 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteUint16(uint16(len(filters.Data())))
bf.WriteBytes(filters.Data())
database, err := db.GetDB() // Capture both return values
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if s.client == VITA || s.client == PS3 || s.client == PS4 {
var psnUser string
s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
database.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
}
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[0])
if s.server.erupeConfig.DebugOptions.CapLink.Values[0] == 51728 {
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[1])
if s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20000 || s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20002 {
ps.Uint16(bf, s.server.erupeConfig.DebugOptions.CapLink.Key, false)
bf.WriteUint16(config.GetConfig().DebugOptions.CapLink.Values[0])
if config.GetConfig().DebugOptions.CapLink.Values[0] == 51728 {
bf.WriteUint16(config.GetConfig().DebugOptions.CapLink.Values[1])
if config.GetConfig().DebugOptions.CapLink.Values[1] == 20000 || config.GetConfig().DebugOptions.CapLink.Values[1] == 20002 {
ps.Uint16(bf, config.GetConfig().DebugOptions.CapLink.Key, false)
}
}
caStruct := []struct {
@@ -357,31 +361,31 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteUint32(caStruct[i].Unk1)
ps.Uint8(bf, caStruct[i].Unk2, false)
}
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[2])
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[3])
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[4])
if s.server.erupeConfig.DebugOptions.CapLink.Values[2] == 51729 && s.server.erupeConfig.DebugOptions.CapLink.Values[3] == 1 && s.server.erupeConfig.DebugOptions.CapLink.Values[4] == 20000 {
ps.Uint16(bf, fmt.Sprintf(`%s:%d`, s.server.erupeConfig.DebugOptions.CapLink.Host, s.server.erupeConfig.DebugOptions.CapLink.Port), false)
bf.WriteUint16(config.GetConfig().DebugOptions.CapLink.Values[2])
bf.WriteUint16(config.GetConfig().DebugOptions.CapLink.Values[3])
bf.WriteUint16(config.GetConfig().DebugOptions.CapLink.Values[4])
if config.GetConfig().DebugOptions.CapLink.Values[2] == 51729 && config.GetConfig().DebugOptions.CapLink.Values[3] == 1 && config.GetConfig().DebugOptions.CapLink.Values[4] == 20000 {
ps.Uint16(bf, fmt.Sprintf(`%s:%d`, config.GetConfig().DebugOptions.CapLink.Host, config.GetConfig().DebugOptions.CapLink.Port), false)
}
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
bf.WriteUint32(0)
tickets := []uint32{
s.server.erupeConfig.GameplayOptions.MezFesSoloTickets,
s.server.erupeConfig.GameplayOptions.MezFesGroupTickets,
config.GetConfig().GameplayOptions.MezFesSoloTickets,
config.GetConfig().GameplayOptions.MezFesGroupTickets,
}
stalls := []uint8{
10, 3, 6, 9, 4, 8, 5, 7,
}
if s.server.erupeConfig.GameplayOptions.MezFesSwitchMinigame {
if config.GetConfig().GameplayOptions.MezFesSwitchMinigame {
stalls[4] = 2
}
// We can just use the start timestamp as the event ID
bf.WriteUint32(uint32(gametime.TimeWeekStart().Unix()))
// Start time
bf.WriteUint32(uint32(gametime.TimeWeekNext().Add(-time.Duration(s.server.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix()))
bf.WriteUint32(uint32(gametime.TimeWeekNext().Add(-time.Duration(config.GetConfig().GameplayOptions.MezFesDuration) * time.Second).Unix()))
// End time
bf.WriteUint32(uint32(gametime.TimeWeekNext().Unix()))
bf.WriteUint8(uint8(len(tickets)))

View File

@@ -1,4 +1,4 @@
package signserver
package sign
type RespID uint8

View File

@@ -1,9 +1,12 @@
package signserver
package sign
import (
"database/sql"
"encoding/hex"
"erupe-ce/config"
"erupe-ce/utils/db"
"erupe-ce/utils/logger"
"erupe-ce/utils/stringsupport"
"fmt"
"net"
@@ -30,7 +33,7 @@ const (
type Session struct {
sync.Mutex
logger logger.Logger
server *Server
server *SignServer
rawConn net.Conn
cryptConn *network.CryptConn
client client
@@ -40,7 +43,7 @@ type Session struct {
func (s *Session) work() {
pkt, err := s.cryptConn.ReadPacket()
if s.server.erupeConfig.DebugOptions.LogInboundMessages {
if config.GetConfig().DebugOptions.LogInboundMessages {
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
@@ -84,7 +87,7 @@ func (s *Session) handlePacket(pkt []byte) error {
}
default:
s.logger.Warn("Unknown request", zap.String("reqType", reqType))
if s.server.erupeConfig.DebugOptions.LogInboundMessages {
if config.GetConfig().DebugOptions.LogInboundMessages {
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
}
}
@@ -108,7 +111,7 @@ func (s *Session) authenticate(username string, password string) {
default:
bf.WriteUint8(uint8(resp))
}
if s.server.erupeConfig.DebugOptions.LogOutboundMessages {
if config.GetConfig().DebugOptions.LogOutboundMessages {
fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(bf.Data()), hex.Dump(bf.Data()))
}
_ = s.cryptConn.SendPacket(bf.Data())
@@ -118,7 +121,11 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) {
_ = bf.ReadBytes(1)
wiiuKey := string(bf.ReadBytes(64))
var uid uint32
err := s.server.db.QueryRow(`SELECT id FROM users WHERE wiiu_key = $1`, wiiuKey).Scan(&uid)
database, err := db.GetDB() // Capture both return values
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow(`SELECT id FROM users WHERE wiiu_key = $1`, wiiuKey).Scan(&uid)
if err != nil {
if err == sql.ErrNoRows {
s.logger.Info("Unlinked Wii U attempted to authenticate", zap.String("Key", wiiuKey))
@@ -145,7 +152,11 @@ func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
}
s.psn = string(bf.ReadNullTerminatedBytes())
var uid uint32
err := s.server.db.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid)
database, err := db.GetDB() // Capture both return values
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
err = database.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid)
if err != nil {
if err == sql.ErrNoRows {
s.cryptConn.SendPacket(s.makeSignResponse(0))
@@ -162,9 +173,13 @@ func (s *Session) handlePSNLink(bf *byteframe.ByteFrame) {
credentials := strings.Split(stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()), "\n")
token := string(bf.ReadNullTerminatedBytes())
uid, resp := s.server.validateLogin(credentials[0], credentials[1])
database, err := db.GetDB() // Capture both return values
if err != nil {
s.logger.Fatal(fmt.Sprintf("Failed to get database instance: %s", err))
}
if resp == SIGN_SUCCESS && uid > 0 {
var psn string
err := s.server.db.QueryRow(`SELECT psn_id FROM sign_sessions WHERE token = $1`, token).Scan(&psn)
err := database.QueryRow(`SELECT psn_id FROM sign_sessions WHERE token = $1`, token).Scan(&psn)
if err != nil {
s.sendCode(SIGN_ECOGLINK)
return
@@ -172,7 +187,7 @@ func (s *Session) handlePSNLink(bf *byteframe.ByteFrame) {
// Since we check for the psn_id, this will never run
var exists int
err = s.server.db.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, psn).Scan(&exists)
err = database.QueryRow(`SELECT count(*) FROM users WHERE psn_id = $1`, psn).Scan(&exists)
if err != nil {
s.sendCode(SIGN_ECOGLINK)
return
@@ -182,7 +197,7 @@ func (s *Session) handlePSNLink(bf *byteframe.ByteFrame) {
}
var currentPSN string
err = s.server.db.QueryRow(`SELECT COALESCE(psn_id, '') FROM users WHERE username = $1`, credentials[0]).Scan(&currentPSN)
err = database.QueryRow(`SELECT COALESCE(psn_id, '') FROM users WHERE username = $1`, credentials[0]).Scan(&currentPSN)
if err != nil {
s.sendCode(SIGN_ECOGLINK)
return
@@ -191,7 +206,7 @@ func (s *Session) handlePSNLink(bf *byteframe.ByteFrame) {
return
}
_, err = s.server.db.Exec(`UPDATE users SET psn_id = $1 WHERE username = $2`, psn, credentials[0])
_, err = database.Exec(`UPDATE users SET psn_id = $1 WHERE username = $2`, psn, credentials[0])
if err == nil {
s.sendCode(SIGN_SUCCESS)
return

View File

@@ -1,4 +1,4 @@
package signserver
package sign
import (
"fmt"
@@ -6,44 +6,33 @@ import (
"net"
"sync"
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/network"
"erupe-ce/utils/logger"
"github.com/jmoiron/sqlx"
"go.uber.org/zap"
)
// Config struct allows configuring the server.
type Config struct {
DB *sqlx.DB
ErupeConfig *_config.Config
}
// Server is a MHF sign server.
type Server struct {
// SignServer is a MHF sign server.
type SignServer struct {
sync.Mutex
logger logger.Logger
erupeConfig *_config.Config
sessions map[int]*Session
db *sqlx.DB
listener net.Listener
isShuttingDown bool
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server{
logger: logger.Get().Named("sign"),
erupeConfig: config.ErupeConfig,
db: config.DB,
func NewServer() *SignServer {
s := &SignServer{
logger: logger.Get().Named("sign"),
}
return s
}
// Start starts the server in a new goroutine.
func (server *Server) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", server.erupeConfig.Sign.Port))
func (server *SignServer) Start() error {
l, err := net.Listen("tcp", fmt.Sprintf(":%d", config.GetConfig().Sign.Port))
if err != nil {
return err
}
@@ -55,7 +44,7 @@ func (server *Server) Start() error {
}
// Shutdown exits the server gracefully.
func (server *Server) Shutdown() {
func (server *SignServer) Shutdown() {
server.logger.Debug("Shutting down...")
server.Lock()
@@ -66,7 +55,7 @@ func (server *Server) Shutdown() {
server.listener.Close()
}
func (server *Server) acceptClients() {
func (server *SignServer) acceptClients() {
for {
conn, err := server.listener.Accept()
if err != nil {
@@ -86,7 +75,7 @@ func (server *Server) acceptClients() {
}
}
func (server *Server) handleConnection(conn net.Conn) {
func (server *SignServer) handleConnection(conn net.Conn) {
server.logger.Debug("New connection", zap.String("RemoteAddr", conn.RemoteAddr().String()))
defer conn.Close()

View File

@@ -1,260 +0,0 @@
package signserver
import (
"database/sql"
"errors"
"erupe-ce/utils/mhfcourse"
"erupe-ce/utils/token"
"strings"
"time"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
)
func (server *Server) newUserChara(uid uint32) error {
var numNewChars int
err := server.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", uid).Scan(&numNewChars)
if err != nil {
return err
}
// prevent users with an uninitialised character from creating more
if numNewChars >= 1 {
return err
}
_, err = server.db.Exec(`
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hr, gr, weapon_type, last_login)
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
uid,
uint32(time.Now().Unix()),
)
if err != nil {
return err
}
return nil
}
func (server *Server) registerDBAccount(username string, password string) (uint32, error) {
var uid uint32
server.logger.Info("Creating user", zap.String("User", username))
// Create salted hash of user password
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return 0, err
}
err = server.db.QueryRow("INSERT INTO users (username, password, return_expires) VALUES ($1, $2, $3) RETURNING id", username, string(passwordHash), time.Now().Add(time.Hour*24*30)).Scan(&uid)
if err != nil {
return 0, err
}
return uid, nil
}
type character struct {
ID uint32 `db:"id"`
IsFemale bool `db:"is_female"`
IsNewCharacter bool `db:"is_new_character"`
Name string `db:"name"`
UnkDescString string `db:"unk_desc_string"`
HR uint16 `db:"hr"`
GR uint16 `db:"gr"`
WeaponType uint16 `db:"weapon_type"`
LastLogin uint32 `db:"last_login"`
}
func (server *Server) getCharactersForUser(uid uint32) ([]character, error) {
characters := make([]character, 0)
err := server.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
if err != nil {
return nil, err
}
return characters, nil
}
func (server *Server) getReturnExpiry(uid uint32) time.Time {
var returnExpiry, lastLogin time.Time
server.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
returnExpiry = time.Now().Add(time.Hour * 24 * 30)
server.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
} else {
err := server.db.Get(&returnExpiry, "SELECT return_expires FROM users WHERE id=$1", uid)
if err != nil {
returnExpiry = time.Now()
server.db.Exec("UPDATE users SET return_expires=$1 WHERE id=$2", returnExpiry, uid)
}
}
server.db.Exec("UPDATE users SET last_login=$1 WHERE id=$2", time.Now(), uid)
return returnExpiry
}
func (server *Server) getLastCID(uid uint32) uint32 {
var lastPlayed uint32
_ = server.db.QueryRow("SELECT last_character FROM users WHERE id=$1", uid).Scan(&lastPlayed)
return lastPlayed
}
func (server *Server) getUserRights(uid uint32) uint32 {
var rights uint32
if uid != 0 {
_ = server.db.QueryRow("SELECT rights FROM users WHERE id=$1", uid).Scan(&rights)
_, rights = mhfcourse.GetCourseStruct(rights)
}
return rights
}
type members struct {
CID uint32 // Local character ID
ID uint32 `db:"id"`
Name string `db:"name"`
}
func (server *Server) getFriendsForCharacters(chars []character) []members {
friends := make([]members, 0)
for _, char := range chars {
friendsCSV := ""
err := server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", char.ID).Scan(&friendsCSV)
friendsSlice := strings.Split(friendsCSV, ",")
friendQuery := "SELECT id, name FROM characters WHERE id="
for i := 0; i < len(friendsSlice); i++ {
friendQuery += friendsSlice[i]
if i+1 != len(friendsSlice) {
friendQuery += " OR id="
}
}
charFriends := make([]members, 0)
err = server.db.Select(&charFriends, friendQuery)
if err != nil {
continue
}
for i := range charFriends {
charFriends[i].CID = char.ID
}
friends = append(friends, charFriends...)
}
return friends
}
func (server *Server) getGuildmatesForCharacters(chars []character) []members {
guildmates := make([]members, 0)
for _, char := range chars {
var inGuild int
_ = server.db.QueryRow("SELECT count(*) FROM guild_characters WHERE character_id=$1", char.ID).Scan(&inGuild)
if inGuild > 0 {
var guildID int
err := server.db.QueryRow("SELECT guild_id FROM guild_characters WHERE character_id=$1", char.ID).Scan(&guildID)
if err != nil {
continue
}
charGuildmates := make([]members, 0)
err = server.db.Select(&charGuildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, char.ID)
if err != nil {
continue
}
for i := range charGuildmates {
charGuildmates[i].CID = char.ID
}
guildmates = append(guildmates, charGuildmates...)
}
}
return guildmates
}
func (server *Server) deleteCharacter(cid int, token string, tokenID uint32) error {
if !server.validateToken(token, tokenID) {
return errors.New("invalid token")
}
var isNew bool
err := server.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", cid).Scan(&isNew)
if isNew {
_, err = server.db.Exec("DELETE FROM characters WHERE id = $1", cid)
} else {
_, err = server.db.Exec("UPDATE characters SET deleted = true WHERE id = $1", cid)
}
if err != nil {
return err
}
return nil
}
// Unused
func (server *Server) checkToken(uid uint32) (bool, error) {
var exists int
err := server.db.QueryRow("SELECT count(*) FROM sign_sessions WHERE user_id = $1", uid).Scan(&exists)
if err != nil {
return false, err
}
if exists > 0 {
return true, nil
}
return false, nil
}
func (server *Server) registerUidToken(uid uint32) (uint32, string, error) {
_token := token.Generate(16)
var tid uint32
err := server.db.QueryRow(`INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id`, uid, _token).Scan(&tid)
return tid, _token, err
}
func (server *Server) registerPsnToken(psn string) (uint32, string, error) {
_token := token.Generate(16)
var tid uint32
err := server.db.QueryRow(`INSERT INTO sign_sessions (psn_id, token) VALUES ($1, $2) RETURNING id`, psn, _token).Scan(&tid)
return tid, _token, err
}
func (server *Server) validateToken(token string, tokenID uint32) bool {
query := `SELECT count(*) FROM sign_sessions WHERE token = $1`
if tokenID > 0 {
query += ` AND id = $2`
}
var exists int
err := server.db.QueryRow(query, token, tokenID).Scan(&exists)
if err != nil || exists == 0 {
return false
}
return true
}
func (server *Server) validateLogin(user string, pass string) (uint32, RespID) {
var uid uint32
var passDB string
err := server.db.QueryRow(`SELECT id, password FROM users WHERE username = $1`, user).Scan(&uid, &passDB)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
server.logger.Info("User not found", zap.String("User", user))
if server.erupeConfig.AutoCreateAccount {
uid, err = server.registerDBAccount(user, pass)
if err == nil {
return uid, SIGN_SUCCESS
} else {
return 0, SIGN_EABORT
}
}
return 0, SIGN_EAUTH
}
return 0, SIGN_EABORT
} else {
if bcrypt.CompareHashAndPassword([]byte(passDB), []byte(pass)) == nil {
var bans int
err = server.db.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires IS NULL`, uid).Scan(&bans)
if err == nil && bans > 0 {
return uid, SIGN_EELIMINATE
}
err = server.db.QueryRow(`SELECT count(*) FROM bans WHERE user_id=$1 AND expires > now()`, uid).Scan(&bans)
if err == nil && bans > 0 {
return uid, SIGN_ESUSPEND
}
return uid, SIGN_SUCCESS
}
return 0, SIGN_EPASS
}
}

77
utils/db/db.go Normal file
View File

@@ -0,0 +1,77 @@
package db
import (
"fmt"
"sync"
"erupe-ce/config"
"erupe-ce/utils/logger"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq" // Postgres driver
)
var (
instance *sqlx.DB
once sync.Once
dbLogger logger.Logger
)
// InitDB initializes the database connection pool as a singleton
func InitDB(config *config.Config) (*sqlx.DB, error) {
dbLogger = logger.Get().Named("database")
var err error
once.Do(func() {
// Create the postgres DB pool.
connectString := fmt.Sprintf(
"host='%s' port='%d' user='%s' password='%s' dbname='%s' sslmode=disable",
config.Database.Host,
config.Database.Port,
config.Database.User,
config.Database.Password,
config.Database.Database,
)
instance, err = sqlx.Open("postgres", connectString)
if err != nil {
return // Stop here if there's an error opening the database
}
// Test the DB connection.
err = instance.Ping()
if err != nil {
return // Stop here if there's an error pinging the database
}
dbLogger.Info("Database: Started successfully")
// Clear stale data
if config.DebugOptions.ProxyPort == 0 {
_ = instance.MustExec("DELETE FROM sign_sessions")
}
_ = instance.MustExec("DELETE FROM servers")
_ = instance.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`)
// Clean the DB if the option is on.
if config.DebugOptions.CleanDB {
dbLogger.Info("Database: Started clearing...")
cleanDB(instance)
dbLogger.Info("Database: Finished clearing")
}
})
return instance, err
}
func GetDB() (*sqlx.DB, error) {
if instance == nil {
return nil, fmt.Errorf("database not initialized")
}
return instance, nil
}
// Temporary DB auto clean on startup for quick development & testing.
func cleanDB(db *sqlx.DB) {
_ = db.MustExec("DELETE FROM guild_characters")
_ = db.MustExec("DELETE FROM guilds")
_ = db.MustExec("DELETE FROM characters")
_ = db.MustExec("DELETE FROM users")
}

View File

@@ -1,7 +1,7 @@
package mhfcourse
import (
_config "erupe-ce/config"
"erupe-ce/config"
"math"
"sort"
"time"
@@ -68,7 +68,7 @@ func CourseExists(ID uint16, c []Course) bool {
// GetCourseStruct returns a slice of Course(s) from a rights integer
func GetCourseStruct(rights uint32) ([]Course, uint32) {
var resp []Course
for _, c := range _config.ErupeConfig.DefaultCourses {
for _, c := range config.GetConfig().DefaultCourses {
resp = append(resp, Course{ID: c})
}
s := Courses()

View File

@@ -1,7 +1,7 @@
package mhfitem
import (
_config "erupe-ce/config"
"erupe-ce/config"
"erupe-ce/utils/byteframe"
"erupe-ce/utils/token"
)
@@ -114,7 +114,7 @@ func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment {
for i := 0; i < 3; i++ {
equipment.Decorations[i].ItemID = bf.ReadUint16()
}
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
equipment.Sigils[i].Effects[j].ID = bf.ReadUint16()
@@ -128,7 +128,7 @@ func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment {
equipment.Sigils[i].Unk3 = bf.ReadUint8()
}
}
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
equipment.Unk1 = bf.ReadUint16()
}
return equipment
@@ -144,7 +144,7 @@ func (e MHFEquipment) ToBytes() []byte {
for i := 0; i < 3; i++ {
bf.WriteUint16(e.Decorations[i].ItemID)
}
if _config.ErupeConfig.ClientID >= _config.G1 {
if config.GetConfig().ClientID >= config.G1 {
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
bf.WriteUint16(e.Sigils[i].Effects[j].ID)
@@ -158,7 +158,7 @@ func (e MHFEquipment) ToBytes() []byte {
bf.WriteUint8(e.Sigils[i].Unk3)
}
}
if _config.ErupeConfig.ClientID >= _config.Z1 {
if config.GetConfig().ClientID >= config.Z1 {
bf.WriteUint16(e.Unk1)
}
return bf.Data()