From d84651a899a6104fce73d95b9c571627c55e5d16 Mon Sep 17 00:00:00 2001 From: fusion32 Date: Fri, 1 Aug 2025 19:48:26 -0300 Subject: bare bones website --- common.go | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 common.go (limited to 'common.go') diff --git a/common.go b/common.go new file mode 100644 index 0000000..0676487 --- /dev/null +++ b/common.go @@ -0,0 +1,380 @@ +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 +} -- cgit v1.2.3