Merge branch 'ZeruLight:main' into feature/discord-login

This commit is contained in:
Matthew
2023-11-26 09:18:54 -08:00
committed by GitHub
18 changed files with 126 additions and 122 deletions

View File

@@ -13,6 +13,7 @@
"DeleteOnSaveCorruption": false, "DeleteOnSaveCorruption": false,
"ClientMode": "ZZ", "ClientMode": "ZZ",
"QuestCacheExpiry": 300, "QuestCacheExpiry": 300,
"ProxyPort": 0,
"DevMode": true, "DevMode": true,
"DevModeOptions": { "DevModeOptions": {
"AutoCreateAccount": true, "AutoCreateAccount": true,
@@ -25,8 +26,6 @@
"DivaEvent": 0, "DivaEvent": 0,
"FestaEvent": -1, "FestaEvent": -1,
"TournamentEvent": 0, "TournamentEvent": 0,
"MezFesEvent": true,
"MezFesAlt": false,
"DisableTokenCheck": false, "DisableTokenCheck": false,
"QuestDebugTools": false, "QuestDebugTools": false,
"EarthStatusOverride": 0, "EarthStatusOverride": 0,
@@ -45,8 +44,8 @@
"TreasureHuntExpiry": 604800, "TreasureHuntExpiry": 604800,
"DisableLoginBoost": false, "DisableLoginBoost": false,
"DisableBoostTime": false, "DisableBoostTime": false,
"BoostTimeDuration": 120, "BoostTimeDuration": 7200,
"GuildMealDuration": 60, "GuildMealDuration": 3600,
"BonusQuestAllowance": 3, "BonusQuestAllowance": 3,
"DailyQuestAllowance": 1, "DailyQuestAllowance": 1,
"MezfesSoloTickets": 10, "MezfesSoloTickets": 10,
@@ -65,6 +64,8 @@
"MaterialMultiplier": 1.00, "MaterialMultiplier": 1.00,
"ExtraCarves": 0, "ExtraCarves": 0,
"DisableHunterNavi": false, "DisableHunterNavi": false,
"MezFesDuration": 172800,
"MezFesSwitchMinigame": false,
"EnableKaijiEvent": false, "EnableKaijiEvent": false,
"EnableHiganjimaEvent": false, "EnableHiganjimaEvent": false,
"EnableNierEvent": false, "EnableNierEvent": false,

View File

@@ -79,7 +79,8 @@ type Config struct {
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
ClientMode string ClientMode string
RealClientMode Mode RealClientMode Mode
QuestCacheExpiry int // Number of seconds to keep quest data cached QuestCacheExpiry int // Number of seconds to keep quest data cached
ProxyPort uint16 // Forces the game to connect to a channel server proxy
DevMode bool DevMode bool
DevModeOptions DevModeOptions DevModeOptions DevModeOptions
@@ -106,8 +107,6 @@ type DevModeOptions struct {
DivaEvent int // Diva Defense event status DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status TournamentEvent int // VS Tournament event status
MezFesEvent bool // MezFes status
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestDebugTools bool // Enable various quest debug logs QuestDebugTools bool // Enable various quest debug logs
EarthStatusOverride int32 EarthStatusOverride int32
@@ -131,8 +130,8 @@ type GameplayOptions struct {
TreasureHuntPartnyaCooldown uint32 // Seconds until a Partnya can be assigned to another Clan Treasure Hunt TreasureHuntPartnyaCooldown uint32 // Seconds until a Partnya can be assigned to another Clan Treasure Hunt
DisableLoginBoost bool // Disables the Login Boost system DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time DisableBoostTime bool // Disables the daily NetCafe Boost Time
BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for BoostTimeDuration int // Second that the NetCafe Boost Time lasts
GuildMealDuration int // The number of minutes a Guild Meal can be activated for after cooking GuildMealDuration int // Second that a Guild Meal can be activated for after cooking
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
MezfesSoloTickets uint32 // Number of solo tickets given weekly MezfesSoloTickets uint32 // Number of solo tickets given weekly
@@ -151,6 +150,8 @@ type GameplayOptions struct {
MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion
ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses
DisableHunterNavi bool // Disables the Hunter Navi DisableHunterNavi bool // Disables the Hunter Navi
MezFesDuration int // Seconds that MezFes will last for weekly (from 12AM Mon backwards)
MezFesSwitchMinigame bool // Swaps out Volpakkun Together for Tokotoko Partnya
EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar
EnableHiganjimaEvent bool // Enables the Higanjima event in the Rasta Bar EnableHiganjimaEvent bool // Enables the Higanjima event in the Rasta Bar
EnableNierEvent bool // Enables the Nier event in the Rasta Bar EnableNierEvent bool // Enables the Nier event in the Rasta Bar

12
main.go
View File

@@ -22,11 +22,13 @@ import (
) )
// Temporary DB auto clean on startup for quick development & testing. // Temporary DB auto clean on startup for quick development & testing.
func cleanDB(db *sqlx.DB) { func cleanDB(db *sqlx.DB, config *_config.Config) {
_ = db.MustExec("DELETE FROM guild_characters") _ = db.MustExec("DELETE FROM guild_characters")
_ = db.MustExec("DELETE FROM guilds") _ = db.MustExec("DELETE FROM guilds")
_ = db.MustExec("DELETE FROM characters") _ = db.MustExec("DELETE FROM characters")
_ = db.MustExec("DELETE FROM sign_sessions") if config.ProxyPort == 0 {
_ = db.MustExec("DELETE FROM sign_sessions")
}
_ = db.MustExec("DELETE FROM users") _ = db.MustExec("DELETE FROM users")
} }
@@ -124,14 +126,16 @@ func main() {
logger.Info("Database: Started successfully") logger.Info("Database: Started successfully")
// Clear stale data // Clear stale data
_ = db.MustExec("DELETE FROM sign_sessions") if config.ProxyPort == 0 {
_ = db.MustExec("DELETE FROM sign_sessions")
}
_ = db.MustExec("DELETE FROM servers") _ = db.MustExec("DELETE FROM servers")
_ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`) _ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`)
// Clean the DB if the option is on. // Clean the DB if the option is on.
if config.DevMode && config.DevModeOptions.CleanDB { if config.DevMode && config.DevModeOptions.CleanDB {
logger.Info("Database: Started clearing...") logger.Info("Database: Started clearing...")
cleanDB(db) cleanDB(db, config)
logger.Info("Database: Finished clearing") logger.Info("Database: Finished clearing")
} }

View File

@@ -3,7 +3,6 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/common/bfutil"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
) )
@@ -13,7 +12,7 @@ type MsgSysCreateStage struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 // Likely only has 1 and 2 as values. Unk0 uint8 // Likely only has 1 and 2 as values.
PlayerCount uint8 PlayerCount uint8
StageID string // NULL terminated string. StageID string
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -26,8 +25,8 @@ func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint8()
m.PlayerCount = bf.ReadUint8() m.PlayerCount = bf.ReadUint8()
stageIDLength := bf.ReadUint8() bf.ReadUint8() // Length StageID
m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) m.StageID = string(bf.ReadNullTerminatedBytes())
return nil return nil
} }

