mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
simplify config
This commit is contained in:
@@ -68,7 +68,7 @@ func CourseExists(ID uint16, c []Course) bool {
|
|||||||
// GetCourseStruct returns a slice of Course(s) from a rights integer
|
// GetCourseStruct returns a slice of Course(s) from a rights integer
|
||||||
func GetCourseStruct(rights uint32) ([]Course, uint32) {
|
func GetCourseStruct(rights uint32) ([]Course, uint32) {
|
||||||
var resp []Course
|
var resp []Course
|
||||||
for _, c := range _config.ErupeConfig.DevModeOptions.DefaultCourses {
|
for _, c := range _config.ErupeConfig.DefaultCourses {
|
||||||
resp = append(resp, Course{ID: c})
|
resp = append(resp, Course{ID: c})
|
||||||
}
|
}
|
||||||
s := Courses()
|
s := Courses()
|
||||||
|
|||||||
31
config.json
31
config.json
@@ -13,11 +13,18 @@
|
|||||||
"DeleteOnSaveCorruption": false,
|
"DeleteOnSaveCorruption": false,
|
||||||
"ClientMode": "ZZ",
|
"ClientMode": "ZZ",
|
||||||
"QuestCacheExpiry": 300,
|
"QuestCacheExpiry": 300,
|
||||||
"ProxyPort": 0,
|
|
||||||
"CommandPrefix": "!",
|
"CommandPrefix": "!",
|
||||||
"DevMode": true,
|
"AutoCreateAccount": true,
|
||||||
"DevModeOptions": {
|
"DefaultCourses": [1, 23, 24],
|
||||||
"AutoCreateAccount": true,
|
"EarthStatus": 0,
|
||||||
|
"EarthID": 0,
|
||||||
|
"EarthMonsters": [0, 0, 0, 0],
|
||||||
|
"SaveDumps": {
|
||||||
|
"Enabled": true,
|
||||||
|
"RawEnabled": false,
|
||||||
|
"OutputDir": "save-backups"
|
||||||
|
},
|
||||||
|
"DebugOptions": {
|
||||||
"CleanDB": false,
|
"CleanDB": false,
|
||||||
"MaxLauncherHR": false,
|
"MaxLauncherHR": false,
|
||||||
"LogInboundMessages": false,
|
"LogInboundMessages": false,
|
||||||
@@ -28,16 +35,8 @@
|
|||||||
"FestaEvent": -1,
|
"FestaEvent": -1,
|
||||||
"TournamentEvent": 0,
|
"TournamentEvent": 0,
|
||||||
"DisableTokenCheck": false,
|
"DisableTokenCheck": false,
|
||||||
"QuestDebugTools": false,
|
"QuestTools": false,
|
||||||
"DefaultCourses": [1, 23, 24],
|
"ProxyPort": 0,
|
||||||
"EarthStatusOverride": 0,
|
|
||||||
"EarthIDOverride": 0,
|
|
||||||
"EarthMonsterOverride": [0, 0, 0, 0],
|
|
||||||
"SaveDumps": {
|
|
||||||
"Enabled": true,
|
|
||||||
"RawEnabled": false,
|
|
||||||
"OutputDir": "save-backups"
|
|
||||||
},
|
|
||||||
"CapLink": {
|
"CapLink": {
|
||||||
"Values": [51728, 20000, 51729, 1, 20000],
|
"Values": [51728, 20000, 51729, 1, 20000],
|
||||||
"Key": "",
|
"Key": "",
|
||||||
@@ -58,8 +57,6 @@
|
|||||||
"ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]],
|
"ClanMemberLimits": [[0, 30], [3, 40], [7, 50], [10, 60]],
|
||||||
"BonusQuestAllowance": 3,
|
"BonusQuestAllowance": 3,
|
||||||
"DailyQuestAllowance": 1,
|
"DailyQuestAllowance": 1,
|
||||||
"MezfesSoloTickets": 10,
|
|
||||||
"MezfesGroupTickets": 4,
|
|
||||||
"LowLatencyRaviente": false,
|
"LowLatencyRaviente": false,
|
||||||
"RegularRavienteMaxPlayers": 8,
|
"RegularRavienteMaxPlayers": 8,
|
||||||
"ViolentRavienteMaxPlayers": 8,
|
"ViolentRavienteMaxPlayers": 8,
|
||||||
@@ -74,6 +71,8 @@
|
|||||||
"MaterialMultiplier": 1.00,
|
"MaterialMultiplier": 1.00,
|
||||||
"ExtraCarves": 0,
|
"ExtraCarves": 0,
|
||||||
"DisableHunterNavi": false,
|
"DisableHunterNavi": false,
|
||||||
|
"MezFesSoloTickets": 5,
|
||||||
|
"MezFesGroupTickets": 1,
|
||||||
"MezFesDuration": 172800,
|
"MezFesDuration": 172800,
|
||||||
"MezFesSwitchMinigame": false,
|
"MezFesSwitchMinigame": false,
|
||||||
"EnableKaijiEvent": false,
|
"EnableKaijiEvent": false,
|
||||||
|
|||||||
@@ -80,42 +80,23 @@ type Config struct {
|
|||||||
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
|
|
||||||
CommandPrefix string // The prefix for commands
|
CommandPrefix string // The prefix for commands
|
||||||
DevMode bool
|
AutoCreateAccount bool // Automatically create accounts if they don't exist
|
||||||
|
DefaultCourses []uint16
|
||||||
DevModeOptions DevModeOptions
|
EarthStatus int32
|
||||||
GameplayOptions GameplayOptions
|
EarthID int32
|
||||||
Discord Discord
|
EarthMonsters []int32
|
||||||
Commands []Command
|
SaveDumps SaveDumpOptions
|
||||||
Courses []Course
|
DebugOptions DebugOptions
|
||||||
Database Database
|
GameplayOptions GameplayOptions
|
||||||
Sign Sign
|
Discord Discord
|
||||||
SignV2 SignV2
|
Commands []Command
|
||||||
Channel Channel
|
Courses []Course
|
||||||
Entrance Entrance
|
Database Database
|
||||||
}
|
Sign Sign
|
||||||
|
SignV2 SignV2
|
||||||
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
|
Channel Channel
|
||||||
type DevModeOptions struct {
|
Entrance Entrance
|
||||||
AutoCreateAccount bool // Automatically create accounts if they don't exist
|
|
||||||
CleanDB bool // Automatically wipes the DB on server reset.
|
|
||||||
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
|
|
||||||
LogInboundMessages bool // Log all messages sent to the server
|
|
||||||
LogOutboundMessages bool // Log all messages sent to the clients
|
|
||||||
LogMessageData bool // Log all bytes transferred as a hexdump
|
|
||||||
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
|
|
||||||
DivaEvent int // Diva Defense event status
|
|
||||||
FestaEvent int // Hunter's Festa event status
|
|
||||||
TournamentEvent int // VS Tournament event status
|
|
||||||
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
|
|
||||||
QuestDebugTools bool // Enable various quest debug logs
|
|
||||||
DefaultCourses []uint16
|
|
||||||
EarthStatusOverride int32
|
|
||||||
EarthIDOverride int32
|
|
||||||
EarthMonsterOverride []int32
|
|
||||||
SaveDumps SaveDumpOptions
|
|
||||||
CapLink CapLinkOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SaveDumpOptions struct {
|
type SaveDumpOptions struct {
|
||||||
@@ -124,6 +105,23 @@ type SaveDumpOptions struct {
|
|||||||
OutputDir string
|
OutputDir string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DebugOptions holds various debug/temporary options for use while developing Erupe.
|
||||||
|
type DebugOptions struct {
|
||||||
|
CleanDB bool // Automatically wipes the DB on server reset.
|
||||||
|
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
|
||||||
|
LogInboundMessages bool // Log all messages sent to the server
|
||||||
|
LogOutboundMessages bool // Log all messages sent to the clients
|
||||||
|
LogMessageData bool // Log all bytes transferred as a hexdump
|
||||||
|
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
|
||||||
|
DivaEvent int // Diva Defense event status
|
||||||
|
FestaEvent int // Hunter's Festa event status
|
||||||
|
TournamentEvent int // VS Tournament event status
|
||||||
|
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
|
||||||
|
QuestTools bool // Enable various quest debug logs
|
||||||
|
ProxyPort uint16 // Forces the game to connect to a channel server proxy
|
||||||
|
CapLink CapLinkOptions
|
||||||
|
}
|
||||||
|
|
||||||
type CapLinkOptions struct {
|
type CapLinkOptions struct {
|
||||||
Values []uint16
|
Values []uint16
|
||||||
Key string
|
Key string
|
||||||
@@ -146,8 +144,6 @@ type GameplayOptions struct {
|
|||||||
ClanMemberLimits [][]uint8 // Array of maximum Clan Members -> [Rank, Members]
|
ClanMemberLimits [][]uint8 // Array of maximum Clan Members -> [Rank, Members]
|
||||||
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
|
|
||||||
MezfesGroupTickets uint32 // Number of group tickets given weekly
|
|
||||||
LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive
|
LowLatencyRaviente bool // Toggles low latency mode for Raviente, can be network intensive
|
||||||
RegularRavienteMaxPlayers uint8
|
RegularRavienteMaxPlayers uint8
|
||||||
ViolentRavienteMaxPlayers uint8
|
ViolentRavienteMaxPlayers uint8
|
||||||
@@ -162,6 +158,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
|
||||||
|
MezFesSoloTickets uint32 // Number of solo tickets given weekly
|
||||||
|
MezFesGroupTickets uint32 // Number of group tickets given weekly
|
||||||
MezFesDuration int // Seconds that MezFes will last for weekly (from 12AM Mon backwards)
|
MezFesDuration int // Seconds that MezFes will last for weekly (from 12AM Mon backwards)
|
||||||
MezFesSwitchMinigame bool // Swaps out Volpakkun Together for Tokotoko Partnya
|
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
|
||||||
|
|||||||
17
main.go
17
main.go
@@ -22,13 +22,10 @@ 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, config *_config.Config) {
|
func cleanDB(db *sqlx.DB) {
|
||||||
_ = 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")
|
||||||
if config.ProxyPort == 0 {
|
|
||||||
_ = db.MustExec("DELETE FROM sign_sessions")
|
|
||||||
}
|
|
||||||
_ = db.MustExec("DELETE FROM users")
|
_ = db.MustExec("DELETE FROM users")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +45,7 @@ func main() {
|
|||||||
|
|
||||||
var zapLogger *zap.Logger
|
var zapLogger *zap.Logger
|
||||||
config := _config.ErupeConfig
|
config := _config.ErupeConfig
|
||||||
if config.DevMode {
|
zapLogger, _ = zap.NewDevelopment()
|
||||||
zapLogger, _ = zap.NewDevelopment()
|
|
||||||
} else {
|
|
||||||
zapLogger, _ = zap.NewProduction()
|
|
||||||
}
|
|
||||||
|
|
||||||
defer zapLogger.Sync()
|
defer zapLogger.Sync()
|
||||||
logger := zapLogger.Named("main")
|
logger := zapLogger.Named("main")
|
||||||
@@ -126,16 +119,16 @@ func main() {
|
|||||||
logger.Info("Database: Started successfully")
|
logger.Info("Database: Started successfully")
|
||||||
|
|
||||||
// Clear stale data
|
// Clear stale data
|
||||||
if config.ProxyPort == 0 {
|
if config.DebugOptions.ProxyPort == 0 {
|
||||||
_ = db.MustExec("DELETE FROM sign_sessions")
|
_ = 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.DebugOptions.CleanDB {
|
||||||
logger.Info("Database: Started clearing...")
|
logger.Info("Database: Started clearing...")
|
||||||
cleanDB(db, config)
|
cleanDB(db)
|
||||||
logger.Info("Database: Finished clearing")
|
logger.Info("Database: Finished clearing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
|
|||||||
|
|
||||||
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
|
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(uint32(s.server.erupeConfig.DevModeOptions.EarthIDOverride))
|
bf.WriteUint32(uint32(s.server.erupeConfig.EarthID))
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
bf.WriteUint32(uint32(len(data)))
|
bf.WriteUint32(uint32(len(data)))
|
||||||
@@ -127,7 +127,7 @@ func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysLogin)
|
pkt := p.(*mhfpacket.MsgSysLogin)
|
||||||
|
|
||||||
if !s.server.erupeConfig.DevModeOptions.DisableTokenCheck {
|
if !s.server.erupeConfig.DebugOptions.DisableTokenCheck {
|
||||||
var token string
|
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 := 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)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1147,9 +1147,9 @@ func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start
|
bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start
|
||||||
bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End
|
bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End
|
||||||
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride)
|
bf.WriteInt32(s.server.erupeConfig.EarthStatus)
|
||||||
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)
|
bf.WriteInt32(s.server.erupeConfig.EarthID)
|
||||||
for i, m := range s.server.erupeConfig.DevModeOptions.EarthMonsterOverride {
|
for i, m := range s.server.erupeConfig.EarthMonsters {
|
||||||
if _config.ErupeConfig.RealClientMode <= _config.G9 {
|
if _config.ErupeConfig.RealClientMode <= _config.G9 {
|
||||||
if i == 3 {
|
if i == 3 {
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools == true && s.server.erupeConfig.DevMode {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
|
if pkt.BroadcastType == 0x03 && pkt.MessageType == 0x02 && len(pkt.RawDataPayload) > 32 {
|
||||||
// This is only correct most of the time
|
// This is only correct most of the time
|
||||||
tmp.ReadBytes(20)
|
tmp.ReadBytes(20)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.server.erupeConfig.DevModeOptions.SaveDumps.RawEnabled {
|
if s.server.erupeConfig.SaveDumps.RawEnabled {
|
||||||
dumpSaveData(s, saveData, "raw-savedata")
|
dumpSaveData(s, saveData, "raw-savedata")
|
||||||
}
|
}
|
||||||
s.logger.Info("Updating save with blob")
|
s.logger.Info("Updating save with blob")
|
||||||
@@ -112,11 +112,11 @@ func grpToGR(n int) uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dumpSaveData(s *Session, data []byte, suffix string) {
|
func dumpSaveData(s *Session, data []byte, suffix string) {
|
||||||
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
|
if !s.server.erupeConfig.SaveDumps.Enabled {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID))
|
dir := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID))
|
||||||
path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix))
|
path := filepath.Join(s.server.erupeConfig.SaveDumps.OutputDir, fmt.Sprintf("%d", s.charID), fmt.Sprintf("%d_%s.bin", s.charID, suffix))
|
||||||
_, err := os.Stat(dir)
|
_, err := os.Stat(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var timestamps []uint32
|
var timestamps []uint32
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 {
|
if s.server.erupeConfig.DebugOptions.DivaEvent >= 0 {
|
||||||
if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 {
|
if s.server.erupeConfig.DebugOptions.DivaEvent == 0 {
|
||||||
if s.server.erupeConfig.RealClientMode >= _config.Z2 {
|
if s.server.erupeConfig.RealClientMode >= _config.Z2 {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
|
||||||
} else {
|
} else {
|
||||||
@@ -79,7 +79,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true)
|
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.DivaEvent), true)
|
||||||
} else {
|
} else {
|
||||||
timestamps = generateDivaTimestamps(s, start, false)
|
timestamps = generateDivaTimestamps(s, start, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateRanking)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateRanking)
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
state := s.server.erupeConfig.DevModeOptions.TournamentEvent
|
state := s.server.erupeConfig.DebugOptions.TournamentEvent
|
||||||
// Unk
|
// Unk
|
||||||
// Unk
|
// Unk
|
||||||
// Start?
|
// Start?
|
||||||
@@ -173,12 +173,12 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var timestamps []uint32
|
var timestamps []uint32
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.FestaEvent >= 0 {
|
if s.server.erupeConfig.DebugOptions.FestaEvent >= 0 {
|
||||||
if s.server.erupeConfig.DevModeOptions.FestaEvent == 0 {
|
if s.server.erupeConfig.DebugOptions.FestaEvent == 0 {
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.FestaEvent), true)
|
timestamps = generateFestaTimestamps(s, uint32(s.server.erupeConfig.DebugOptions.FestaEvent), true)
|
||||||
} else {
|
} else {
|
||||||
timestamps = generateFestaTimestamps(s, start, false)
|
timestamps = generateFestaTimestamps(s, start, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {}
|
|||||||
|
|
||||||
func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysPositionObject)
|
pkt := p.(*mhfpacket.MsgSysPositionObject)
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
|
if s.server.erupeConfig.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)
|
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()
|
s.stage.Lock()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
pkt := p.(*mhfpacket.MsgSysGetFile)
|
pkt := p.(*mhfpacket.MsgSysGetFile)
|
||||||
|
|
||||||
if pkt.IsScenario {
|
if pkt.IsScenario {
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
s.logger.Debug(
|
s.logger.Debug(
|
||||||
"Scenario",
|
"Scenario",
|
||||||
zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID),
|
zap.Uint8("CategoryID", pkt.ScenarioIdentifer.CategoryID),
|
||||||
@@ -40,7 +40,7 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
} else {
|
} else {
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools && s.server.erupeConfig.DevMode {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
s.logger.Debug(
|
s.logger.Debug(
|
||||||
"Quest",
|
"Quest",
|
||||||
zap.String("Filename", pkt.Filename),
|
zap.String("Filename", pkt.Filename),
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfPostTowerInfo)
|
pkt := p.(*mhfpacket.MsgMhfPostTowerInfo)
|
||||||
|
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
s.logger.Debug(
|
s.logger.Debug(
|
||||||
p.Opcode().String(),
|
p.Opcode().String(),
|
||||||
zap.Uint32("InfoType", pkt.InfoType),
|
zap.Uint32("InfoType", pkt.InfoType),
|
||||||
@@ -328,7 +328,7 @@ func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
|
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
|
||||||
|
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
s.logger.Debug(
|
s.logger.Debug(
|
||||||
p.Opcode().String(),
|
p.Opcode().String(),
|
||||||
zap.Uint8("Unk0", pkt.Unk0),
|
zap.Uint8("Unk0", pkt.Unk0),
|
||||||
@@ -442,7 +442,7 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfPostGemInfo)
|
pkt := p.(*mhfpacket.MsgMhfPostGemInfo)
|
||||||
|
|
||||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
if s.server.erupeConfig.DebugOptions.QuestTools {
|
||||||
s.logger.Debug(
|
s.logger.Debug(
|
||||||
p.Opcode().String(),
|
p.Opcode().String(),
|
||||||
zap.Uint32("Op", pkt.Op),
|
zap.Uint32("Op", pkt.Op),
|
||||||
|
|||||||
@@ -253,13 +253,9 @@ func ignored(opcode network.PacketID) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipient string) {
|
func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipient string) {
|
||||||
if !s.server.erupeConfig.DevMode {
|
if sender == "Server" && !s.server.erupeConfig.DebugOptions.LogOutboundMessages {
|
||||||
return
|
return
|
||||||
}
|
} else if sender != "Server" && !s.server.erupeConfig.DebugOptions.LogInboundMessages {
|
||||||
|
|
||||||
if sender == "Server" && !s.server.erupeConfig.DevModeOptions.LogOutboundMessages {
|
|
||||||
return
|
|
||||||
} else if sender != "Server" && !s.server.erupeConfig.DevModeOptions.LogInboundMessages {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +273,8 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
|
|||||||
fmt.Printf("[%s] -> [%s]\n", sender, recipient)
|
fmt.Printf("[%s] -> [%s]\n", sender, recipient)
|
||||||
}
|
}
|
||||||
fmt.Printf("Opcode: %s\n", opcodePID)
|
fmt.Printf("Opcode: %s\n", opcodePID)
|
||||||
if s.server.erupeConfig.DevModeOptions.LogMessageData {
|
if s.server.erupeConfig.DebugOptions.LogMessageData {
|
||||||
if len(data) <= s.server.erupeConfig.DevModeOptions.MaxHexdumpLength {
|
if len(data) <= s.server.erupeConfig.DebugOptions.MaxHexdumpLength {
|
||||||
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
|
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Data [%d bytes]: (Too long!)\n\n", len(data))
|
fmt.Printf("Data [%d bytes]: (Too long!)\n\n", len(data))
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ func (s *Server) handleEntranceServerConnection(conn net.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogInboundMessages {
|
if s.erupeConfig.DebugOptions.LogInboundMessages {
|
||||||
fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
fmt.Printf("[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ 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)
|
||||||
if _config.ErupeConfig.DevMode && _config.ErupeConfig.ProxyPort != 0 {
|
if _config.ErupeConfig.DebugOptions.ProxyPort != 0 {
|
||||||
bf.WriteUint16(_config.ErupeConfig.ProxyPort)
|
bf.WriteUint16(_config.ErupeConfig.DebugOptions.ProxyPort)
|
||||||
} else {
|
} else {
|
||||||
bf.WriteUint16(ci.Port)
|
bf.WriteUint16(ci.Port)
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte {
|
|||||||
}
|
}
|
||||||
rawServerData := encodeServerInfo(config, s, local)
|
rawServerData := encodeServerInfo(config, s, local)
|
||||||
|
|
||||||
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
|
if s.erupeConfig.DebugOptions.LogOutboundMessages {
|
||||||
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
|
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ func makeUsrResp(pkt []byte, s *Server) []byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
|
if s.erupeConfig.DebugOptions.LogOutboundMessages {
|
||||||
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data()))
|
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(resp.Data()), hex.Dump(resp.Data()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ func (s *Server) validateLogin(user string, pass string) (uint32, RespID) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
s.logger.Info("User not found", zap.String("User", user))
|
s.logger.Info("User not found", zap.String("User", user))
|
||||||
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.AutoCreateAccount {
|
if s.erupeConfig.AutoCreateAccount {
|
||||||
uid, err = s.registerDBAccount(user, pass)
|
uid, err = s.registerDBAccount(user, pass)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return uid, SIGN_SUCCESS
|
return uid, SIGN_SUCCESS
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
bf.WriteUint32(char.ID)
|
bf.WriteUint32(char.ID)
|
||||||
|
|
||||||
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR {
|
if s.server.erupeConfig.DebugOptions.MaxLauncherHR {
|
||||||
bf.WriteUint16(999)
|
bf.WriteUint16(999)
|
||||||
} else {
|
} else {
|
||||||
bf.WriteUint16(char.HRP)
|
bf.WriteUint16(char.HRP)
|
||||||
@@ -145,11 +145,11 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
|
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint16(s.server.erupeConfig.DevModeOptions.CapLink.Values[0])
|
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[0])
|
||||||
if s.server.erupeConfig.DevModeOptions.CapLink.Values[0] == 51728 {
|
if s.server.erupeConfig.DebugOptions.CapLink.Values[0] == 51728 {
|
||||||
bf.WriteUint16(s.server.erupeConfig.DevModeOptions.CapLink.Values[1])
|
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[1])
|
||||||
if s.server.erupeConfig.DevModeOptions.CapLink.Values[1] == 20000 || s.server.erupeConfig.DevModeOptions.CapLink.Values[1] == 20002 {
|
if s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20000 || s.server.erupeConfig.DebugOptions.CapLink.Values[1] == 20002 {
|
||||||
ps.Uint16(bf, s.server.erupeConfig.DevModeOptions.CapLink.Key, false)
|
ps.Uint16(bf, s.server.erupeConfig.DebugOptions.CapLink.Key, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
caStruct := []struct {
|
caStruct := []struct {
|
||||||
@@ -163,19 +163,19 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
|||||||
bf.WriteUint32(caStruct[i].Unk1)
|
bf.WriteUint32(caStruct[i].Unk1)
|
||||||
ps.Uint8(bf, caStruct[i].Unk2, false)
|
ps.Uint8(bf, caStruct[i].Unk2, false)
|
||||||
}
|
}
|
||||||
bf.WriteUint16(s.server.erupeConfig.DevModeOptions.CapLink.Values[2])
|
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[2])
|
||||||
bf.WriteUint16(s.server.erupeConfig.DevModeOptions.CapLink.Values[3])
|
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[3])
|
||||||
bf.WriteUint16(s.server.erupeConfig.DevModeOptions.CapLink.Values[4])
|
bf.WriteUint16(s.server.erupeConfig.DebugOptions.CapLink.Values[4])
|
||||||
if s.server.erupeConfig.DevModeOptions.CapLink.Values[2] == 51729 && s.server.erupeConfig.DevModeOptions.CapLink.Values[3] == 1 && s.server.erupeConfig.DevModeOptions.CapLink.Values[4] == 20000 {
|
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.DevModeOptions.CapLink.Host, s.server.erupeConfig.DevModeOptions.CapLink.Port), false)
|
ps.Uint16(bf, fmt.Sprintf(`%s:%d`, s.server.erupeConfig.DebugOptions.CapLink.Host, s.server.erupeConfig.DebugOptions.CapLink.Port), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
|
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
|
||||||
bf.WriteUint32(0)
|
bf.WriteUint32(0)
|
||||||
|
|
||||||
tickets := []uint32{
|
tickets := []uint32{
|
||||||
s.server.erupeConfig.GameplayOptions.MezfesSoloTickets,
|
s.server.erupeConfig.GameplayOptions.MezFesSoloTickets,
|
||||||
s.server.erupeConfig.GameplayOptions.MezfesGroupTickets,
|
s.server.erupeConfig.GameplayOptions.MezFesGroupTickets,
|
||||||
}
|
}
|
||||||
stalls := []uint8{
|
stalls := []uint8{
|
||||||
10, 3, 6, 9, 4, 8, 5, 7,
|
10, 3, 6, 9, 4, 8, 5, 7,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ type Session struct {
|
|||||||
func (s *Session) work() {
|
func (s *Session) work() {
|
||||||
pkt, err := s.cryptConn.ReadPacket()
|
pkt, err := s.cryptConn.ReadPacket()
|
||||||
|
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
|
if s.server.erupeConfig.DebugOptions.LogInboundMessages {
|
||||||
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ func (s *Session) handlePacket(pkt []byte) error {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
s.logger.Warn("Unknown request", zap.String("reqType", reqType))
|
s.logger.Warn("Unknown request", zap.String("reqType", reqType))
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogInboundMessages {
|
if s.server.erupeConfig.DebugOptions.LogInboundMessages {
|
||||||
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
fmt.Printf("\n[Client] -> [Server]\nData [%d bytes]:\n%s\n", len(pkt), hex.Dump(pkt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ func (s *Session) authenticate(username string, password string) {
|
|||||||
default:
|
default:
|
||||||
bf.WriteUint8(uint8(resp))
|
bf.WriteUint8(uint8(resp))
|
||||||
}
|
}
|
||||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages {
|
if s.server.erupeConfig.DebugOptions.LogOutboundMessages {
|
||||||
fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(bf.Data()), hex.Dump(bf.Data()))
|
fmt.Printf("\n[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(bf.Data()), hex.Dump(bf.Data()))
|
||||||
}
|
}
|
||||||
_ = s.cryptConn.SendPacket(bf.Data())
|
_ = s.cryptConn.SendPacket(bf.Data())
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3
|
|||||||
PatchServer: s.erupeConfig.SignV2.PatchServer,
|
PatchServer: s.erupeConfig.SignV2.PatchServer,
|
||||||
Notices: []string{},
|
Notices: []string{},
|
||||||
}
|
}
|
||||||
if s.erupeConfig.DevModeOptions.MaxLauncherHR {
|
if s.erupeConfig.DebugOptions.MaxLauncherHR {
|
||||||
for i := range resp.Characters {
|
for i := range resp.Characters {
|
||||||
resp.Characters[i].HR = 7
|
resp.Characters[i].HR = 7
|
||||||
}
|
}
|
||||||
@@ -93,8 +93,8 @@ func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint3
|
|||||||
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().Add(-time.Duration(s.erupeConfig.GameplayOptions.MezFesDuration) * time.Second).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 {
|
||||||
@@ -226,7 +226,7 @@ func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if s.erupeConfig.DevModeOptions.MaxLauncherHR {
|
if s.erupeConfig.DebugOptions.MaxLauncherHR {
|
||||||
character.HR = 7
|
character.HR = 7
|
||||||
}
|
}
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
|||||||
Reference in New Issue
Block a user