Skip to content

Commit

Permalink
feat: round 2 pt 1 (#253)
Browse files Browse the repository at this point in the history
* Various improvements, bots, and contracts setup for art/peace round 2

* Formatting frontend

* Share icon
  • Loading branch information
b-j-roberts authored Sep 27, 2024
1 parent f7936c6 commit a3e2a9d
Show file tree
Hide file tree
Showing 30 changed files with 562 additions and 115 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Sys files
**/.DS_Store
.env
.env.main
.env.sep

# Scarb and Starknet Foundry
**/target
Expand Down
34 changes: 34 additions & 0 deletions backend/routes/indexer/pixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,37 @@ func revertExtraPixelsPlacedEvent(event IndexerEvent) {
return
}
}

func processHostAwardedPixelsEvent(event IndexerEvent) {
user := event.Event.Keys[1][2:] // Remove 0x prefix
awardHex := event.Event.Data[0]

award, err := strconv.ParseInt(awardHex, 0, 64)
if err != nil {
PrintIndexerError("processHostAwardedPixelsEvent", "Error converting award hex to int", user, awardHex)
return
}

_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO ExtraPixels (address, available, used) VALUES ($1, $2, 0) ON CONFLICT (address) DO UPDATE SET available = ExtraPixels.available + $2", user, award)
if err != nil {
PrintIndexerError("processHostAwardedPixelsEvent", "Error updating extra pixels in postgres", user, awardHex)
return
}
}

func revertHostAwardedPixelsEvent(event IndexerEvent) {
user := event.Event.Keys[1][2:] // Remove 0x prefix
awardHex := event.Event.Data[0]

award, err := strconv.ParseInt(awardHex, 0, 64)
if err != nil {
PrintIndexerError("revertHostAwardedPixelsEvent", "Error converting award hex to int", user, awardHex)
return
}

_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "UPDATE ExtraPixels SET available = ExtraPixels.available - $1 WHERE address = $2", award, user)
if err != nil {
PrintIndexerError("revertHostAwardedPixelsEvent", "Error updating extra pixels in postgres", user, awardHex)
return
}
}
4 changes: 4 additions & 0 deletions backend/routes/indexer/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const (
factionTemplateRemovedEvent = "0x029a976c0074fc910f3a6a58f1351c48dab7b1c539f54ed930616292c806283f"
chainFactionTemplateAddedEvent = "0x00476f35ea27024c89c1fc05dfad873e9e93419e452ee781e8207e435289a39b"
chainFactionTemplateRemovedEvent = "0x0126718de7cb8b83dfa258eb095bc0ec7a3ef5a2258ebd1ed349551764856c6b"
hostAwardedPixelsEvent = "0x03cab98018a5e38e0cf717d8bed481983eb400f6a1d9ccd34f87050c0f36a32a"
)