View File

@@ -11,7 +11,7 @@ import (
// MsgSysEnterStage represents the MSG_SYS_ENTER_STAGE // MsgSysEnterStage represents the MSG_SYS_ENTER_STAGE
type MsgSysEnterStage struct { type MsgSysEnterStage struct {
AckHandle uint32 AckHandle uint32
UnkBool uint8 Unk bool
StageID string StageID string
} }
@@ -23,8 +23,8 @@ func (m *MsgSysEnterStage) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.UnkBool = bf.ReadUint8() m.Unk = bf.ReadBool() // IsQuest?
bf.ReadUint8() bf.ReadUint8() // Length StageID
m.StageID = string(bf.ReadNullTerminatedBytes()) m.StageID = string(bf.ReadNullTerminatedBytes())
return nil return nil
} }

View File

@@ -2,8 +2,6 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
@@ -12,8 +10,7 @@ import (
// MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE // MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE
type MsgSysEnumerateStage struct { type MsgSysEnumerateStage struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 // Hardcoded 1 in the binary StagePrefix string
StagePrefix string // NULL terminated string.
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -24,9 +21,9 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() bf.ReadUint8() // Always 1
bf.ReadUint8() bf.ReadUint8() // Length StagePrefix
m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) m.StagePrefix = string(bf.ReadNullTerminatedBytes())
return nil return nil
} }

View File

@@ -1,20 +1,17 @@
package mhfpacket package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgSysLockStage represents the MSG_SYS_LOCK_STAGE // MsgSysLockStage represents the MSG_SYS_LOCK_STAGE
type MsgSysLockStage struct { type MsgSysLockStage struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 // Hardcoded 1 in the binary StageID string
Unk1 uint8 // Hardcoded 1 in the binary
StageIDLength uint8
StageID string
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,10 +22,10 @@ func (m *MsgSysLockStage) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysLockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysLockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() bf.ReadUint8() // Always 1
m.Unk1 = bf.ReadUint8() bf.ReadUint8() // Always 1
m.StageIDLength = bf.ReadUint8() bf.ReadUint8() // Length StageID
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) m.StageID = string(bf.ReadNullTerminatedBytes())
return nil return nil
} }

