From 48375acd7d6581b42b354618ed694a9d1cd25439 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Sat, 18 Oct 2025 20:37:00 -0300 Subject: overall improvements + keep up with querymanager v0.2 --- .gitignore | 1 + common.go | 67 +++++++++++++++++++++++++++++++++++++--- config.cfg | 23 -------------- config.cfg.dist | 23 ++++++++++++++ main.go | 2 +- query.go | 16 ++++++---- templates.go | 8 ++++- templates/character_create.tmpl | 2 +- templates/character_profile.tmpl | 2 +- templates/world_info.tmpl | 17 ++++++++-- templates/world_list.tmpl | 6 ++++ 11 files changed, 127 insertions(+), 40 deletions(-) delete mode 100644 config.cfg create mode 100644 config.cfg.dist diff --git a/.gitignore b/.gitignore index d680a30..353c4bd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bin build local *.log +config.cfg diff --git a/common.go b/common.go index 0676487..0430dcf 100644 --- a/common.go +++ b/common.go @@ -9,6 +9,7 @@ import ( "strings" "time" "unicode" + "unicode/utf8" ) // TReadBuffer @@ -83,7 +84,11 @@ func (ReadBuffer *TReadBuffer) ReadString() string { Result := "" if ReadBuffer.CanRead(Length) { - Result = string(ReadBuffer.Buffer[ReadBuffer.Position:][:Length]) + // IMPORTANT(fusion): The game server uses LATIN1 encoding, which forces + // the query manager to use LATIN1 encoding for text, at least on the + // protocol level. + Input := ReadBuffer.Buffer[ReadBuffer.Position:][:Length] + Result = string(Latin1ToUTF8(Input)) } ReadBuffer.Position += Length return Result @@ -157,7 +162,11 @@ func (WriteBuffer *TWriteBuffer) Write32BE(Value uint32) { } func (WriteBuffer *TWriteBuffer) WriteString(String string) { - Length := len(String) + // IMPORTANT(fusion): The game server uses LATIN1 encoding, which forces + // the query manager to use LATIN1 encoding for text, at least on the + // protocol level. + Output := UTF8ToLatin1([]byte(String)) + Length := len(Output) if Length < 0xFFFF { WriteBuffer.Write16(uint16(Length)) } else { @@ -166,7 +175,7 @@ func (WriteBuffer *TWriteBuffer) WriteString(String string) { } if WriteBuffer.CanWrite(Length) { - copy(WriteBuffer.Buffer[WriteBuffer.Position:], String) + copy(WriteBuffer.Buffer[WriteBuffer.Position:], Output) } WriteBuffer.Position += Length } @@ -370,7 +379,7 @@ func WorldTypeString(Type int) string { } } -func TimestampString(Timestamp int) string { +func FormatTimestamp(Timestamp int) string { String := "Never" if Timestamp > 0 { Time := time.Unix(int64(Timestamp), 0) @@ -378,3 +387,53 @@ func TimestampString(Timestamp int) string { } return String } + +func FormatDurationSince(Timestamp int) string { + String := "N/A" + if Timestamp > 0{ + Duration := time.Since(time.Unix(int64(Timestamp), 0)) + String = Duration.Truncate(time.Second).String() + } + return String +} + +func UTF8FindNextLeadingByte(Buffer []byte) int { + Offset := 0 + for Offset < len(Buffer) { + // NOTE(fusion): Allow the first byte to be a leading byte, in case we + // just want to advance from one leading byte to another. + if(Offset > 0 && utf8.RuneStart(Buffer[Offset])){ + break + } + Offset += 1 + } + return Offset +} + +func UTF8ToLatin1(Buffer []byte) []byte { + ReadPos := 0 + Result := []byte{} + for ReadPos < len(Buffer) { + Codepoint, Size := utf8.DecodeRune(Buffer[ReadPos:]) + if Codepoint != utf8.RuneError { + ReadPos += Size + }else{ + ReadPos += UTF8FindNextLeadingByte(Buffer[ReadPos:]) + } + + if Codepoint >= 0 && Codepoint <= 0xFF { + Result = append(Result, byte(Codepoint)) + }else{ + Result = append(Result, '?') + } + } + return Result +} + +func Latin1ToUTF8(Buffer []byte) []byte { + Result := []byte{} + for ReadPos := range Buffer { + Result = utf8.AppendRune(Result, rune(Buffer[ReadPos])) + } + return Result +} diff --git a/config.cfg b/config.cfg deleted file mode 100644 index 81fd054..0000000 --- a/config.cfg +++ /dev/null @@ -1,23 +0,0 @@ -# HTTP/HTTPS Config -HttpPort = 80 -HttpsPort = 443 -HttpsCertFile = "https/cert.pem" -HttpsKeyFile = "https/key.pem" - -# SMTP Config -SmtpHost = "smtp.domain.com" -SmtpPort = 587 -SmtpUser = "username" -SmtpPassword = "" -SmtpSender = "support@domain.com" - -# Query Manager Config -QueryManagerHost = "127.0.0.1" -QueryManagerPort = 7173 -QueryManagerPassword = "a6glaf0c" - -# Query Manager Cache Config -MaxCachedAccounts = 4096 -MaxCachedCharacters = 4096 -CharacterRefreshInterval = 15m -WorldRefreshInterval = 15m diff --git a/config.cfg.dist b/config.cfg.dist new file mode 100644 index 0000000..81fd054 --- /dev/null +++ b/config.cfg.dist @@ -0,0 +1,23 @@ +# HTTP/HTTPS Config +HttpPort = 80 +HttpsPort = 443 +HttpsCertFile = "https/cert.pem" +HttpsKeyFile = "https/key.pem" + +# SMTP Config +SmtpHost = "smtp.domain.com" +SmtpPort = 587 +SmtpUser = "username" +SmtpPassword = "" +SmtpSender = "support@domain.com" + +# Query Manager Config +QueryManagerHost = "127.0.0.1" +QueryManagerPort = 7173 +QueryManagerPassword = "a6glaf0c" + +# Query Manager Cache Config +MaxCachedAccounts = 4096 +MaxCachedCharacters = 4096 +CharacterRefreshInterval = 15m +WorldRefreshInterval = 15m diff --git a/main.go b/main.go index 679096f..e2d16ad 100644 --- a/main.go +++ b/main.go @@ -563,7 +563,7 @@ func HandleWorld(Context *THttpRequestContext) { } func main() { - g_Log.Print("Tibia Web Server v0.1") + g_Log.Print("Tibia Web Server v0.2") if !ReadConfig("config.cfg", WebKVCallback) { return } diff --git a/query.go b/query.go index 907e77f..3abca9a 100644 --- a/query.go +++ b/query.go @@ -45,8 +45,10 @@ type ( Type string NumPlayers int MaxPlayers int - OnlineRecord int - OnlineRecordTime string + OnlinePeak int + OnlinePeakTimestamp int + LastStartup int + LastShutdown int } TAccountSummary struct { @@ -77,7 +79,7 @@ type ( Level int Profession string Residence string - LastLogin string + LastLogin int PremiumDays int Online bool Deleted bool @@ -389,7 +391,7 @@ func (Connection *TQueryManagerConnection) GetCharacterProfile(CharacterName str Character.Level = int(ReadBuffer.Read16()) Character.Profession = ReadBuffer.ReadString() Character.Residence = ReadBuffer.ReadString() - Character.LastLogin = TimestampString(int(ReadBuffer.Read32())) + Character.LastLogin = int(ReadBuffer.Read32()) Character.PremiumDays = int(ReadBuffer.Read16()) Character.Online = ReadBuffer.ReadFlag() Character.Deleted = ReadBuffer.ReadFlag() @@ -422,8 +424,10 @@ func (Connection *TQueryManagerConnection) GetWorlds() (Result int, Worlds []TWo Worlds[Index].Type = WorldTypeString(int(ReadBuffer.Read8())) Worlds[Index].NumPlayers = int(ReadBuffer.Read16()) Worlds[Index].MaxPlayers = int(ReadBuffer.Read16()) - Worlds[Index].OnlineRecord = int(ReadBuffer.Read16()) - Worlds[Index].OnlineRecordTime = TimestampString(int(ReadBuffer.Read32())) + Worlds[Index].OnlinePeak = int(ReadBuffer.Read16()) + Worlds[Index].OnlinePeakTimestamp = int(ReadBuffer.Read32()) + Worlds[Index].LastStartup = int(ReadBuffer.Read32()) + Worlds[Index].LastShutdown = int(ReadBuffer.Read32()) } } default: diff --git a/templates.go b/templates.go index 1f47782..5281c14 100644 --- a/templates.go +++ b/templates.go @@ -58,7 +58,13 @@ var ( func InitTemplates() bool { var Err error - g_Templates, Err = template.ParseGlob("templates/*.tmpl") + + CustomFuncs := template.FuncMap{ + "FormatTimestamp": FormatTimestamp, + "FormatDurationSince": FormatDurationSince, + } + + g_Templates, Err = template.New("").Funcs(CustomFuncs).ParseGlob("templates/*.tmpl") if Err != nil { g_LogErr.Printf("Failed to parse templates: %v", Err) return false diff --git a/templates/character_create.tmpl b/templates/character_create.tmpl index c243f2d..1a235dd 100644 --- a/templates/character_create.tmpl +++ b/templates/character_create.tmpl @@ -1,6 +1,6 @@ {{template "_header.tmpl" .Common}}
-