var eventProcessors = map[string](func(IndexerEvent)){
Expand Down Expand Up @@ -111,6 +112,7 @@ var eventProcessors = map[string](func(IndexerEvent)){
factionTemplateRemovedEvent: processFactionTemplateRemovedEvent,
chainFactionTemplateAddedEvent: processChainFactionTemplateAddedEvent,
chainFactionTemplateRemovedEvent: processChainFactionTemplateRemovedEvent,
hostAwardedPixelsEvent: processHostAwardedPixelsEvent,
}

var eventReverters = map[string](func(IndexerEvent)){
Expand Down Expand Up @@ -141,6 +143,7 @@ var eventReverters = map[string](func(IndexerEvent)){
factionTemplateRemovedEvent: revertFactionTemplateRemovedEvent,
chainFactionTemplateAddedEvent: revertChainFactionTemplateAddedEvent,
chainFactionTemplateRemovedEvent: revertChainFactionTemplateRemovedEvent,
hostAwardedPixelsEvent: revertHostAwardedPixelsEvent,
}

var eventRequiresOrdering = map[string]bool{
Expand Down Expand Up @@ -171,6 +174,7 @@ var eventRequiresOrdering = map[string]bool{
factionTemplateRemovedEvent: true,
chainFactionTemplateAddedEvent: true,
chainFactionTemplateRemovedEvent: true,
hostAwardedPixelsEvent: false,
}

const (
Expand Down
132 changes: 119 additions & 13 deletions backend/routes/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func InitTemplateRoutes() {
http.HandleFunc("/get-templates", getTemplates)
http.HandleFunc("/get-faction-templates", getFactionTemplates)
http.HandleFunc("/get-chain-faction-templates", getChainFactionTemplates)
http.HandleFunc("/build-template-img", buildTemplateImg)
http.HandleFunc("/add-template-img", addTemplateImg)
http.HandleFunc("/add-template-data", addTemplateData)
if !core.ArtPeaceBackend.BackendConfig.Production {
Expand Down Expand Up @@ -56,33 +57,44 @@ func hashTemplateImage(pixelData []byte) string {
return "00" + hashStr[2:]
}

func bytesToRGBA(colorBytes []byte) color.RGBA {
r := colorBytes[0]
g := colorBytes[1]
b := colorBytes[2]
return color.RGBA{r, g, b, 0xFF}
func hexToRGBA(colorBytes string) color.RGBA {
// Hex like "rrggbb"
r, err := strconv.ParseUint(colorBytes[0:2], 16, 8)
if err != nil {
return color.RGBA{}
}
g, err := strconv.ParseUint(colorBytes[2:4], 16, 8)
if err != nil {
return color.RGBA{}
}
b, err := strconv.ParseUint(colorBytes[4:6], 16, 8)
if err != nil {
return color.RGBA{}
}
return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
}

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

colors, err := core.PostgresQueryJson[ColorType]("SELECT hex FROM colors ORDER BY key")
colors, err := core.PostgresQuery[ColorType]("SELECT hex FROM colors ORDER BY key")
if err != nil {
return nil, err
}

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

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

for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
Expand All @@ -91,7 +103,7 @@ func imageToPixelData(imageData []byte) ([]byte, error) {
pixelData[y*width+x] = 0xFF
} else {
closestIndex := findClosestColor(rgba, palette)
pixelData[y*width+x] = byte(closestIndex)
pixelData[y*width+x] = closestIndex
}
}
}
Expand All @@ -114,7 +126,10 @@ func findClosestColor(target color.RGBA, palette []color.Color) int {
}

func colorDistance(c1, c2 color.RGBA) float64 {
return math.Sqrt(float64((c1.R-c2.R)*(c1.R-c2.R) + (c1.G-c2.G)*(c1.G-c2.G) + (c1.B-c2.B)*(c1.B-c2.B)))
r_diff := float64(int(c1.R) - int(c2.R))
g_diff := float64(int(c1.G) - int(c2.G))
b_diff := float64(int(c1.B) - int(c2.B))
return math.Sqrt(r_diff*r_diff + g_diff*g_diff + b_diff*b_diff)
}

type TemplateData struct {
Expand Down Expand Up @@ -179,6 +194,92 @@ func getChainFactionTemplates(w http.ResponseWriter, r *http.Request) {
routeutils.WriteDataJson(w, string(factionTemplates))
}

// curl -F "image=@<path to image>" http://localhost:8080/build-template-img?start=0
func buildTemplateImg(w http.ResponseWriter, r *http.Request) {
file, _, err := r.FormFile("image")
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read image")
return
}
defer file.Close()

startStr := r.URL.Query().Get("start")
start, err := strconv.Atoi(startStr)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid start position")
return
}

// Decode the image to check dimensions
img, _, err := image.Decode(file)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to decode image")
return
}
bounds := img.Bounds()
width, _ := bounds.Max.X-bounds.Min.X, bounds.Max.Y-bounds.Min.Y
file.Seek(0, 0)

fileBytes, err := io.ReadAll(file)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to read image data")

return
}

imageData, err := imageToPixelData(fileBytes)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to convert image to pixel data")
return
}

imageDataBytes := make([]byte, len(imageData))
for idx, val := range imageData {
imageDataBytes[idx] = byte(val)
}
hash := hashTemplateImage(imageDataBytes)

// Make file to store template data
if _, err := os.Stat("templates-build"); os.IsNotExist(err) {
err = os.Mkdir("templates-build", os.ModePerm)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to create templates directory")
return
}
}

filename := fmt.Sprintf("templates-build/template-%s.txt", hash)
newtemp, err := os.Create(filename)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to create image file")
return
}
defer file.Close()

// TODO: Hardcoded width
x := start % 512
y := start / 512
start_x := x
end_x := x + width
// Write image data to file
for _, pixel := range imageData {
pos := y*512 + x
// Convert byte to integer representation
if pixel != 0xFF {
_, err := newtemp.WriteString(fmt.Sprintf("%d %d\n", pos, int(pixel)))
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to write image data")
return
}
}
x++
if x >= end_x {
x = start_x
y++
}
}
}

