Compare commits
No commits in common. "e0380e44c3dafaae37fbe920fc6374e99baa4e1d" and "65caea328b48ba9c53b321b3032ea941f18d4c98" have entirely different histories.
e0380e44c3
...
65caea328b
107
app/app.go
Normal file
107
app/app.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/haleyrom/skia-go"
|
||||||
|
"image/color"
|
||||||
|
"log"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/go-gl/gl/v3.3-core/gl"
|
||||||
|
"github.com/go-gl/glfw/v3.3/glfw"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// GLFW event handling must run on the main OS thread
|
||||||
|
runtime.LockOSThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Initialize GLFW
|
||||||
|
if err := glfw.Init(); err != nil {
|
||||||
|
log.Fatalf("failed to initialize GLFW: %v", err)
|
||||||
|
}
|
||||||
|
defer glfw.Terminate()
|
||||||
|
|
||||||
|
// Create GLFW window
|
||||||
|
glfw.WindowHint(glfw.ContextVersionMajor, 3)
|
||||||
|
glfw.WindowHint(glfw.ContextVersionMinor, 3)
|
||||||
|
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
||||||
|
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
||||||
|
|
||||||
|
window, err := glfw.CreateWindow(800, 600, "GLFW + Skia Demo", nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to create GLFW window: %v", err)
|
||||||
|
}
|
||||||
|
window.MakeContextCurrent()
|
||||||
|
|
||||||
|
// Initialize OpenGL
|
||||||
|
if err := gl.Init(); err != nil {
|
||||||
|
log.Fatalf("failed to initialize OpenGL: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up Skia surface
|
||||||
|
glInterface := skia.NewNativeGrGlinterface()
|
||||||
|
if glInterface == nil {
|
||||||
|
log.Fatalf("failed to create Skia OpenGL interface")
|
||||||
|
}
|
||||||
|
grContext := skia.NewGLGrContext(glInterface)
|
||||||
|
if grContext == nil {
|
||||||
|
log.Fatalf("failed to create Skia GrContext")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get framebuffer info
|
||||||
|
var framebufferInfo skia.GrGlFramebufferinfo
|
||||||
|
var fboID int32
|
||||||
|
gl.GetIntegerv(gl.FRAMEBUFFER_BINDING, &fboID)
|
||||||
|
framebufferInfo.FFBOID = uint32(fboID)
|
||||||
|
framebufferInfo.FFormat = gl.RGBA8
|
||||||
|
|
||||||
|
width, height := window.GetSize()
|
||||||
|
renderTarget := skia.NewGlGrBackendrendertarget(int32(width), int32(height), 1, 8, &framebufferInfo)
|
||||||
|
if renderTarget == nil {
|
||||||
|
log.Fatalf("failed to create Skia render target")
|
||||||
|
}
|
||||||
|
|
||||||
|
surface := skia.NewSurfaceBackendRenderTarget(grContext, renderTarget, skia.GR_SURFACE_ORIGIN_BOTTOM_LEFT, skia.SK_COLORTYPE_RGBA_8888, nil, nil)
|
||||||
|
if surface == nil {
|
||||||
|
log.Fatalf("failed to create Skia surface")
|
||||||
|
}
|
||||||
|
defer surface.Unref()
|
||||||
|
|
||||||
|
c := color.RGBA{R: 255, G: 100, B: 50, A: 255}
|
||||||
|
f := skia.NewColor4f(c)
|
||||||
|
colorWhite := f.ToColor()
|
||||||
|
|
||||||
|
for !window.ShouldClose() {
|
||||||
|
gl.ClearColor(0.3, 0.3, 0.3, 1.0)
|
||||||
|
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
|
||||||
|
|
||||||
|
canvas := surface.GetCanvas()
|
||||||
|
|
||||||
|
paint := skia.NewPaint()
|
||||||
|
paint.SetColor(0xffff0000)
|
||||||
|
|
||||||
|
// Clear the canvas with white color
|
||||||
|
canvas.Clear(colorWhite)
|
||||||
|
|
||||||
|
// Create a rectangle and draw it
|
||||||
|
rect := skia.Rect{
|
||||||
|
Left: 100,
|
||||||
|
Top: 100,
|
||||||
|
Right: 300,
|
||||||
|
Bottom: 300,
|
||||||
|
}
|
||||||
|
canvas.DrawRect(&rect, paint)
|
||||||
|
grContext.Flush()
|
||||||
|
|
||||||
|
// Swap OpenGL buffers
|
||||||
|
window.SwapBuffers()
|
||||||
|
|
||||||
|
// Poll for GLFW events
|
||||||
|
glfw.PollEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
surface.Unref()
|
||||||
|
grContext.Unref()
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
var key = "ccebff0f-939a-4afe-b5b3-30a7a665ee38"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var demoApp = NewDemoApp(key)
|
|
||||||
demoApp.Start()
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type LogBuffer struct {
|
|
||||||
strings []string
|
|
||||||
size int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogBuffer(size int) *LogBuffer {
|
|
||||||
return &LogBuffer{
|
|
||||||
strings: make([]string, 0, size),
|
|
||||||
size: size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogBuffer) Add(s string) {
|
|
||||||
if len(l.strings) == l.size {
|
|
||||||
l.strings = l.strings[1:]
|
|
||||||
}
|
|
||||||
l.strings = append(l.strings, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogBuffer) Get() []string {
|
|
||||||
return l.strings
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogBuffer) GetLast() (string, error) {
|
|
||||||
if len(l.strings) == 0 {
|
|
||||||
return "", fmt.Errorf("log buffer is empty")
|
|
||||||
}
|
|
||||||
return l.strings[len(l.strings)-1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogBuffer) GetSecondToLast() (string, error) {
|
|
||||||
if len(l.strings) < 2 {
|
|
||||||
return "", fmt.Errorf("log buffer does not have enough lines")
|
|
||||||
}
|
|
||||||
return l.strings[len(l.strings)-2], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LogBuffer) GetLineStepsBack(x int) (string, error) {
|
|
||||||
if x < 0 || x >= len(l.strings) {
|
|
||||||
return "", fmt.Errorf("log buffer does not have enough lines to step back %d times", x)
|
|
||||||
}
|
|
||||||
return l.strings[len(l.strings)-1-x], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
//var LogBuf = NewLogBuffer(10)
|
|
@ -52,7 +52,7 @@ func (c *CustomLogger) Error(msg string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Example usage of the custom logger
|
// Example usage of the custom logger
|
||||||
func showcase() {
|
func main() {
|
||||||
logger := NewCustomLogger()
|
logger := NewCustomLogger()
|
||||||
|
|
||||||
logger.Info("This is an info message")
|
logger.Info("This is an info message")
|
||||||
|
495
app/main.go
495
app/main.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -10,234 +9,171 @@ import (
|
|||||||
"hudly/mcfetch"
|
"hudly/mcfetch"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PlayerWrapper struct {
|
var key = "ccebff0f-939a-4afe-b5b3-30a7a665ee38"
|
||||||
Player hypixel.Player `json:"player"`
|
var UUID = "5328930e-d411-49cb-90ad-4e5c7b27dd86"
|
||||||
|
|
||||||
|
func demo() {
|
||||||
|
// Ensure a username is provided as a command-line argument
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
log.Fatal("Please provide a Minecraft username as a command-line argument.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceCorruptedRune(msg string) string {
|
// Get the username from the command-line arguments
|
||||||
runes := []rune(msg)
|
username := os.Args[1]
|
||||||
for i, r := range runes {
|
|
||||||
if r == '<27>' {
|
|
||||||
runes[i] = '§'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string(runes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearTerminal() {
|
thing := hypixel.NewAPIKey(key)
|
||||||
var cmd *exec.Cmd
|
api := hypixel.NewAPI(*thing)
|
||||||
if runtime.GOOS == "windows" {
|
thing.UsesLeft = 11
|
||||||
cmd = exec.Command("cmd", "/c", "cls")
|
|
||||||
|
// Create a MemoryCache instance
|
||||||
|
memCache := &mcfetch.MemoryCache{}
|
||||||
|
memCache.Init()
|
||||||
|
|
||||||
|
// Create a channel to receive the result
|
||||||
|
resultChan := make(chan map[string]interface{})
|
||||||
|
errorChan := make(chan error)
|
||||||
|
|
||||||
|
// Create an AsyncPlayerFetcher for asynchronous data fetching with MemoryCache
|
||||||
|
asyncFetcher := mcfetch.NewAsyncPlayerFetcher(
|
||||||
|
username, // Minecraft username or UUID
|
||||||
|
memCache, // Pass the memory cache instance
|
||||||
|
2, // Number of retries
|
||||||
|
2*time.Second, // Retry delay
|
||||||
|
5*time.Second, // Request timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
// Start asynchronous data fetching
|
||||||
|
asyncFetcher.FetchPlayerData(resultChan, errorChan)
|
||||||
|
|
||||||
|
// Non-blocking code execution (do something else while waiting)
|
||||||
|
fmt.Println("Fetching data asynchronously...")
|
||||||
|
|
||||||
|
var userID string
|
||||||
|
// Block until we receive data or an error
|
||||||
|
select {
|
||||||
|
case data := <-resultChan:
|
||||||
|
fmt.Printf("Player data: %+v\n", data)
|
||||||
|
|
||||||
|
// Check if "uuid" exists and is not nil
|
||||||
|
if uuid, ok := data["id"].(string); ok {
|
||||||
|
userID = uuid
|
||||||
} else {
|
} else {
|
||||||
cmd = exec.Command("clear")
|
fmt.Println(fmt.Sprintf("%+v", data))
|
||||||
}
|
log.Fatal("UUID not found or invalid for player")
|
||||||
cmd.Stdout = os.Stdout
|
|
||||||
cmd.Run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateRatio is a helper function to calculate ratios (e.g., WLR, KDR, etc.)
|
case err := <-errorChan:
|
||||||
func calculateRatio(numerator, denominator int) float64 {
|
log.Fatal(err)
|
||||||
if denominator == 0 {
|
|
||||||
return float64(numerator)
|
|
||||||
}
|
|
||||||
return float64(numerator) / float64(denominator)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DemoApp struct {
|
// Use the Hypixel API to get additional player data
|
||||||
Client *netclient.Client
|
res, err := api.GetPlayerResponse(userID)
|
||||||
API *hypixel.HypixelApi
|
if err != nil {
|
||||||
MemCache *mcfetch.MemoryCache
|
panic(err)
|
||||||
LogBuf *LogBuffer
|
}
|
||||||
PartyBuilder []map[string]interface{}
|
fmt.Println(fmt.Sprintf("%+v", res))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDemoApp(key string) *DemoApp {
|
func main() {
|
||||||
var api_key = hypixel.NewAPIKey(key)
|
var cmd string
|
||||||
app := &DemoApp{
|
fmt.Print(" | CREATE\n | JOIN\nEnter Choice:\n>")
|
||||||
API: hypixel.NewAPI(*api_key),
|
fmt.Scanln(&cmd)
|
||||||
MemCache: &mcfetch.MemoryCache{},
|
|
||||||
LogBuf: NewLogBuffer(10),
|
if cmd != "CREATE" && cmd != "JOIN" {
|
||||||
PartyBuilder: []map[string]interface{}{},
|
fmt.Println("Invalid command.")
|
||||||
}
|
return
|
||||||
app.MemCache.Init()
|
|
||||||
return app
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *DemoApp) FetchMCPlayer(name string) (*mcfetch.FetchedPlayerResult, error) {
|
client, err := netclient.NewClient("0.0.0.0", uuid.New().String())
|
||||||
asyncFetcher := mcfetch.NewPlayerFetcher(
|
if err != nil {
|
||||||
name,
|
log.Fatalf("Failed to create client: %v", err)
|
||||||
app.MemCache,
|
return
|
||||||
|
}
|
||||||
|
var code string
|
||||||
|
if cmd == "CREATE" {
|
||||||
|
var err error
|
||||||
|
code, err = client.CreateRoom("")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Created room:", code)
|
||||||
|
|
||||||
|
err = client.JoinRoom(code, "password")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
println("Connected to room")
|
||||||
|
} else if cmd == "JOIN" {
|
||||||
|
var password string
|
||||||
|
fmt.Print("Enter Room Code:\n>")
|
||||||
|
fmt.Scanln(&code)
|
||||||
|
|
||||||
|
fmt.Print("Enter Room Password:\n>")
|
||||||
|
fmt.Scanln(&password)
|
||||||
|
|
||||||
|
err := client.JoinRoom(code, password)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Joined room:", code)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.ListenForData()
|
||||||
|
// Handle received data in a separate goroutine
|
||||||
|
go func() {
|
||||||
|
for data := range client.DataChannel {
|
||||||
|
fmt.Printf("Received data: %s\n", data)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if cmd == "CREATE" {
|
||||||
|
println("Sleeping...")
|
||||||
|
time.Sleep(time.Second * 20)
|
||||||
|
println("Continuing!")
|
||||||
|
|
||||||
|
thing := hypixel.NewAPIKey(key)
|
||||||
|
api := hypixel.NewAPI(*thing)
|
||||||
|
|
||||||
|
memCache := &mcfetch.MemoryCache{}
|
||||||
|
memCache.Init()
|
||||||
|
|
||||||
|
resultChan := make(chan map[string]interface{})
|
||||||
|
errorChan := make(chan error)
|
||||||
|
|
||||||
|
players := []string{"illyum", "ergopunch"}
|
||||||
|
stuff := []map[string]interface{}{} // List to hold player JSON objects
|
||||||
|
|
||||||
|
println("Getting UUIDs")
|
||||||
|
for _, player := range players {
|
||||||
|
asyncFetcher := mcfetch.NewAsyncPlayerFetcher(
|
||||||
|
player,
|
||||||
|
memCache,
|
||||||
2,
|
2,
|
||||||
2*time.Second,
|
2*time.Second,
|
||||||
5*time.Second,
|
5*time.Second,
|
||||||
)
|
)
|
||||||
data, err := asyncFetcher.FetchPlayerData()
|
asyncFetcher.FetchPlayerData(resultChan, errorChan)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *DemoApp) onFileEmit(line string) {
|
var userID string
|
||||||
msg := strings.TrimSpace(line)
|
for i := 0; i < len(players); i++ {
|
||||||
if len(msg) < 34 {
|
select {
|
||||||
return
|
case data := <-resultChan:
|
||||||
}
|
if uuid, ok := data["id"].(string); ok {
|
||||||
submsg := msg[33:]
|
userID = uuid
|
||||||
if len(submsg) != 0 {
|
} else {
|
||||||
app.LogBuf.Add(submsg)
|
fmt.Println(fmt.Sprintf("%+v", data))
|
||||||
|
log.Fatal("UUID not found or invalid for player")
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
plr, err := app.FetchMCPlayer(playerName)
|
|
||||||
res_name := plr.Name
|
|
||||||
res_uuid := plr.UUID
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error fetching UUID: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("UUID of player %s: %s\n", res_name, res_uuid)
|
|
||||||
|
|
||||||
//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, _ := app.LogBuf.GetSecondToLast()
|
|
||||||
// TODO: Check if moderators
|
|
||||||
if !strings.HasPrefix(last, PartyListMembersPrefix) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
PartyMembersMsg := strings.TrimPrefix(last, PartyListMembersPrefix)
|
|
||||||
var ppl []mcfetch.CacheResult
|
|
||||||
for _, player := range strings.Split(PartyMembersMsg, ",") {
|
|
||||||
playerName := strings.TrimSpace(strings.TrimSuffix(player, " ?"))
|
|
||||||
if strings.HasPrefix(playerName, "[") {
|
|
||||||
playerName = strings.Split(playerName, " ")[1]
|
|
||||||
}
|
|
||||||
plr, err := app.FetchMCPlayer(playerName)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error fetching Player: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
res_name := plr.Name
|
|
||||||
res_uuid := plr.UUID
|
|
||||||
|
|
||||||
res_player := mcfetch.CacheResult{
|
|
||||||
UUID: plr.UUID,
|
|
||||||
Name: plr.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("UUID of player %s: %s\n", res_name, res_uuid)
|
|
||||||
ppl = append(ppl, res_player)
|
|
||||||
|
|
||||||
//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 := app.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]
|
|
||||||
}
|
|
||||||
|
|
||||||
plr, err := app.FetchMCPlayer(playerName)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error fetching Player: %v", err)
|
|
||||||
}
|
|
||||||
res_name := plr.Name
|
|
||||||
res_uuid := plr.UUID
|
|
||||||
|
|
||||||
res_player := mcfetch.CacheResult{
|
|
||||||
UUID: plr.UUID,
|
|
||||||
Name: plr.Name,
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("UUID of player %s: %s\n", res_name, res_uuid)
|
|
||||||
ppl = append(ppl, res_player)
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
app.sendPartyList(ppl)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
println(submsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) tailFile(path string, lineCh chan<- string) {
|
|
||||||
file, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to open file: %v", err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
file.Seek(0, 2)
|
|
||||||
|
|
||||||
reader := bufio.NewReader(file)
|
|
||||||
|
|
||||||
for {
|
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
lineCh <- line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) sendPartyList(ppl []mcfetch.CacheResult) {
|
|
||||||
for _, user := range ppl {
|
|
||||||
// Fetch player stats
|
// Fetch player stats
|
||||||
res, err := app.API.GetPlayerResponse(user.UUID)
|
res, err := api.GetPlayerResponse(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to get player data: %v", err)
|
log.Fatalf("Failed to get player data: %v", err)
|
||||||
}
|
}
|
||||||
@ -257,153 +193,40 @@ func (app *DemoApp) sendPartyList(ppl []mcfetch.CacheResult) {
|
|||||||
// Append the player JSON to the stuff list
|
// Append the player JSON to the stuff list
|
||||||
var playerMap map[string]interface{}
|
var playerMap map[string]interface{}
|
||||||
json.Unmarshal(playerJSON, &playerMap)
|
json.Unmarshal(playerJSON, &playerMap)
|
||||||
app.PartyBuilder = append(app.PartyBuilder, playerMap)
|
stuff = append(stuff, playerMap)
|
||||||
|
|
||||||
|
case err := <-errorChan:
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("UUID Done!")
|
||||||
|
println("Sending data...")
|
||||||
|
|
||||||
// Send the list of JSON objects to the client
|
// Send the list of JSON objects to the client
|
||||||
message, err := json.Marshal(app.PartyBuilder)
|
message, err := json.Marshal(stuff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to marshal stuff: %v", err)
|
log.Fatalf("Failed to marshal stuff: %v", err)
|
||||||
}
|
}
|
||||||
err = app.Client.SendData(string(message))
|
err = client.SendData(string(message))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error sending data: %v", err)
|
log.Printf("Error sending data: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println("Sent stuff:", app.PartyBuilder)
|
fmt.Println("Sent stuff:", stuff)
|
||||||
println("Sending Done!")
|
println("Sending Done!")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) Start() {
|
|
||||||
var cmd string
|
|
||||||
fmt.Print(" | CREATE\n | JOIN\nEnter Choice:\n>")
|
|
||||||
fmt.Scanln(&cmd)
|
|
||||||
|
|
||||||
if cmd != "CREATE" && cmd != "JOIN" {
|
|
||||||
fmt.Println("Invalid command.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
app.Client, err = netclient.NewClient("0.0.0.0", uuid.New().String())
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to create client: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd == "CREATE" {
|
|
||||||
app.CreateRoom()
|
|
||||||
} else if cmd == "JOIN" {
|
|
||||||
err := app.JoinRoom()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("[DEV] Joined Branches\n")
|
|
||||||
|
|
||||||
app.Client.ListenForData()
|
|
||||||
// Handle received data in a separate goroutine
|
|
||||||
go func() {
|
|
||||||
for data := range app.Client.DataChannel {
|
|
||||||
fmt.Printf("Received data: %s\n", data)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
select {
|
||||||
case data, ok := <-app.Client.DataChannel:
|
case <-client.DataChannel: // This blocks the main goroutine until data is received
|
||||||
if !ok {
|
fmt.Println("Data received, exiting...")
|
||||||
fmt.Println("Data channel closed, exiting...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
app.HandleData(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Closing app")
|
fmt.Println("Closing app")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *DemoApp) CreateRoom() {
|
// calculateRatio is a helper function to calculate ratios (e.g., WLR, KDR, etc.)
|
||||||
var err error
|
func calculateRatio(numerator, denominator int) float64 {
|
||||||
code, err := app.Client.CreateRoom("")
|
if denominator == 0 {
|
||||||
if err != nil {
|
return float64(numerator)
|
||||||
log.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
fmt.Println("Created room:", code)
|
return float64(numerator) / float64(denominator)
|
||||||
|
|
||||||
err = app.Client.JoinRoom(code, "password")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
println("Connected to room")
|
|
||||||
|
|
||||||
path := os.Getenv("USERPROFILE")
|
|
||||||
logPath := filepath.Join(path, ".lunarclient", "offline", "multiver", "logs", "latest.log")
|
|
||||||
|
|
||||||
fmt.Println("Reading log file from:", logPath)
|
|
||||||
|
|
||||||
lineCh := make(chan string)
|
|
||||||
go app.tailFile(logPath, lineCh)
|
|
||||||
|
|
||||||
// TODO: Do this in a different goroutine so that you can still listen to data from your own client
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case line := <-lineCh:
|
|
||||||
app.onFileEmit(replaceCorruptedRune(line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) JoinRoom() error {
|
|
||||||
var code string
|
|
||||||
var password string
|
|
||||||
fmt.Print("Enter Room Code:\n>")
|
|
||||||
fmt.Scanln(&code)
|
|
||||||
|
|
||||||
fmt.Print("Enter Room Password:\n>")
|
|
||||||
fmt.Scanln(&password)
|
|
||||||
|
|
||||||
err := app.Client.JoinRoom(code, password)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Joined room:", code)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) HandleData(data string) {
|
|
||||||
var playerWrappers []PlayerWrapper
|
|
||||||
|
|
||||||
err := json.Unmarshal([]byte(data), &playerWrappers)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Error unmarshalling data:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var players []*hypixel.Player
|
|
||||||
for _, wrapper := range playerWrappers {
|
|
||||||
players = append(players, &wrapper.Player)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass the extracted players to DisplayPlayers
|
|
||||||
app.DisplayPlayers(players)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (app *DemoApp) DisplayPlayers(players []*hypixel.Player) {
|
|
||||||
clearTerminal()
|
|
||||||
|
|
||||||
fmt.Printf("| %-20s | %-10s | %-10s |\n", "Player Name", "Bedwars Level", "FKDR")
|
|
||||||
fmt.Println("|----------------------|------------|------------|")
|
|
||||||
|
|
||||||
for _, player := range players {
|
|
||||||
fmt.Printf("| %-20s | %-10d | %-10.3f |\n",
|
|
||||||
player.DisplayName,
|
|
||||||
player.Achievements.BedwarsLevel,
|
|
||||||
player.Stats.Bedwars.FKDR)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("|----------------------|------------|------------|")
|
|
||||||
}
|
}
|
||||||
|
4
go.mod
4
go.mod
@ -3,6 +3,10 @@ module hudly
|
|||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71
|
||||||
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/haleyrom/skia-go v0.0.0-20240328095045-3f321a6a6e4d
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/mattn/go-pointer v0.0.0-20190911064623-a0a44394634f // indirect
|
||||||
|
@ -28,27 +28,24 @@ func NewAsyncPlayerFetcher(playerName string, cache ICache, retries int, retryDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchPlayerData fetches the player data asynchronously using channels
|
// FetchPlayerData fetches the player data asynchronously
|
||||||
func (pf *AsyncPlayerFetcher) FetchPlayerData(resultChan chan *FetchedPlayerResult, errorChan chan error) {
|
func (pf *AsyncPlayerFetcher) FetchPlayerData(resultChan chan map[string]interface{}, errorChan chan error) {
|
||||||
go func() {
|
go func() {
|
||||||
cachedData, found := pf.cache.Get(pf.playerName)
|
cachedData, found := pf.cache.Get(pf.playerName)
|
||||||
if found {
|
if found {
|
||||||
resultChan <- (*FetchedPlayerResult)(cachedData)
|
resultChan <- cachedData
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not in cache, make request to Mojang API
|
var data map[string]interface{}
|
||||||
var player FetchedPlayerResult
|
|
||||||
for i := 0; i < pf.retries; i++ {
|
for i := 0; i < pf.retries; i++ {
|
||||||
resp, err := pf.makeRequest(pf.playerName)
|
resp, err := pf.makeRequest(pf.playerName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
// Decode the response into FetchedPlayerResult
|
if err := json.NewDecoder(resp.Body).Decode(&data); err == nil {
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&player); err == nil {
|
pf.cache.Set(pf.playerName, data)
|
||||||
// Store the result in the cache and return the data
|
|
||||||
pf.cache.Set(pf.playerName, (*CacheResult)(&player))
|
|
||||||
pf.cache.Sync()
|
pf.cache.Sync()
|
||||||
resultChan <- &player
|
resultChan <- data
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,16 +8,11 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CacheResult struct {
|
|
||||||
UUID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ICache interface {
|
type ICache interface {
|
||||||
Init()
|
Init()
|
||||||
Load()
|
Load()
|
||||||
Get(key string) (*CacheResult, bool)
|
Get(key string) (map[string]interface{}, bool)
|
||||||
Set(key string, data *CacheResult)
|
Set(key string, data map[string]interface{})
|
||||||
Save()
|
Save()
|
||||||
Sync()
|
Sync()
|
||||||
Purge()
|
Purge()
|
||||||
@ -26,31 +21,31 @@ type ICache interface {
|
|||||||
|
|
||||||
// MemoryCache implementation
|
// MemoryCache implementation
|
||||||
type MemoryCache struct {
|
type MemoryCache struct {
|
||||||
cache map[string]*CacheResult
|
cache map[string]interface{}
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the cache (no-op for MemoryCache)
|
// Init initializes the cache (no-op for MemoryCache)
|
||||||
func (c *MemoryCache) Init() {
|
func (c *MemoryCache) Init() {
|
||||||
c.cache = make(map[string]*CacheResult)
|
c.cache = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the cache (no-op for MemoryCache)
|
// Load loads the cache (no-op for MemoryCache)
|
||||||
func (c *MemoryCache) Load() {}
|
func (c *MemoryCache) Load() {}
|
||||||
|
|
||||||
// Get retrieves an item from the cache
|
// Get retrieves an item from the cache
|
||||||
func (c *MemoryCache) Get(key string) (*CacheResult, bool) {
|
func (c *MemoryCache) Get(key string) (map[string]interface{}, bool) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
value, found := c.cache[key]
|
value, found := c.cache[key]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
return value, true
|
return value.(map[string]interface{}), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set stores an item in the cache
|
// Set stores an item in the cache
|
||||||
func (c *MemoryCache) Set(key string, data *CacheResult) {
|
func (c *MemoryCache) Set(key string, data map[string]interface{}) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.cache[key] = data
|
c.cache[key] = data
|
||||||
@ -69,19 +64,19 @@ func (c *MemoryCache) Purge() {}
|
|||||||
func (c *MemoryCache) Clear() {
|
func (c *MemoryCache) Clear() {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.cache = make(map[string]*CacheResult)
|
c.cache = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// JsonFileCache implementation
|
// JsonFileCache implementation
|
||||||
type JsonFileCache struct {
|
type JsonFileCache struct {
|
||||||
filename string
|
filename string
|
||||||
cache map[CacheResult]interface{}
|
cache map[string]interface{}
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the cache
|
// Init initializes the cache
|
||||||
func (c *JsonFileCache) Init() {
|
func (c *JsonFileCache) Init() {
|
||||||
c.cache = make(map[CacheResult]interface{})
|
c.cache = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load loads the cache from a JSON file
|
// Load loads the cache from a JSON file
|
||||||
@ -109,22 +104,21 @@ func (c *JsonFileCache) Load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get retrieves an item from the cache
|
// Get retrieves an item from the cache
|
||||||
func (c *JsonFileCache) Get(key string) (*CacheResult, bool) {
|
func (c *JsonFileCache) Get(key string) (map[string]interface{}, bool) {
|
||||||
//c.mu.RLock()
|
c.mu.RLock()
|
||||||
//defer c.mu.RUnlock()
|
defer c.mu.RUnlock()
|
||||||
//value, found := c.cache[key]
|
value, found := c.cache[key]
|
||||||
//if !found {
|
if !found {
|
||||||
// return nil, false
|
|
||||||
//}
|
|
||||||
//return value, true
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
return value.(map[string]interface{}), true
|
||||||
|
}
|
||||||
|
|
||||||
// Set stores an item in the cache
|
// Set stores an item in the cache
|
||||||
func (c *JsonFileCache) Set(key string, data *CacheResult) {
|
func (c *JsonFileCache) Set(key string, data map[string]interface{}) {
|
||||||
//c.mu.Lock()
|
c.mu.Lock()
|
||||||
//defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
//c.cache[key] = data
|
c.cache[key] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save saves the cache to a JSON file
|
// Save saves the cache to a JSON file
|
||||||
@ -156,5 +150,5 @@ func (c *JsonFileCache) Purge() {}
|
|||||||
func (c *JsonFileCache) Clear() {
|
func (c *JsonFileCache) Clear() {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
c.cache = make(map[CacheResult]interface{})
|
c.cache = make(map[string]interface{})
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,6 @@ type PlayerFetcher struct {
|
|||||||
cache ICache
|
cache ICache
|
||||||
}
|
}
|
||||||
|
|
||||||
type FetchedPlayerResult struct {
|
|
||||||
UUID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPlayerFetcher creates a new PlayerFetcher with an abstract cache (ICache)
|
// NewPlayerFetcher creates a new PlayerFetcher with an abstract cache (ICache)
|
||||||
func NewPlayerFetcher(playerName string, cache ICache, retries int, retryDelay time.Duration, timeout time.Duration) *PlayerFetcher {
|
func NewPlayerFetcher(playerName string, cache ICache, retries int, retryDelay time.Duration, timeout time.Duration) *PlayerFetcher {
|
||||||
cache.Init()
|
cache.Init()
|
||||||
@ -34,21 +29,21 @@ func NewPlayerFetcher(playerName string, cache ICache, retries int, retryDelay t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FetchPlayerData fetches the player data synchronously
|
// FetchPlayerData fetches the player data synchronously
|
||||||
func (pf *PlayerFetcher) FetchPlayerData() (*FetchedPlayerResult, error) {
|
func (pf *PlayerFetcher) FetchPlayerData() (map[string]interface{}, error) {
|
||||||
//cachedData, found := pf.cache.Get(pf.playerName)
|
cachedData, found := pf.cache.Get(pf.playerName)
|
||||||
//if found {
|
if found {
|
||||||
// return &FetchedPlayerResult{}, nil
|
return cachedData, nil
|
||||||
//}
|
}
|
||||||
|
|
||||||
var player FetchedPlayerResult
|
var data map[string]interface{}
|
||||||
for i := 0; i < pf.retries; i++ {
|
for i := 0; i < pf.retries; i++ {
|
||||||
resp, err := pf.makeRequest(pf.playerName)
|
resp, err := pf.makeRequest(pf.playerName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&player); err == nil {
|
if err := json.NewDecoder(resp.Body).Decode(&data); err == nil {
|
||||||
// pf.cache.Set(pf.playerName, player)
|
pf.cache.Set(pf.playerName, data)
|
||||||
// pf.cache.Sync()
|
pf.cache.Sync()
|
||||||
return &player, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
time.Sleep(pf.retryDelay)
|
time.Sleep(pf.retryDelay)
|
||||||
|
@ -199,14 +199,12 @@ func handleClient(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleConnectRequest(conn net.Conn, packet ConnectionRequestPacket) {
|
func handleConnectRequest(conn net.Conn, packet ConnectionRequestPacket) {
|
||||||
fmt.Println("Incoming connection request")
|
|
||||||
room := findRoom(packet.RoomCode)
|
room := findRoom(packet.RoomCode)
|
||||||
if room == nil {
|
if room == nil {
|
||||||
writePacket(conn, byte(CONNECT_RESPONSE), encodeResponsePacket(ConnectionResponsePacket{
|
writePacket(conn, byte(CONNECT_RESPONSE), encodeResponsePacket(ConnectionResponsePacket{
|
||||||
Success: false,
|
Success: false,
|
||||||
Reason: "room does not exist",
|
Reason: "room does not exist",
|
||||||
}))
|
}))
|
||||||
fmt.Println("Attempted room does not exist")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +213,6 @@ func handleConnectRequest(conn net.Conn, packet ConnectionRequestPacket) {
|
|||||||
Success: true,
|
Success: true,
|
||||||
Reason: "",
|
Reason: "",
|
||||||
}))
|
}))
|
||||||
fmt.Println("Invalid password")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +220,6 @@ func handleConnectRequest(conn net.Conn, packet ConnectionRequestPacket) {
|
|||||||
Success: false,
|
Success: false,
|
||||||
Reason: "invalid password",
|
Reason: "invalid password",
|
||||||
}))
|
}))
|
||||||
fmt.Println("Connected user to room ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleCreateRoomRequest(conn net.Conn, packet CreateRoomRequestPacket) {
|
func handleCreateRoomRequest(conn net.Conn, packet CreateRoomRequestPacket) {
|
||||||
@ -301,7 +297,6 @@ func handleJoinRequest(conn net.Conn, packet JoinRequestPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleSendData(conn net.Conn, packet SendDataRequestPacket) {
|
func handleSendData(conn net.Conn, packet SendDataRequestPacket) {
|
||||||
fmt.Println("Incoming send data request")
|
|
||||||
mu.Lock()
|
mu.Lock()
|
||||||
client, exists := clients[packet.UserID]
|
client, exists := clients[packet.UserID]
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user