package main import ( "fmt" rl "github.com/gen2brain/raylib-go/raylib" "log" "os" "path/filepath" "strconv" "strings" "sync" "time" ) type App struct { LogPath string API *HypixelAPI UUIDCache *UUIDCache CurrentDisplayTable DisplayTable } func NewApp(apiKey string) *App { path := os.Getenv("USERPROFILE") logPath := filepath.Join(path, ".lunarclient", "offline", "multiver", "logs", "latest.log") return &App{ LogPath: logPath, API: NewHypixelAPI(apiKey), UUIDCache: NewUUIDCache(120 * time.Minute), CurrentDisplayTable: &BedwarsDisplayTable{}, } } func (a *App) Start() { rl.SetConfigFlags(rl.FlagWindowTransparent | rl.FlagWindowUndecorated) screenWidth := 800 screenHeight := 600 rl.InitWindow(int32(screenWidth), int32(screenHeight), "Raylib in Go") defer rl.CloseWindow() windowPosition := rl.NewVector2(500, 200) rl.SetWindowPosition(int(int32(windowPosition.X)), int(int32(windowPosition.Y))) var panOffset rl.Vector2 var dragWindow bool = false const LowFps = 8 rl.SetTargetFPS(LowFps) lineCh := make(chan string) go tailFile(a.LogPath, lineCh) ch := make(chan HypixelPlayerResponse) var wg sync.WaitGroup go func() { wg.Wait() close(ch) }() sx := 10 sy := 10 for !rl.WindowShouldClose() { mousePosition := rl.GetMousePosition() windowPosition := rl.GetWindowPosition() screenMousePosition := rl.Vector2{ X: windowPosition.X + mousePosition.X, Y: windowPosition.Y + mousePosition.Y, } if rl.IsMouseButtonPressed(rl.MouseLeftButton) && !dragWindow { rl.SetTargetFPS(int32(rl.GetMonitorRefreshRate(0))) dragWindow = true panOffset.X = screenMousePosition.X - float32(windowPosition.X) panOffset.Y = screenMousePosition.Y - float32(windowPosition.Y) } if dragWindow { newWindowPositionX := int(screenMousePosition.X - panOffset.X) newWindowPositionY := int(screenMousePosition.Y - panOffset.Y) rl.SetWindowPosition(newWindowPositionX, newWindowPositionY) if rl.IsMouseButtonReleased(rl.MouseLeftButton) { dragWindow = false rl.SetTargetFPS(LowFps) } } select { case line := <-lineCh: a.onFileEmit(replaceCorruptedRune(line)) default: } rl.BeginDrawing() rl.ClearBackground(rl.Color{0, 0, 0, 100}) yOffset := sy for _, player := range a.CurrentDisplayTable.GetPlayers() { playerName := player.DisplayName bedwarsLevel := player.Achievements.BedwarsStar bedwarsWins := player.Stats.Bedwars.Wins rl.DrawText(fmt.Sprintf("Player: %s", playerName), int32(sx), int32(yOffset), 20, rl.Black) yOffset += 25 rl.DrawText(fmt.Sprintf("Bedwars Level: %d", bedwarsLevel), int32(sx), int32(yOffset), 18, rl.DarkGray) yOffset += 25 rl.DrawText(fmt.Sprintf("Bedwars Wins: %d", bedwarsWins), int32(sx), int32(yOffset), 18, rl.DarkGray) yOffset += 40 } rl.EndDrawing() } } func (a *App) onFileEmit(line string) { msg := strings.TrimSpace(line) if len(msg) < 34 { return } submsg := msg[33:] if len(submsg) != 0 { LogBuf.Add(submsg) } OnlinePrefix := "[CHAT] ONLINE: " PartyListSeparatorLinePrefix := "[CHAT] -----------------------------------------------------" PartyMemberCountPrefix := "[CHAT] Party Members (" PartyLeaderPrefix := "[CHAT] Party Leader: " PartyListMembersPrefix := "[CHAT] Party Members: " if strings.HasPrefix(submsg, OnlinePrefix) { // Online Message newsubmsg := strings.TrimPrefix(submsg, OnlinePrefix) players := strings.Split(newsubmsg, ",") for _, player := range players { playerName := strings.TrimSpace(player) playerUUID, err := FetchMCPlayer(playerName) if err != nil { log.Fatalf("Error fetching UUID: %v", err) return } fmt.Printf("UUID of player %s: %s\n", playerName, playerUUID) cachedPlayer := CachedUuid{playerUUID.UUID, playerName, playerName, time.Now()} a.UUIDCache.Add(&cachedPlayer) //names, err := GetNameFromUUID(playerUUID) //if err != nil { // log.Fatalf("Error fetching names from UUID: %v", err) //} //fmt.Printf("Name history for UUID %s: %v\n", playerUUID, names) } } else if strings.HasPrefix(submsg, PartyListSeparatorLinePrefix) { // Party List last, _ := LogBuf.GetSecondToLast() // TODO: Check if moderators if !strings.HasPrefix(last, PartyListMembersPrefix) { return } PartyMembersMsg := strings.TrimPrefix(last, PartyListMembersPrefix) for _, player := range strings.Split(PartyMembersMsg, ",") { playerName := strings.TrimSpace(strings.TrimSuffix(player, " ?")) if strings.HasPrefix(playerName, "[") { playerName = strings.Split(playerName, " ")[1] } print("Found Player: '") print(playerName) print("'\n") a.CurrentDisplayTable.AddPlayerN(playerName, a) //playerName := strings.TrimSpace(player) //playerUUID, err := GetUUIDFromName(playerName) //if err != nil { // log.Fatalf("Error fetching UUID: %v", err) // return //} //fmt.Printf("UUID of player %s: %s\n", playerName, playerUUID) //cachedPlayer := CachedUuid{playerUUID, playerName, playerName, time.Now()} //UuidCache.Add(&cachedPlayer) } // Parse Party Leader leaders_msg, err := LogBuf.GetLineStepsBack(2) if err != nil { println("Unable to find party leader message") return } PartyLeaderMsg := strings.TrimPrefix(leaders_msg, PartyLeaderPrefix) playerName := strings.TrimSpace(strings.TrimSuffix(PartyLeaderMsg, " ?")) if strings.HasPrefix(playerName, "[") { playerName = strings.Split(playerName, " ")[1] } print("Found Leader: '") print(playerName) print("'\n") a.CurrentDisplayTable.AddPlayerN(playerName, a) // Parse Party Count party_count_msg, err := LogBuf.GetLineStepsBack(4) if err != nil { println("Unable to find party count message") return } PartyCountMsg := strings.TrimPrefix(party_count_msg, PartyMemberCountPrefix) count_str := strings.TrimSuffix(PartyCountMsg, ")") count, err := strconv.Atoi(count_str) if err != nil { println("Unable to parse party count message - Invalid number used") return } print("Expected ") print(count) print(" party members\n") } println(submsg) }