View File

@@ -1,15 +1,14 @@
package mhfpacket package mhfpacket
import ( import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
) )
// MsgSysUnlockStage represents the MSG_SYS_UNLOCK_STAGE // MsgSysUnlockStage represents the MSG_SYS_UNLOCK_STAGE
type MsgSysUnlockStage struct { type MsgSysUnlockStage struct{}
Unk0 uint16 // Hardcoded 0 in the binary.
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgSysUnlockStage) Opcode() network.PacketID { func (m *MsgSysUnlockStage) Opcode() network.PacketID {
@@ -18,12 +17,11 @@ func (m *MsgSysUnlockStage) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.Unk0 = bf.ReadUint16() bf.ReadUint16() // Zeroed
return nil return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgSysUnlockStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysUnlockStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint16(m.Unk0) return errors.New("NOT IMPLEMENTED")
return nil
} }

View File

@@ -378,7 +378,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
if session.charID == CharID { if session.charID == CharID {
count++ count++
sessionName := stringsupport.UTF8ToSJIS(session.Name) sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stageID) sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port) resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID) resp.WriteUint32(session.charID)
@@ -408,7 +408,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
if strings.Contains(session.Name, searchTerm) { if strings.Contains(session.Name, searchTerm) {
count++ count++
sessionName := stringsupport.UTF8ToSJIS(session.Name) sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stageID) sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port) resp.WriteUint16(c.Port)
resp.WriteUint32(session.charID) resp.WriteUint32(session.charID)
@@ -445,7 +445,7 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
hrp := uint16(1) hrp := uint16(1)
gr := uint16(0) gr := uint16(0)
s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr) s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr)
sessionStage := stringsupport.UTF8ToSJIS(session.stageID) sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
sessionName := stringsupport.UTF8ToSJIS(session.Name) sessionName := stringsupport.UTF8ToSJIS(session.Name)
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
resp.WriteUint16(c.Port) resp.WriteUint16(c.Port)
@@ -988,8 +988,8 @@ func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus) pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(TimeWeekStart().Add(time.Hour * -24).Unix())) // Start bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start
bf.WriteUint32(uint32(TimeWeekNext().Add(time.Hour * 24).Unix())) // End bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride) bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride)
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride) bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride) bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride)

View File