Create Account

+

Create Character

diff --git a/templates/character_profile.tmpl b/templates/character_profile.tmpl index 824e908..2711e03 100644 --- a/templates/character_profile.tmpl +++ b/templates/character_profile.tmpl @@ -35,7 +35,7 @@ Last Login: - {{.LastLogin}} + {{FormatTimestamp .LastLogin}} Account Status: diff --git a/templates/world_info.tmpl b/templates/world_info.tmpl index 578a0f7..353b674 100644 --- a/templates/world_info.tmpl +++ b/templates/world_info.tmpl @@ -16,13 +16,24 @@ {{.NumPlayers}} - Online Record: - {{if .OnlineRecord}} - {{.OnlineRecord}} players (on {{.OnlineRecordTime}}) + Online Peak: + {{if eq .OnlinePeak 1}} + {{.OnlinePeak}} player (on {{FormatTimestamp .OnlinePeakTimestamp}}) + {{else if gt .OnlinePeak 1}} + {{.OnlinePeak}} players (on {{FormatTimestamp .OnlinePeakTimestamp}}) {{else}} None {{end}} + + {{if gt .LastStartup .LastShutdown}} + Uptime: + {{FormatDurationSince .LastStartup}} + {{else}} + Downtime: + {{FormatDurationSince .LastShutdown}} + {{end}} + Kill Statistics {{else}} diff --git a/templates/world_list.tmpl b/templates/world_list.tmpl index de9786c..07494b6 100644 --- a/templates/world_list.tmpl +++ b/templates/world_list.tmpl @@ -7,12 +7,18 @@ Name Type Players Online + Status {{range .Worlds}} {{.Name}} {{.Type}} {{.NumPlayers}} + {{if gt .LastStartup .LastShutdown}} + Online + {{else}} + Offline + {{end}} {{end}} -- cgit v1.2.3