diff --git a/config.json b/config.json index fc5b2a644..a1951e791 100644 --- a/config.json +++ b/config.json @@ -170,6 +170,11 @@ "Enabled": true, "Description": "Toggle the Quest timer", "Prefix": "timer" + }, { + "Name": "Playtime", + "Enabled": true, + "Description": "Show your playtime", + "Prefix": "playtime" } ], "Courses": [ diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 17815dc30..e43ff3b1d 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -407,6 +407,13 @@ func parseChatCommand(s *Session, command string) { } else { sendDisabledCommandMessage(s, commands["Discord"]) } + case commands["Playtime"].Prefix: + if commands["Playtime"].Enabled || s.isOp() { + playtime := s.playtime + uint32(time.Now().Sub(s.playtimeTime).Seconds()) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.playtime, playtime/60/60, playtime/60%60, playtime%60)) + } else { + sendDisabledCommandMessage(s, commands["Playtime"]) + } case commands["Help"].Prefix: if commands["Help"].Enabled || s.isOp() { for _, command := range commands { diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 795fc05e1..8672b94a5 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -23,6 +23,7 @@ const ( pGalleryData // +1748 pToreData // +240 pGardenData // +68 + pPlaytime // +4 pWeaponType // +1 pWeaponID // +2 pHR // +2 @@ -45,6 +46,7 @@ type CharacterSaveData struct { GalleryData []byte ToreData []byte GardenData []byte + Playtime uint32 WeaponType uint8 WeaponID uint16 HR uint16 @@ -59,6 +61,7 @@ func getPointers() map[SavePointer]int { pointers := map[SavePointer]int{pGender: 81, lBookshelfData: 5576} switch _config.ErupeConfig.RealClientMode { case _config.ZZ: + pointers[pPlaytime] = 128356 pointers[pWeaponID] = 128522 pointers[pWeaponType] = 128789 pointers[pHouseTier] = 129900 @@ -74,6 +77,7 @@ func getPointers() map[SavePointer]int { case _config.Z2, _config.Z1, _config.G101, _config.G10, _config.G91, _config.G9, _config.G81, _config.G8, _config.G7, _config.G61, _config.G6, _config.G52, _config.G51, _config.G5, _config.GG, _config.G32, _config.G31, _config.G3, _config.G2, _config.G1: + pointers[pPlaytime] = 92356 pointers[pWeaponID] = 92522 pointers[pWeaponType] = 92789 pointers[pHouseTier] = 93900 @@ -87,6 +91,7 @@ func getPointers() map[SavePointer]int { pointers[pRP] = 106614 pointers[pKQF] = 110720 case _config.F5, _config.F4: + pointers[pPlaytime] = 60356 pointers[pWeaponID] = 60522 pointers[pWeaponType] = 60789 pointers[pHouseTier] = 61900 @@ -98,6 +103,7 @@ func getPointers() map[SavePointer]int { pointers[pGardenData] = 74424 pointers[pRP] = 74614 case _config.S6: + pointers[pPlaytime] = 12356 pointers[pWeaponID] = 12522 pointers[pWeaponType] = 12789 pointers[pHouseTier] = 13900 @@ -231,6 +237,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() { save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748] save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240] save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68] + save.Playtime = binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pPlaytime] : save.Pointers[pPlaytime]+4]) save.WeaponType = save.decompSave[save.Pointers[pWeaponType]] save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2]) save.HR = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHR] : save.Pointers[pHR]+2]) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index fd41e1366..0d41c42ca 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -54,6 +54,9 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { } characterSaveData.updateStructWithSaveData() + s.playtime = characterSaveData.Playtime + s.playtimeTime = time.Now() + // Bypass name-checker if new if characterSaveData.IsNewCharacter == true { s.Name = characterSaveData.Name diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index aae8706bb..6f24b6f32 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -10,6 +10,7 @@ type i18n struct { noOp string disabled string reload string + playtime string kqf struct { get string set struct { @@ -175,6 +176,8 @@ func getLangStrings(s *Server) i18n { i.commands.noOp = "You don't have permission to use this command" i.commands.disabled = "%s command is disabled" i.commands.reload = "Reloading players..." + i.commands.playtime = "Playtime: %d hours %d minutes %d seconds" + i.commands.kqf.get = "KQF: %x" i.commands.kqf.set.error = "Error in command. Format: %s set xxxxxxxxxxxxxxxx" i.commands.kqf.set.success = "KQF set, please switch Land/World" diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 4c05d2c5f..a13d0d4ce 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -50,6 +50,9 @@ type Session struct { kqf []byte kqfOverride bool + playtime uint32 + playtimeTime time.Time + semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. semaphoreMode bool semaphoreID []uint16