@@ -220,7 +220,7 @@ func addPointNetcafe(s *Session, p int) error {
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStartBoostTime) pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
boostLimit := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Minute) boostLimit := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.BoostTimeDuration) * time.Second)
if s.server.erupeConfig.GameplayOptions.DisableBoostTime { if s.server.erupeConfig.GameplayOptions.DisableBoostTime {
bf.WriteUint32(0) bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())

View File

@@ -1831,18 +1831,18 @@ func handleMsgMhfLoadGuildCooking(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildCooking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking) pkt := p.(*mhfpacket.MsgMhfRegistGuildCooking)
guild, _ := GetGuildInfoByCharacterId(s, s.charID) guild, _ := GetGuildInfoByCharacterId(s, s.charID)
currentTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.GuildMealDuration-60) * time.Minute) startTime := TimeAdjusted().Add(time.Duration(s.server.erupeConfig.GameplayOptions.GuildMealDuration-3600) * time.Second)
if pkt.OverwriteID != 0 { if pkt.OverwriteID != 0 {
s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, currentTime, pkt.OverwriteID) s.server.db.Exec("UPDATE guild_meals SET meal_id = $1, level = $2, created_at = $3 WHERE id = $4", pkt.MealID, pkt.Success, startTime, pkt.OverwriteID)
} else { } else {
s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, currentTime).Scan(&pkt.OverwriteID) s.server.db.QueryRow("INSERT INTO guild_meals (guild_id, meal_id, level, created_at) VALUES ($1, $2, $3, $4) RETURNING id", guild.ID, pkt.MealID, pkt.Success, startTime).Scan(&pkt.OverwriteID)
} }
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint16(1) bf.WriteUint16(1)
bf.WriteUint32(pkt.OverwriteID) bf.WriteUint32(pkt.OverwriteID)
bf.WriteUint32(uint32(pkt.MealID)) bf.WriteUint32(uint32(pkt.MealID))
bf.WriteUint32(uint32(pkt.Success)) bf.WriteUint32(uint32(pkt.Success))
bf.WriteUint32(uint32(currentTime.Unix())) bf.WriteUint32(uint32(startTime.Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }

View File

@@ -55,7 +55,6 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
// Save our new stage ID and pointer to the new stage itself. // Save our new stage ID and pointer to the new stage itself.
s.Lock() s.Lock()
s.stageID = stageID
s.stage = s.server.stages[stageID] s.stage = s.server.stages[stageID]
s.Unlock() s.Unlock()
@@ -153,13 +152,13 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage) pkt := p.(*mhfpacket.MsgSysEnterStage)
// Push our current stage ID to the movement stack before entering another one. // Push our current stage ID to the movement stack before entering another one.
if s.stageID == "" { if s.stage.id == "" {
s.stageMoveStack.Set(pkt.StageID) s.stageMoveStack.Set(pkt.StageID)
} else { } else {
s.stage.Lock() s.stage.Lock()
s.stage.reservedClientSlots[s.charID] = false s.stage.reservedClientSlots[s.charID] = false
s.stage.Unlock() s.stage.Unlock()
s.stageMoveStack.Push(s.stageID) s.stageMoveStack.Push(s.stage.id)
s.stageMoveStack.Lock() s.stageMoveStack.Lock()
} }
@@ -206,9 +205,12 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLockStage) pkt := p.(*mhfpacket.MsgSysLockStage)
// TODO(Andoryuuta): What does this packet _actually_ do? if stage, exists := s.server.stages[pkt.StageID]; exists {
// I think this is supposed to mark a stage as no longer able to accept client reservations stage.Lock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) stage.locked = true
stage.Unlock()
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
@@ -218,7 +220,9 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
for charID := range s.reservationStage.reservedClientSlots { for charID := range s.reservationStage.reservedClientSlots {
session := s.server.FindSessionByCharID(charID) session := s.server.FindSessionByCharID(charID)
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) if session != nil {
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
} }
delete(s.server.stages, s.reservationStage.id) delete(s.server.stages, s.reservationStage.id)
@@ -241,6 +245,10 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
} }
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers { } else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
if stage.locked {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
if len(stage.password) > 0 { if len(stage.password) > 0 {
if stage.password != s.stagePass { if stage.password != s.stagePass {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
@@ -383,20 +391,17 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
joinable++ joinable++
bf.WriteUint16(uint16(len(stage.reservedClientSlots))) bf.WriteUint16(uint16(len(stage.reservedClientSlots)))
bf.WriteUint16(0) // Unk bf.WriteUint16(uint16(len(stage.clients)))
if len(stage.clients) > 0 { bf.WriteUint16(uint16(len(stage.clients)))
bf.WriteUint16(1)
} else {
bf.WriteUint16(0)
}
bf.WriteUint16(stage.maxPlayers) bf.WriteUint16(stage.maxPlayers)
if len(stage.password) > 0 { var flags uint8
// This byte has also been seen as 1 if stage.locked {
// The quest is also recognised as locked when this is 2 flags |= 1
bf.WriteUint8(2)
} else {
bf.WriteUint8(0)
} }
if len(stage.password) > 0 {
flags |= 2
}
bf.WriteUint8(flags)
ps.Uint8(bf, sid, false) ps.Uint8(bf, sid, false)
stage.RUnlock() stage.RUnlock()
} }

View File

@@ -36,7 +36,6 @@ type Session struct {
objectIndex uint16 objectIndex uint16
userEnteredStage bool // If the user has entered a stage before userEnteredStage bool // If the user has entered a stage before
stageID string
stage *Stage stage *Stage
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
stagePass string // Temporary storage stagePass string // Temporary storage

View File

@@ -47,6 +47,7 @@ type Stage struct {
host *Session host *Session
maxPlayers uint16 maxPlayers uint16
password string password string
locked bool
} }
// NewStage creates a new stage with intialized values. // NewStage creates a new stage with intialized values.

View File

@@ -16,8 +16,11 @@ func TimeMidnight() time.Time {
func TimeWeekStart() time.Time { func TimeWeekStart() time.Time {
midnight := TimeMidnight() midnight := TimeMidnight()
offset := (int(midnight.Weekday()) - 1) * -24 offset := int(midnight.Weekday()) - int(time.Monday)
return midnight.Add(time.Hour * time.Duration(offset)) if offset < 0 {
offset += 7
}
return midnight.Add(-time.Duration(offset) * 24 * time.Hour)
} }
func TimeWeekNext() time.Time { func TimeWeekNext() time.Time {

View File

@@ -69,7 +69,11 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
for channelIdx, ci := range si.Channels { for channelIdx, ci := range si.Channels {
sid = (4096 + serverIdx*256) + (16 + channelIdx) sid = (4096 + serverIdx*256) + (16 + channelIdx)
bf.WriteUint16(ci.Port) if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 {
bf.WriteUint16(_config.ErupeConfig.ProxyPort)
} else {
bf.WriteUint16(ci.Port)
}
bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(16 + uint16(channelIdx))
bf.WriteUint16(ci.MaxPlayers) bf.WriteUint16(ci.MaxPlayers)
var currentPlayers uint16 var currentPlayers uint16

View File

@@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
"strings" "strings"
"time"
) )
func (s *Session) makeSignResponse(uid uint32) []byte { func (s *Session) makeSignResponse(uid uint32) []byte {
@@ -154,35 +155,30 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix())) bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
bf.WriteUint32(0) bf.WriteUint32(0)
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent tickets := []uint32{
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt s.server.erupeConfig.GameplayOptions.MezfesSoloTickets,
if mezfes { s.server.erupeConfig.GameplayOptions.MezfesGroupTickets,
// We can just use the start timestamp as the event ID }
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) stalls := []uint8{
// Start time 10, 3, 6, 9, 4, 8, 5, 7,
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) }
// End time if s.server.erupeConfig.GameplayOptions.MezFesSwitchMinigame {
bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) stalls[4] = 2
bf.WriteUint8(2) // Unk }
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesSoloTickets)
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesGroupTickets) // We can just use the start timestamp as the event ID
bf.WriteUint8(8) // Stalls open bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
bf.WriteUint8(10) // Stall Map // Start time
bf.WriteUint8(3) // Pachinko bf.WriteUint32(uint32(channelserver.TimeWeekNext().Add(-time.Duration(s.server.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix()))
bf.WriteUint8(6) // Nyanrendo // End time
bf.WriteUint8(9) // Point stall bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix()))
if alt { bf.WriteUint8(uint8(len(tickets)))
bf.WriteUint8(2) // Tokotoko Partnya for i := range tickets {
} else { bf.WriteUint32(tickets[i])
bf.WriteUint8(4) // Volpakkun Together }
} bf.WriteUint8(uint8(len(stalls)))
bf.WriteUint8(8) // Dokkan Battle Cats for i := range stalls {
bf.WriteUint8(5) // Goocoo Scoop bf.WriteUint8(stalls[i])
bf.WriteUint8(7) // Honey Panic
} else {
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
} }
return bf.Data() return bf.Data()
} }