func addTemplateImg(w http.ResponseWriter, r *http.Request) {
file, _, err := r.FormFile("image")
if err != nil {
Expand Down Expand Up @@ -216,7 +317,12 @@ func addTemplateImg(w http.ResponseWriter, r *http.Request) {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to convert image to pixel data")
return
}
hash := hashTemplateImage(imageData)

imageDataBytes := make([]byte, len(imageData))
for idx, val := range imageData {
imageDataBytes[idx] = byte(val)
}
hash := hashTemplateImage(imageDataBytes)

if _, err := os.Stat("templates"); os.IsNotExist(err) {
err = os.Mkdir("templates", os.ModePerm)
Expand Down
62 changes: 31 additions & 31 deletions configs/factions.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"id": 1,
"name": "Ducks Everywhere",
"icon": "$BACKEND_URL/faction-images/ducks-everywhere.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"leader": "0x5cf1e201ace78e436ec07748ddd7312db07a9d588e8b177ded3eb612daad80e",
"joinable": true,
"allocation": 1,
"links": {
Expand All @@ -18,80 +18,80 @@
},
{
"id": 2,
"name": "WASD",
"icon": "$BACKEND_URL/faction-images/wasd.png",
"name": "Realms",
"icon": "$BACKEND_URL/faction-images/realms.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"joinable": true,
"allocation": 1,
"links": {
"telegram": "https://t.me/art_peace_game/3",
"twitter": "https://twitter.com/WASD_0x",
"github": "",
"site": "https://bento.me/wasd"
"telegram": "https://t.me/art_peace_game/25",
"twitter": "https://x.com/LootRealms",
"github": "https://github.com/bibliothecaDAO",
"site": "https://realms.world"
},
"members": [
]
},
{
"id": 3,
"name": "Influence",
"icon": "$BACKEND_URL/faction-images/influence.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"name": "Wolf Pack League",
"icon": "$BACKEND_URL/faction-images/wpl.png",
"leader": "0x0179caf1b35202a0a544109cc04caf07a813755d1a15927e025ad874108b0543",
"joinable": true,
"allocation": 1,
"links": {
"telegram": "https://t.me/art_peace_game/4",
"twitter": "https://x.com/influenceth",
"telegram": "https://t.me/art_peace_game/26",
"twitter": "https://x.com/WPLSTARK",
"github": "",
"site": "https://www.influenceth.io/"
"site": "https://www.thewpl.xyz"
},
"members": [
]
},
{
"id": 4,
"name": "Ark Project",
"icon": "$BACKEND_URL/faction-images/ark-project.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"name": "zkCampus",
"icon": "$BACKEND_URL/faction-images/zk-campus.png",
"leader": "0x00Cca69dDF0833Bd6De5d0074949dC45820113fd6F4124a1C380304EDC4d79E9",
"joinable": true,
"allocation": 1,
"links": {
"telegram": "https://t.me/art_peace_game/5",
"twitter": "https://x.com/arkprojectnfts",
"telegram": "https://t.me/art_peace_game/27",
"twitter": "https://x.com/ZkCampus",
"github": "",
"site": "https://www.arkproject.dev/"
"site": "https://medium.com/@ZkCampus"
},
"members": [
]
},
{
"id": 5,
"name": "Everai",
"icon": "$BACKEND_URL/faction-images/everai.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"name": "Starknet Africa",
"icon": "$BACKEND_URL/faction-images/sn_africa.png",
"leader": "0x0091549c2fe52f07266a21228406131df81b39a7f4793cf23c8cb30d4c667850",
"joinable": true,
"allocation": 1,
"links": {
"telegram": "https://t.me/art_peace_game/6",
"twitter": "https://x.com/everai",
"telegram": "https://t.me/art_peace_game/12",
"twitter": "https://x.com/StarkNetAfrica",
"github": "",
"site": "https://www.everai.xyz/"
"site": "https://www.starknet.africa"
},
"members": [
]
},
{
"id": 6,
"name": "2077 Collective",
"icon": "$BACKEND_URL/faction-images/2077.png",
"name": "Influence",
"icon": "$BACKEND_URL/faction-images/influence.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"joinable": true,
"allocation": 1,
"links": {
"telegram": "https://t.me/art_peace_game/7",
"twitter": "https://x.com/2077Collective",
"telegram": "https://t.me/art_peace_game/4",
"twitter": "https://x.com/influenceth",
"github": "",
"site": "https://2077.xyz/"
"site": "https://www.influenceth.io/"
},
"members": [
]
Expand All @@ -100,7 +100,7 @@
"id": 7,
"name": "Argent",
"icon": "$BACKEND_URL/faction-images/argent.png",
"leader": "0x05bd7adfE8AfaA58300aDC72bF5584b191E236987Fe16A217b1a3e067869A0Aa",
"leader": "0x02775831D81d35d30Cd842511331b482E4275Cd1c8a34AE832aDb0D282307AFd",
"joinable": true,
"allocation": 1,
"links": {
Expand Down
Loading

0 comments on commit a3e2a9d

Please sign in to comment.