Merge branch 'main' into feature/diva

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

View File

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

View File

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

View File

@@ -38,16 +38,18 @@ var commands map[string]config.Command
func init() {
commands = make(map[string]config.Command)
zapLogger, _ := zap.NewDevelopment()
zapConfig := zap.NewDevelopmentConfig()
zapConfig.DisableCaller = true
zapLogger, _ := zapConfig.Build()
defer zapLogger.Sync()
logger := zapLogger.Named("commands")
cmds := config.ErupeConfig.Commands
for _, cmd := range cmds {
commands[cmd.Name] = cmd
if cmd.Enabled {
logger.Info(fmt.Sprintf("%s command is enabled, prefix: %s", cmd.Name, cmd.Prefix))
logger.Info(fmt.Sprintf("Command %s: Enabled, prefix: %s", cmd.Name, cmd.Prefix))
} else {
logger.Info(fmt.Sprintf("%s command is disabled", cmd.Name))
logger.Info(fmt.Sprintf("Command %s: Disabled", cmd.Name))
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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