View File

@@ -8,6 +8,7 @@ import (
"erupe-ce/server/channelserver" "erupe-ce/server/channelserver"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/lib/pq" "github.com/lib/pq"
"go.uber.org/zap" "go.uber.org/zap"
@@ -82,19 +83,17 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userToken string,
resp.Characters[i].HR = 7 resp.Characters[i].HR = 7
} }
} }
if s.erupeConfig.DevModeOptions.MezFesEvent { stalls := []uint32{10, 3, 6, 9, 4, 8, 5, 7}
stalls := []uint32{10, 3, 6, 9, 4, 8, 5, 7} if s.erupeConfig.GameplayOptions.MezFesSwitchMinigame {
if s.erupeConfig.DevModeOptions.MezFesAlt { stalls[4] = 2
stalls[4] = 2 }
} resp.MezFes = &MezFes{
resp.MezFes = &MezFes{ ID: uint32(channelserver.TimeWeekStart().Unix()),
ID: uint32(channelserver.TimeWeekStart().Unix()), Start: uint32(channelserver.TimeWeekStart().Add(-time.Duration(s.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).Unix()),
Start: uint32(channelserver.TimeWeekStart().Unix()), End: uint32(channelserver.TimeWeekNext().Unix()),
End: uint32(channelserver.TimeWeekNext().Unix()), SoloTickets: s.erupeConfig.GameplayOptions.MezfesSoloTickets,
SoloTickets: s.erupeConfig.GameplayOptions.MezfesSoloTickets, GroupTickets: s.erupeConfig.GameplayOptions.MezfesGroupTickets,
GroupTickets: s.erupeConfig.GameplayOptions.MezfesGroupTickets, Stalls: stalls,
Stalls: stalls,
}
} }
if !s.erupeConfig.HideLoginNotice { if !s.erupeConfig.HideLoginNotice {
resp.Notices = append(resp.Notices, strings.Join(s.erupeConfig.LoginNotices[:], "<PAGE>")) resp.Notices = append(resp.Notices, strings.Join(s.erupeConfig.LoginNotices[:], "<PAGE>"))