Skip to content

Commit

Permalink
Merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
b-j-roberts committed Dec 16, 2024
2 parents d6380f7 + 26081ea commit 36cf487
Show file tree
Hide file tree
Showing 11 changed files with 5,049 additions and 9,456 deletions.
59 changes: 38 additions & 21 deletions backend/routes/indexer/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,33 @@ import (

func processCanvasCreatedEvent(event IndexerEvent) {
canvasIdHex := event.Event.Keys[1]
host := event.Event.Data[0][2:] // Remove 0x prefix
nameHex := event.Event.Data[1][2:] // Remove 0x prefix
widthHex := event.Event.Data[2]
heightHex := event.Event.Data[3]
timeBetweenPixelsHex := event.Event.Data[4]
colorPaletteLenHex := event.Event.Data[5]
host := event.Event.Data[0][2:] // Remove 0x prefix
nameHex := event.Event.Data[1][2:] // Remove 0x prefix
uniqueNameHex := event.Event.Data[2][2:] // Remove 0x prefix
widthHex := event.Event.Data[3]
heightHex := event.Event.Data[4]
timeBetweenPixelsHex := event.Event.Data[5]
colorPaletteLenHex := event.Event.Data[6]

colorPaletteLen, err := strconv.ParseInt(colorPaletteLenHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse colorPaletteLenHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse colorPaletteLenHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}
// Skip colors since they are processed in another event

startTimeHex := event.Event.Data[6+colorPaletteLen]
endTimeHex := event.Event.Data[7+colorPaletteLen]
startTimeHex := event.Event.Data[7+colorPaletteLen]
endTimeHex := event.Event.Data[8+colorPaletteLen]

canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse canvasIdHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse canvasIdHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

decodedName, err := hex.DecodeString(nameHex)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to decode nameHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to decode nameHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}
trimmedName := []byte{}
Expand All @@ -55,40 +56,56 @@ func processCanvasCreatedEvent(event IndexerEvent) {
}
name := string(trimmedName)

decodedUniqueName, err := hex.DecodeString(uniqueNameHex)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to decode uniqueNameHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}
trimmedUniqueName := []byte{}
trimming = true
for _, b := range decodedUniqueName {
if b == 0 && trimming {
continue
}
trimming = false
trimmedUniqueName = append(trimmedUniqueName, b)
}
uniqueName := string(trimmedUniqueName)

