overall improvements + keep up with querymanager v0.2
This commit is contained in:
parent
cd93a7c2bf
commit
48375acd7d
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ bin
|
|||||||
build
|
build
|
||||||
local
|
local
|
||||||
*.log
|
*.log
|
||||||
|
config.cfg
|
||||||
|
|||||||
67
common.go
67
common.go
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TReadBuffer
|
// TReadBuffer
|
||||||
@ -83,7 +84,11 @@ func (ReadBuffer *TReadBuffer) ReadString() string {
|
|||||||
|
|
||||||
Result := ""
|
Result := ""
|
||||||
if ReadBuffer.CanRead(Length) {
|
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
|
ReadBuffer.Position += Length
|
||||||
return Result
|
return Result
|
||||||
@ -157,7 +162,11 @@ func (WriteBuffer *TWriteBuffer) Write32BE(Value uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (WriteBuffer *TWriteBuffer) WriteString(String string) {
|
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 {
|
if Length < 0xFFFF {
|
||||||
WriteBuffer.Write16(uint16(Length))
|
WriteBuffer.Write16(uint16(Length))
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +175,7 @@ func (WriteBuffer *TWriteBuffer) WriteString(String string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if WriteBuffer.CanWrite(Length) {
|
if WriteBuffer.CanWrite(Length) {
|
||||||
copy(WriteBuffer.Buffer[WriteBuffer.Position:], String)
|
copy(WriteBuffer.Buffer[WriteBuffer.Position:], Output)
|
||||||
}
|
}
|
||||||
WriteBuffer.Position += Length
|
WriteBuffer.Position += Length
|
||||||
}
|
}
|
||||||
@ -370,7 +379,7 @@ func WorldTypeString(Type int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TimestampString(Timestamp int) string {
|
func FormatTimestamp(Timestamp int) string {
|
||||||
String := "Never"
|
String := "Never"
|
||||||
if Timestamp > 0 {
|
if Timestamp > 0 {
|
||||||
Time := time.Unix(int64(Timestamp), 0)
|
Time := time.Unix(int64(Timestamp), 0)
|
||||||
@ -378,3 +387,53 @@ func TimestampString(Timestamp int) string {
|
|||||||
}
|
}
|
||||||
return 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
|
||||||
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@ -563,7 +563,7 @@ func HandleWorld(Context *THttpRequestContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
g_Log.Print("Tibia Web Server v0.1")
|
g_Log.Print("Tibia Web Server v0.2")
|
||||||
if !ReadConfig("config.cfg", WebKVCallback) {
|
if !ReadConfig("config.cfg", WebKVCallback) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
16
query.go
16
query.go
@ -45,8 +45,10 @@ type (
|
|||||||
Type string
|
Type string
|
||||||
NumPlayers int
|
NumPlayers int
|
||||||
MaxPlayers int
|
MaxPlayers int
|
||||||
OnlineRecord int
|
OnlinePeak int
|
||||||
OnlineRecordTime string
|
OnlinePeakTimestamp int
|
||||||
|
LastStartup int
|
||||||
|
LastShutdown int
|
||||||
}
|
}
|
||||||
|
|
||||||
TAccountSummary struct {
|
TAccountSummary struct {
|
||||||
@ -77,7 +79,7 @@ type (
|
|||||||
Level int
|
Level int
|
||||||
Profession string
|
Profession string
|
||||||
Residence string
|
Residence string
|
||||||
LastLogin string
|
LastLogin int
|
||||||
PremiumDays int
|
PremiumDays int
|
||||||
Online bool
|
Online bool
|
||||||
Deleted bool
|
Deleted bool
|
||||||
@ -389,7 +391,7 @@ func (Connection *TQueryManagerConnection) GetCharacterProfile(CharacterName str
|
|||||||
Character.Level = int(ReadBuffer.Read16())
|
Character.Level = int(ReadBuffer.Read16())
|
||||||
Character.Profession = ReadBuffer.ReadString()
|
Character.Profession = ReadBuffer.ReadString()
|
||||||
Character.Residence = ReadBuffer.ReadString()
|
Character.Residence = ReadBuffer.ReadString()
|
||||||
Character.LastLogin = TimestampString(int(ReadBuffer.Read32()))
|
Character.LastLogin = int(ReadBuffer.Read32())
|
||||||
Character.PremiumDays = int(ReadBuffer.Read16())
|
Character.PremiumDays = int(ReadBuffer.Read16())
|
||||||
Character.Online = ReadBuffer.ReadFlag()
|
Character.Online = ReadBuffer.ReadFlag()
|
||||||
Character.Deleted = 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].Type = WorldTypeString(int(ReadBuffer.Read8()))
|
||||||
Worlds[Index].NumPlayers = int(ReadBuffer.Read16())
|
Worlds[Index].NumPlayers = int(ReadBuffer.Read16())
|
||||||
Worlds[Index].MaxPlayers = int(ReadBuffer.Read16())
|
Worlds[Index].MaxPlayers = int(ReadBuffer.Read16())
|
||||||
Worlds[Index].OnlineRecord = int(ReadBuffer.Read16())
|
Worlds[Index].OnlinePeak = int(ReadBuffer.Read16())
|
||||||
Worlds[Index].OnlineRecordTime = TimestampString(int(ReadBuffer.Read32()))
|
Worlds[Index].OnlinePeakTimestamp = int(ReadBuffer.Read32())
|
||||||
|
Worlds[Index].LastStartup = int(ReadBuffer.Read32())
|
||||||
|
Worlds[Index].LastShutdown = int(ReadBuffer.Read32())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -58,7 +58,13 @@ var (
|
|||||||
|
|
||||||
func InitTemplates() bool {
|
func InitTemplates() bool {
|
||||||
var Err error
|
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 {
|
if Err != nil {
|
||||||
g_LogErr.Printf("Failed to parse templates: %v", Err)
|
g_LogErr.Printf("Failed to parse templates: %v", Err)
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{{template "_header.tmpl" .Common}}
|
{{template "_header.tmpl" .Common}}
|
||||||
<form class="box" action="/character/create" method="POST">
|
<form class="box" action="/character/create" method="POST">
|
||||||
<h1>Create Account</h1>
|
<h1>Create Character</h1>
|
||||||
|
|
||||||
<label for="character_name">NAME</label>
|
<label for="character_name">NAME</label>
|
||||||
<input id="character_name" type="text" name="name"/>
|
<input id="character_name" type="text" name="name"/>
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Last Login:</th>
|
<th>Last Login:</th>
|
||||||
<td>{{.LastLogin}}</td>
|
<td>{{FormatTimestamp .LastLogin}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Account Status:</th>
|
<th>Account Status:</th>
|
||||||
|
|||||||
@ -16,13 +16,24 @@
|
|||||||
<td>{{.NumPlayers}}</td>
|
<td>{{.NumPlayers}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Online Record:</th>
|
<th>Online Peak:</th>
|
||||||
{{if .OnlineRecord}}
|
{{if eq .OnlinePeak 1}}
|
||||||
<td>{{.OnlineRecord}} players (on {{.OnlineRecordTime}})</td>
|
<td>{{.OnlinePeak}} player (on {{FormatTimestamp .OnlinePeakTimestamp}})</td>
|
||||||
|
{{else if gt .OnlinePeak 1}}
|
||||||
|
<td>{{.OnlinePeak}} players (on {{FormatTimestamp .OnlinePeakTimestamp}})</td>
|
||||||
{{else}}
|
{{else}}
|
||||||
<td>None</td>
|
<td>None</td>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
{{if gt .LastStartup .LastShutdown}}
|
||||||
|
<th>Uptime:</th>
|
||||||
|
<td>{{FormatDurationSince .LastStartup}}</td>
|
||||||
|
{{else}}
|
||||||
|
<th>Downtime:</th>
|
||||||
|
<td style="color: #A11;">{{FormatDurationSince .LastShutdown}}</td>
|
||||||
|
{{end}}
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<a class="button" href="/killstatistics?world={{.Name}}">Kill Statistics</a>
|
<a class="button" href="/killstatistics?world={{.Name}}">Kill Statistics</a>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|||||||
@ -7,12 +7,18 @@
|
|||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Players Online</th>
|
<th>Players Online</th>
|
||||||
|
<th>Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
{{range .Worlds}}
|
{{range .Worlds}}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/world?name={{.Name}}">{{.Name}}</a></td>
|
<td><a href="/world?name={{.Name}}">{{.Name}}</a></td>
|
||||||
<td>{{.Type}}</td>
|
<td>{{.Type}}</td>
|
||||||
<td>{{.NumPlayers}}</td>
|
<td>{{.NumPlayers}}</td>
|
||||||
|
{{if gt .LastStartup .LastShutdown}}
|
||||||
|
<td style="color: #1A1;">Online</td>
|
||||||
|
{{else}}
|
||||||
|
<td style="color: #A11;">Offline</td>
|
||||||
|
{{end}}
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user