Skip to content

Commit

Permalink
Cleanup changes
Browse files Browse the repository at this point in the history
  • Loading branch information
b-j-roberts committed Dec 19, 2024
1 parent d8c31c9 commit db46f6a
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 82 deletions.
115 changes: 99 additions & 16 deletions backend/routes/stencils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package routes

import (
"bytes"
"encoding/json"
"fmt"
"image"
"image/color"
"image/png"
"io"
"log"
"net/http"
"os"
"os/exec"
Expand All @@ -27,6 +28,7 @@ func InitStencilsRoutes() {
http.HandleFunc("/get-hot-stencils", getHotStencils)
http.HandleFunc("/add-stencil-img", addStencilImg)
http.HandleFunc("/add-stencil-data", addStencilData)
http.HandleFunc("/get-stencil-pixel-data", getStencilPixelData)
if !core.ArtPeaceBackend.BackendConfig.Production {
http.HandleFunc("/add-stencil-devnet", addStencilDevnet)
http.HandleFunc("/remove-stencil-devnet", removeStencilDevnet)
Expand Down Expand Up @@ -403,7 +405,7 @@ func addStencilImg(w http.ResponseWriter, r *http.Request) {

r.Body.Close()

imageData, err := imageToPixelData(fileBytes, 1)
imageData, err := worldImageToPixelData(fileBytes, 1, 0)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to convert image to pixel data")
return
Expand Down Expand Up @@ -590,28 +592,16 @@ func addStencilDevnet(w http.ResponseWriter, r *http.Request) {
return
}

// Add the stencil
shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.AddStencilDevnet
contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS")
cmd := exec.Command(shellCmd, contract, "add_stencil", strconv.Itoa(worldId), hash, strconv.Itoa(width), strconv.Itoa(height), strconv.Itoa(position))
output, err := cmd.CombinedOutput()
_, err = cmd.Output()
if err != nil {
log.Printf("Add stencil command failed: %v\nOutput: %s\nCommand: %v", err, string(output), cmd.String())
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to add stencil to devnet")
return
}

// Favorite the newly created stencil
shellCmd = core.ArtPeaceBackend.BackendConfig.Scripts.FavoriteStencilDevnet
cmd = exec.Command(shellCmd, contract, "favorite_stencil", strconv.Itoa(worldId), strconv.Itoa(position))
output, err = cmd.CombinedOutput()
if err != nil {
log.Printf("Favorite stencil command failed: %v\nOutput: %s\nCommand: %v", err, string(output), cmd.String())
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to favorite newly created stencil")
return
}

routeutils.WriteResultJson(w, "Stencil added and favorited")
routeutils.WriteResultJson(w, "Stencil added to devnet")
}

func removeStencilDevnet(w http.ResponseWriter, r *http.Request) {
Expand Down Expand Up @@ -721,3 +711,96 @@ func unfavoriteStencilDevnet(w http.ResponseWriter, r *http.Request) {

routeutils.WriteResultJson(w, "Stencil unfavorited in devnet")
}

func worldImageToPixelData(imageData []byte, scaleFactor int, worldId int) ([]int, error) {
img, _, err := image.Decode(bytes.NewReader(imageData))
if err != nil {
return nil, err
}

colors, err := core.PostgresQuery[ColorType]("SELECT hex FROM WorldsColors WHERE world_id = $1 ORDER BY color_key", worldId)
if err != nil {
return nil, err
}

colorCount := len(colors)
palette := make([]color.Color, colorCount)
for i := 0; i < colorCount; i++ {
colorHex := colors[i]
palette[i] = hexToRGBA(colorHex)
}

bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
scaledWidth := width / scaleFactor
scaledHeight := height / scaleFactor
pixelData := make([]int, scaledWidth*scaledHeight)

for y := 0; y < height; y += scaleFactor {
for x := 0; x < width; x += scaleFactor {
newX := x / scaleFactor
newY := y / scaleFactor
rgba := color.RGBAModel.Convert(img.At(x, y)).(color.RGBA)
if rgba.A < 128 { // Consider pixels with less than 50% opacity as transparent
pixelData[newY*scaledWidth+newX] = 0xFF
} else {
closestIndex := findClosestColor(rgba, palette)
pixelData[newY*scaledWidth+newX] = closestIndex
}
}
}

return pixelData, nil
}

func getStencilPixelData(w http.ResponseWriter, r *http.Request) {
// Get stencil hash from query params
hash := r.URL.Query().Get("hash")
if hash == "" {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Hash parameter is required")
return
}

// Read the stencil image file
filename := fmt.Sprintf("stencils/stencil-%s.png", hash)
fileBytes, err := os.ReadFile(filename)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusNotFound, "Stencil not found")
return
}

// Convert image to pixel data
pixelData, err := worldImageToPixelData(fileBytes, 1, 0)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to process image")
return
}

// Get image dimensions
img, _, err := image.Decode(bytes.NewReader(fileBytes))
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to decode image")
return
}
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y

// Create response structure
response := struct {
Width int `json:"width"`
Height int `json:"height"`
PixelData []int `json:"pixelData"`
}{
Width: width,
Height: height,
PixelData: pixelData,
}

jsonResponse, err := json.Marshal(response)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to create response")
return
}

routeutils.WriteDataJson(w, string(jsonResponse))
}
53 changes: 5 additions & 48 deletions backend/routes/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package routes

import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/exec"
Expand Down Expand Up @@ -542,24 +540,13 @@ func createCanvasDevnet(w http.ResponseWriter, r *http.Request) {
contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS")

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))
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("Create canvas command failed: %v\nOutput: %s\nCommand: %v", err, string(output), cmd.String())
routeutils.WriteErrorJson(w, http.StatusInternalServerError, fmt.Sprintf("Failed to create canvas: %v", err))
return
}