width, err := strconv.ParseInt(widthHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse widthHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse widthHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

height, err := strconv.ParseInt(heightHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse heightHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse heightHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

timeBetweenPixels, err := strconv.ParseInt(timeBetweenPixelsHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse timeBetweenPixelsHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse timeBetweenPixelsHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

startTime, err := strconv.ParseInt(startTimeHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse startTimeHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse startTimeHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

endTime, err := strconv.ParseInt(endTimeHex, 0, 64)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse endTimeHex", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to parse endTimeHex", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

// Insert into Worlds
_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Worlds (world_id, host, name, width, height, time_between_pixels, start_time, end_time) VALUES ($1, $2, $3, $4, $5, $6, TO_TIMESTAMP($7), TO_TIMESTAMP($8))", canvasId, host, name, width, height, timeBetweenPixels, startTime, endTime)
_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Worlds (world_id, host, name, unique_name, width, height, time_between_pixels, start_time, end_time) VALUES ($1, $2, $3, $4, $5, $6, $7, TO_TIMESTAMP($8), TO_TIMESTAMP($9))", canvasId, host, name, uniqueName, width, height, timeBetweenPixels, startTime, endTime)
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to insert into Worlds", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to insert into Worlds", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}

Expand All @@ -103,11 +120,11 @@ func processCanvasCreatedEvent(event IndexerEvent) {
canvas := make([]byte, totalByteSize)
err := core.ArtPeaceBackend.Databases.Redis.Set(context.Background(), canvasRedisKey, canvas, 0).Err()
if err != nil {
PrintIndexerError("processCanvasCreatedEvent", "Failed to set canvas in redis", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Failed to set canvas in redis", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
return
}
} else {
PrintIndexerError("processCanvasCreatedEvent", "Canvas already exists in redis", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
PrintIndexerError("processCanvasCreatedEvent", "Canvas already exists in redis", canvasIdHex, host, nameHex, uniqueNameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err)
}

// Create base directories if they don't exist
Expand All @@ -127,7 +144,7 @@ func processCanvasCreatedEvent(event IndexerEvent) {
}

generatedWorldImage := image.NewRGBA(image.Rect(0, 0, int(width), int(height)))
baseColorHex := event.Event.Data[6]
baseColorHex := event.Event.Data[7]
baseColor := baseColorHex[len(baseColorHex)-6:] // Remove prefix
baseColorR, err := strconv.ParseInt(baseColor[0:2], 16, 64)
if err != nil {
Expand Down
117 changes: 28 additions & 89 deletions backend/routes/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package routes

import (
"context"
"fmt"
"net/http"
"os"
"os/exec"
Expand All @@ -17,6 +16,7 @@ import (
// TODO: check-worlds-name-unique?
func InitWorldsRoutes() {
http.HandleFunc("/get-world-canvas", getWorldCanvas)
http.HandleFunc("/get-world-id", getWorldId)
http.HandleFunc("/get-world", getWorld)
http.HandleFunc("/get-worlds", getWorlds)
http.HandleFunc("/get-new-worlds", getNewWorlds)
Expand All @@ -29,7 +29,6 @@ func InitWorldsRoutes() {
http.HandleFunc("/get-worlds-colors", getWorldsColors)
http.HandleFunc("/get-worlds-pixel-count", getWorldsPixelCount)
http.HandleFunc("/get-worlds-pixel-info", getWorldsPixelInfo)
http.HandleFunc("/worlds/", handleWorldRoute)
http.HandleFunc("/check-world-name", checkWorldName)
if !core.ArtPeaceBackend.BackendConfig.Production {
http.HandleFunc("/create-canvas-devnet", createCanvasDevnet)
Expand Down Expand Up @@ -68,6 +67,7 @@ type WorldData struct {
WorldId int `json:"worldId"`
Host string `json:"host"`
Name string `json:"name"`
UniqueName string `json:"uniqueName"`
Width int `json:"width"`
Height int `json:"height"`
TimeBetweenPixels int `json:"timeBetweenPixels"`
Expand All @@ -77,25 +77,28 @@ type WorldData struct {
Favorited bool `json:"favorited"`
}

func getWorldId(w http.ResponseWriter, r *http.Request) {
worldName := r.URL.Query().Get("worldName")
if worldName == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldName")
return
}
worldId, err := core.PostgresQueryOne[int]("SELECT world_id FROM worlds WHERE unique_name = $1", worldName)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve World")
return
}
routeutils.WriteDataJson(w, strconv.Itoa(*worldId))
}

func getWorld(w http.ResponseWriter, r *http.Request) {
worldId := r.URL.Query().Get("worldId")
if worldId == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId")
return
}

var world []byte
var err error

// Try to parse as numeric ID first
if _, parseErr := strconv.Atoi(worldId); parseErr == nil {
// It's a numeric ID
world, err = core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE world_id = $1", worldId)
} else {
// Try as name
world, err = core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE name = $1", worldId)
}

world, err := core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE world_id = $1", worldId)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve World")
return
Expand Down Expand Up @@ -186,7 +189,6 @@ func getNewWorlds(w http.ResponseWriter, r *http.Request) {
LIMIT $2 OFFSET $3`
worlds, err := core.PostgresQueryJson[WorldData](query, address, pageLength, offset)
if err != nil {
fmt.Println(err)
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve Worlds")
return
}
Expand Down Expand Up @@ -267,40 +269,14 @@ func getWorldsLastPlacedTime(w http.ResponseWriter, r *http.Request) {
return
}

var query string
var args []interface{}

// Try to parse as numeric ID first
if _, parseErr := strconv.Atoi(worldId); parseErr == nil {
// It's a numeric ID
query = `
SELECT COALESCE(
(SELECT time FROM WorldsLastPlacedTime
WHERE world_id = $1 and address = $2),
TO_TIMESTAMP(0)
)`
args = []interface{}{worldId, address}
} else {
// Try as name
query = `
SELECT COALESCE(
(SELECT wlpt.time
FROM WorldsLastPlacedTime wlpt
JOIN worlds w ON w.world_id = wlpt.world_id
WHERE w.name = $1 and wlpt.address = $2),
TO_TIMESTAMP(0)
)`
args = []interface{}{worldId, address}
}

lastTime, err := core.PostgresQueryOne[*time.Time](query, args...)
lastTime, err := core.PostgresQueryOne[*time.Time]("SELECT COALESCE((SELECT time FROM WorldsLastPlacedTime WHERE world_id = $1 and address = $2), TO_TIMESTAMP(0))", worldId, address)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get last placed time")
return
}

// Return the last placed time in utc z format
routeutils.WriteDataJson(w, "\""+(*lastTime).UTC().Format(time.RFC3339)+"\"")
routeutils.WriteDataJson(w, "\""+string((*lastTime).UTC().Format(time.RFC3339))+"\"")
}

func getWorldsExtraPixels(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -498,16 +474,17 @@ func createCanvasDevnet(w http.ResponseWriter, r *http.Request) {

host := (*jsonBody)["host"]
name := (*jsonBody)["name"]
uniqueName := (*jsonBody)["unique_name"]

if host == "" || name == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing host or name")
if host == "" || name == "" || uniqueName == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing host or name or uniqueName")
return
}

// Check if world name already exists
exists, err := core.PostgresQueryOne[bool]("SELECT EXISTS(SELECT 1 FROM worlds WHERE LOWER(name) = LOWER($1))", name)
exists, err := core.PostgresQueryOne[bool]("SELECT EXISTS(SELECT 1 FROM worlds WHERE unique_name = $1)", uniqueName)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to check world name")
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "World unique name check failed")
return
}
if *exists {
Expand Down Expand Up @@ -562,7 +539,7 @@ func createCanvasDevnet(w http.ResponseWriter, r *http.Request) {
shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.CreateCanvasDevnet
contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS")

cmd := exec.Command(shellCmd, contract, "create_canvas", host, name, strconv.Itoa(width), strconv.Itoa(height), strconv.Itoa(timer), strconv.Itoa(len(palette)), paletteInput, strconv.Itoa(startTime), strconv.Itoa(endTime))
cmd := exec.Command(shellCmd, contract, "create_canvas", host, name, uniqueName, strconv.Itoa(width), strconv.Itoa(height), strconv.Itoa(timer), strconv.Itoa(len(palette)), paletteInput, strconv.Itoa(startTime), strconv.Itoa(endTime))
_, err = cmd.Output()
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to create canvas")
Expand Down Expand Up @@ -688,45 +665,10 @@ func placeWorldPixelDevnet(w http.ResponseWriter, r *http.Request) {
routeutils.WriteResultJson(w, "Pixel placed world")
}

func handleWorldRoute(w http.ResponseWriter, r *http.Request) {
routeutils.SetupAccessHeaders(w)

// Extract the identifier from the URL path
pathParts := strings.Split(r.URL.Path, "/")
if len(pathParts) != 3 {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid world path")
return
}

identifier := pathParts[2]

// Try to parse as worldId first
if worldId, err := strconv.Atoi(identifier); err == nil {
// It's a numeric ID
world, err := core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE world_id = $1", worldId)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusNotFound, "World not found")
return
}
routeutils.WriteDataJson(w, string(world))
return
}

// If not numeric, try to find by name
world, err := core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE name = $1", identifier)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusNotFound, "World not found")
return
}
routeutils.WriteDataJson(w, string(world))
}

func checkWorldName(w http.ResponseWriter, r *http.Request) {
routeutils.SetupAccessHeaders(w)

name := strings.TrimSpace(r.URL.Query().Get("name"))
name := r.URL.Query().Get("uniqueName")
if name == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing name parameter")
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing uniqueName parameter")
return
}

Expand All @@ -737,15 +679,12 @@ func checkWorldName(w http.ResponseWriter, r *http.Request) {
return
}

// Log for debugging
fmt.Printf("Checking world name: %s, exists: %v\n", name, exists)

routeutils.WriteDataJson(w, strconv.FormatBool(exists))
}

// Add a helper function to check if a world name exists
func doesWorldNameExist(name string) (bool, error) {
exists, err := core.PostgresQueryOne[bool]("SELECT EXISTS(SELECT 1 FROM worlds WHERE LOWER(name) = LOWER($1))", name)
exists, err := core.PostgresQueryOne[bool]("SELECT EXISTS(SELECT 1 FROM worlds WHERE unique_name = $1)", name)
if err != nil {
return false, err
}
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ services:
- REACT_APP_ROUND_NUMBER=2
volumes:
- ./frontend/package.json:/app/package.json
- ./frontend/package-lock.json:/app/package-lock.json
- ./frontend/pnpm-lock.yaml:/app/pnpm-lock.yaml
- ./frontend/public/:/app/public
- ./frontend/src:/app/src
- configs:/app/src/configs
Expand Down
Loading

0 comments on commit 36cf487

Please sign in to comment.