web/common.go
2025-08-01 19:48:26 -03:00

381 lines
8.6 KiB
Go

package main
import (
"bufio"
"encoding/binary"
"net"
"os"
"strconv"
"strings"
"time"
"unicode"
)
// TReadBuffer
// ==============================================================================
type TReadBuffer struct {
Buffer []byte
Position int
}
func (ReadBuffer *TReadBuffer) CanRead(Bytes int) bool {
return ReadBuffer.Position+Bytes <= len(ReadBuffer.Buffer)
}
func (ReadBuffer *TReadBuffer) Overflowed() bool {
return ReadBuffer.Position > len(ReadBuffer.Buffer)
}
func (ReadBuffer *TReadBuffer) ReadFlag() bool {
return ReadBuffer.Read8() != 0
}
func (ReadBuffer *TReadBuffer) Read8() uint8 {
Result := uint8(0)
if ReadBuffer.CanRead(1) {
Result = ReadBuffer.Buffer[ReadBuffer.Position]
}
ReadBuffer.Position += 1
return Result
}
func (ReadBuffer *TReadBuffer) Read16() uint16 {
Result := uint16(0)
if ReadBuffer.CanRead(2) {
Result = binary.LittleEndian.Uint16(ReadBuffer.Buffer[ReadBuffer.Position:])
}
ReadBuffer.Position += 2
return Result
}
func (ReadBuffer *TReadBuffer) Read16BE() uint16 {
Result := uint16(0)
if ReadBuffer.CanRead(2) {
Result = binary.BigEndian.Uint16(ReadBuffer.Buffer[ReadBuffer.Position:])
}
ReadBuffer.Position += 2
return Result
}
func (ReadBuffer *TReadBuffer) Read32() uint32 {
Result := uint32(0)
if ReadBuffer.CanRead(4) {
Result = binary.LittleEndian.Uint32(ReadBuffer.Buffer[ReadBuffer.Position:])
}
ReadBuffer.Position += 4
return Result
}
func (ReadBuffer *TReadBuffer) Read32BE() uint32 {
Result := uint32(0)
if ReadBuffer.CanRead(4) {
Result = binary.BigEndian.Uint32(ReadBuffer.Buffer[ReadBuffer.Position:])
}
ReadBuffer.Position += 4
return Result
}
func (ReadBuffer *TReadBuffer) ReadString() string {
Length := int(ReadBuffer.Read16())
if Length == 0xFFFF {
Length = int(ReadBuffer.Read32())
}
Result := ""
if ReadBuffer.CanRead(Length) {
Result = string(ReadBuffer.Buffer[ReadBuffer.Position:][:Length])
}
ReadBuffer.Position += Length
return Result
}
func (ReadBuffer *TReadBuffer) ReadBytes(Count int) []byte {
Result := []byte(nil)
if ReadBuffer.CanRead(Count) {
Result = ReadBuffer.Buffer[ReadBuffer.Position:][:Count]
}
ReadBuffer.Position += Count
return Result
}
// TWriteBuffer
// ==============================================================================
type TWriteBuffer struct {
Buffer []byte
Position int
}
func (WriteBuffer *TWriteBuffer) CanWrite(Bytes int) bool {
return (WriteBuffer.Position + Bytes) <= len(WriteBuffer.Buffer)
}
func (WriteBuffer *TWriteBuffer) Overflowed() bool {
return WriteBuffer.Position > len(WriteBuffer.Buffer)
}
func (WriteBuffer *TWriteBuffer) WriteFlag(Value bool) {
Value8 := uint8(0)
if Value {
Value8 = uint8(1)
}
WriteBuffer.Write8(Value8)
}
func (WriteBuffer *TWriteBuffer) Write8(Value uint8) {
if WriteBuffer.CanWrite(1) {
WriteBuffer.Buffer[WriteBuffer.Position] = Value
}
WriteBuffer.Position += 1
}
func (WriteBuffer *TWriteBuffer) Write16(Value uint16) {
if WriteBuffer.CanWrite(2) {
binary.LittleEndian.PutUint16(WriteBuffer.Buffer[WriteBuffer.Position:], Value)
}
WriteBuffer.Position += 2
}
func (WriteBuffer *TWriteBuffer) Write16BE(Value uint16) {
if WriteBuffer.CanWrite(2) {
binary.BigEndian.PutUint16(WriteBuffer.Buffer[WriteBuffer.Position:], Value)
}
WriteBuffer.Position += 2
}
func (WriteBuffer *TWriteBuffer) Write32(Value uint32) {
if WriteBuffer.CanWrite(4) {
binary.LittleEndian.PutUint32(WriteBuffer.Buffer[WriteBuffer.Position:], Value)
}
WriteBuffer.Position += 4
}
func (WriteBuffer *TWriteBuffer) Write32BE(Value uint32) {
if WriteBuffer.CanWrite(4) {
binary.BigEndian.PutUint32(WriteBuffer.Buffer[WriteBuffer.Position:], Value)
}
WriteBuffer.Position += 4
}
func (WriteBuffer *TWriteBuffer) WriteString(String string) {
Length := len(String)
if Length < 0xFFFF {
WriteBuffer.Write16(uint16(Length))
} else {
WriteBuffer.Write16(0xFFFF)
WriteBuffer.Write32(uint32(Length))
}
if WriteBuffer.CanWrite(Length) {
copy(WriteBuffer.Buffer[WriteBuffer.Position:], String)
}
WriteBuffer.Position += Length
}
func (WriteBuffer *TWriteBuffer) WriteBytes(Bytes []byte) {
Count := len(Bytes)
if WriteBuffer.CanWrite(Count) {
copy(WriteBuffer.Buffer[WriteBuffer.Position:], Bytes)
}
WriteBuffer.Position += Count
}
func (WriteBuffer *TWriteBuffer) Rewrite16(Position int, Value uint16) {
if (Position+2) <= WriteBuffer.Position && !WriteBuffer.Overflowed() {
binary.LittleEndian.PutUint16(WriteBuffer.Buffer[Position:], Value)
}
}
func (WriteBuffer *TWriteBuffer) Insert32(Position int, Value uint32) {
if Position <= WriteBuffer.Position {
if WriteBuffer.CanWrite(4) {
copy(WriteBuffer.Buffer[Position+4:], WriteBuffer.Buffer[Position:])
binary.LittleEndian.PutUint32(WriteBuffer.Buffer[Position:], Value)
}
WriteBuffer.Position += 4
}
}
// Config
// ==============================================================================
func ParseBoolean(String string) bool {
return strings.EqualFold(String, "true")
}
func ParseInteger(String string) int {
Result, Err := strconv.Atoi(String)
if Err != nil {
g_LogErr.Printf("Failed to parse integer \"%v\": %v", String, Err)
}
return Result
}
func ParseIntegerSuffix(String string) (int, string) {
SuffixStart := -1
for Index, Rune := range String {
if !unicode.IsDigit(Rune) {
SuffixStart = Index
break
}
}
var Value, Suffix string
if SuffixStart != -1 {
Value = strings.TrimSpace(String[:SuffixStart])
Suffix = strings.TrimSpace(String[SuffixStart:])
} else {
Value = String
Suffix = ""
}
Result, Err := strconv.Atoi(Value)
if Err != nil {
g_LogErr.Printf("Failed to parse duration \"%v\": %v", String, Err)
}
return Result, Suffix
}
func ParseDuration(String string) time.Duration {
Value, Suffix := ParseIntegerSuffix(String)
Result := time.Duration(Value) * time.Millisecond
if len(Suffix) > 0 {
switch Suffix[0] {
case 'S', 's':
Result = time.Duration(Value) * time.Second
case 'M', 'm':
Result = time.Duration(Value) * time.Minute
case 'H', 'h':
Result = time.Duration(Value) * time.Hour
}
}
return Result
}
func ParseSize(String string) int {
Result, Suffix := ParseIntegerSuffix(String)
if len(Suffix) > 0 {
switch Suffix[0] {
case 'K', 'k':
Result *= 1024
case 'M', 'm':
Result *= (1024 * 1024)
}
}
return Result
}
func ParseString(String string) string {
if len(String) > 2 {
if String[0] == '"' && String[len(String)-1] == '"' ||
String[0] == '\'' && String[len(String)-1] == '\'' ||
String[0] == '`' && String[len(String)-1] == '`' {
String = String[1 : len(String)-1]
}
}
return String
}
func ReadConfig(FileName string, KVCallback func(string, string)) bool {
File, Err := os.Open(FileName)
if Err != nil {
g_LogErr.Print(Err)
return false
}
defer File.Close()
Scanner := bufio.NewScanner(File)
for LineNumber := 1; Scanner.Scan(); LineNumber += 1 {
Line := strings.TrimSpace(Scanner.Text())
if len(Line) == 0 || Line[0] == '#' {
continue
}
Key, Value, Ok := strings.Cut(Scanner.Text(), "=")
if !Ok {
g_LogErr.Printf("%v:%v: No assignment found on non empty line", FileName, LineNumber)
continue
}
Key = strings.TrimSpace(Key)
if len(Key) == 0 {
g_LogErr.Printf("%v:%v: Empty key", FileName, LineNumber)
continue
}
Value = strings.TrimSpace(Value)
if len(Value) == 0 {
g_LogErr.Printf("%v:%v: Empty value", FileName, LineNumber)
continue
}
KVCallback(Key, Value)
}
return true
}
// Utility
// ==============================================================================
func FileExists(FileName string) bool {
_, Err := os.Stat(FileName)
return Err == nil
}
func JoinHostPort(Host string, Port int) string {
PortString := strconv.Itoa(Port)
return net.JoinHostPort(Host, PortString)
}
func SplitDiscardEmpty(String string, Sep string) []string {
var Result []string
for String != "" {
SubStr := ""
Index := strings.Index(String, Sep)
if Index == -1 {
SubStr = String
String = ""
} else {
SubStr = String[:Index]
String = String[Index+1:]
}
if SubStr != "" {
Result = append(Result, SubStr)
}
}
return Result
}
func SwapAndPop[T any](Slice []T, Index int) []T {
if len(Slice) == 0 {
panic("slice is empty")
}
var Zero T
Slice[Index] = Slice[len(Slice)-1]
Slice[len(Slice)-1] = Zero
return Slice[:len(Slice)-1]
}
func WorldTypeString(Type int) string {
switch Type {
case 0:
return "Normal"
case 1:
return "Non-PvP"
case 2:
return "PvP-Enforced"
default:
return "Unknown"
}
}
func TimestampString(Timestamp int) string {
String := "Never"
if Timestamp > 0 {
Time := time.Unix(int64(Timestamp), 0)
String = Time.Format("Jan 02 2006, 15:04:05 MST")
}
return String
}