// Favorite the newly created canvas
shellCmd = core.ArtPeaceBackend.BackendConfig.Scripts.FavoriteWorldDevnet
cmd = exec.Command(shellCmd, contract, "favorite_canvas", uniqueName)
output, err = cmd.CombinedOutput()
_, err = cmd.Output()
if err != nil {
log.Printf("Favorite canvas command failed: %v\nOutput: %s\nCommand: %v", err, string(output), cmd.String())
routeutils.WriteErrorJson(w, http.StatusInternalServerError, fmt.Sprintf("Failed to favorite newly created canvas: %v", err))
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to create canvas")
return
}

routeutils.WriteResultJson(w, "Canvas created and favorited")
routeutils.WriteResultJson(w, "Canvas created")
}

func favoriteWorldDevnet(w http.ResponseWriter, r *http.Request) {
Expand All @@ -574,22 +561,7 @@ func favoriteWorldDevnet(w http.ResponseWriter, r *http.Request) {
return
}

// Try to get worldId either directly or from worldName
var worldId string
if (*jsonBody)["worldId"] != "" {
worldId = (*jsonBody)["worldId"]
} else if (*jsonBody)["worldName"] != "" {
// Get worldId from worldName
id, err := core.PostgresQueryOne[int]("SELECT world_id FROM worlds WHERE unique_name = $1", (*jsonBody)["worldName"])
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid world name")
return
}
worldId = strconv.Itoa(*id)
} else {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Must provide either worldId or worldName")
return
}
worldId := (*jsonBody)["worldId"]

shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.FavoriteWorldDevnet
contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS")
Expand All @@ -616,22 +588,7 @@ func unfavoriteWorldDevnet(w http.ResponseWriter, r *http.Request) {
return
}

// Try to get worldId either directly or from worldName
var worldId string
if (*jsonBody)["worldId"] != "" {
worldId = (*jsonBody)["worldId"]
} else if (*jsonBody)["worldName"] != "" {
// Get worldId from worldName
id, err := core.PostgresQueryOne[int]("SELECT world_id FROM worlds WHERE unique_name = $1", (*jsonBody)["worldName"])
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid world name")
return
}
worldId = strconv.Itoa(*id)
} else {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Must provide either worldId or worldName")
return
}
worldId := (*jsonBody)["worldId"]

shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.UnfavoriteWorldDevnet
contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS")
Expand Down
25 changes: 12 additions & 13 deletions onchain/src/canvas_factory.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -113,30 +113,29 @@ pub mod CanvasFactory {
fn create_canvas(
ref self: ContractState, init_params: super::Canvas::InitParams
) -> (ContractAddress, u64) {
// TODO: Serialize before calling this function to defer serialization to the contract input
let mut init_params_serialized = array![];
init_params.serialize(ref init_params_serialized);
let deploy_res = deploy_syscall(
self.canvas_class_hash.read(),
get_caller_address().into(),
self.canvas_count.read().into(),
init_params_serialized.span(),
false
true
);
let (canvas_address, _) = deploy_res.unwrap();
if deploy_res.is_err() {
panic!("Failed to deploy canvas contract");
}
let (addr, _response) = deploy_res.unwrap();
let canvas_id = self.canvas_count.read();
self.canvases.write(canvas_id, canvas_address);
self.canvases.write(canvas_id, addr);
self.canvas_count.write(canvas_id + 1);

// Auto-favorite the canvas for the creator
let caller = get_caller_address();
self.canvas_favorites.write((canvas_id, caller), true);
self.emit(Event::CanvasFavorited(CanvasFavorited { canvas_id, user: caller }));

// Emit the canvas created event
self
.emit(
Event::CanvasCreated(CanvasCreated { canvas_id, canvas_address, init_params })
Event::CanvasCreated(
CanvasCreated { canvas_id, canvas_address: addr, init_params, }
)
);
(canvas_address, canvas_id)
(addr, canvas_id)
}

fn get_canvas(self: @ContractState, canvas_id: u64) -> ContractAddress {
Expand Down
7 changes: 2 additions & 5 deletions onchain/src/multi_canvas.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -328,14 +328,12 @@ pub mod MultiCanvas {
};
self.canvas_count.write(canvas_id + 1);
self.unique_names.write(init_params.unique_name, true);
self.emit(CanvasCreated { canvas_id, init_params });

// Auto-favorite the canvas for the creator
let caller = get_caller_address();
self.canvas_favorites.write((canvas_id, caller), true);
self.emit(Event::CanvasFavorited(CanvasFavorited { canvas_id, user: caller }));

// Emit canvas created event
self.emit(CanvasCreated { canvas_id, init_params });
canvas_id
}

Expand Down Expand Up @@ -498,14 +496,13 @@ pub mod MultiCanvas {
assert(stencil.height <= MAX_STENCIL_SIZE, 'Stencil too large');
self.stencils.write((canvas_id, stencil_id), stencil.clone());
self.stencil_counts.write(canvas_id, stencil_id + 1);
self.emit(StencilAdded { canvas_id, stencil_id, stencil });

// Auto-favorite the stencil for the creator
let caller = get_caller_address();
self.stencil_favorites.write((canvas_id, stencil_id, caller), true);
self.emit(StencilFavorited { canvas_id, stencil_id, user: caller });

// Emit the stencil added event
self.emit(StencilAdded { canvas_id, stencil_id, stencil });
stencil_id
}

Expand Down

0 comments on commit db46f6a

Please sign in to comment.