package chunk import ( "net/url" "strconv" "strings" "gno.land/p/demo/tokens/grc721" "gno.land/p/jeronimoalbi/pager" "gno.land/p/nt/ufmt" "gno.land/r/akkadia/admin" ) // Render func Render(iUrl string) string { u, err := url.Parse(iUrl) if err != nil { return "404\n" } path := u.Path query := u.Query() switch { case path == "": return renderHome() case path == "chunk": id := query.Get("id") if id == "" { return renderChunkForm() } return renderChunk(iUrl, id) case path == "chunks": owner := query.Get("owner") if owner != "" { return renderChunksByOwner(iUrl, owner) } return renderChunksList(iUrl) case path == "worlds": return renderWorldsList(iUrl) case strings.HasPrefix(path, "worlds/") && strings.HasSuffix(path, "/chunks"): worldIDStr := strings.TrimPrefix(path, "worlds/") worldIDStr = strings.TrimSuffix(worldIDStr, "/chunks") worldID, err := strconv.ParseUint(worldIDStr, 10, 32) if err != nil { return "404\n" } return renderChunksByWorld(iUrl, uint32(worldID)) default: return "404\n" } } func renderHome() string { output := `# Chunk Welcome to the Chunk management system on Akkadia. ## What is Chunk? Chunk is an NFT-based land ownership system where each chunk represents a unique piece of land in the game world: * **GRC721 NFT**: Each chunk is a unique token with ownership and transferability * **World-based**: Chunks belong to specific worlds with coordinates (x, y) * **Metadata Storage**: Each chunk stores custom metadata for game state * **Role-based Access**: Permissions control who can mint, burn, and modify chunks ## How Chunks Work 1. **World Creation**: Admins create worlds with biome, name, slug, and seed 2. **Chunk Minting**: Authorized users mint chunks at specific coordinates 3. **Metadata Management**: Chunk owners can update metadata for game state 4. **Ownership Transfer**: Chunks can be transferred between users as NFTs ## Config ` output += ufmt.Sprintf("* **List Limit**: %d\n", listLimit) output += ufmt.Sprintf("* **Batch Limit**: %d\n", batchLimit) output += ` ## Stats ` output += ufmt.Sprintf("* **Total Chunks**: %d\n", TokenCount()) output += ufmt.Sprintf("* **Total Worlds**: %d\n", GetTotalWorldSize()) output += ` ## Quick Links * [Browse Chunks](/r/akkadia/chunk:worlds) (select a world first) ## Search by Owner ` return output } func renderChunkForm() string { return `# View Chunk [← Home](/r/akkadia/chunk:) ` } func renderChunk(iUrl string, id string) string { tokenID := grc721.TokenID(id) meta := GetMetadata(tokenID) if meta == nil { return ufmt.Sprintf("# Chunk Not Found\n\n[← Home](/r/akkadia/chunk:)\n\nChunk `%s` does not exist.\n", id) } u, _ := url.Parse(iUrl) query := u.Query() worldIDParam := query.Get("worldId") pageParam := query.Get("page") output := ufmt.Sprintf("# Chunk %s\n\n", id) output += ufmt.Sprintf("[[View on Explorer](%s/m/explorer/chunk?id=%s)]\n\n", admin.GetExplorerURL(), id) if worldIDParam != "" { backLink := "/r/akkadia/chunk:worlds/" + worldIDParam + "/chunks" if pageParam != "" { backLink += "?page=" + pageParam } output += ufmt.Sprintf("[← Back to World %s](%s)\n\n", worldIDParam, backLink) } else { output += "[← Home](/r/akkadia/chunk:)\n\n" } owner := OwnerOf(tokenID) output += ufmt.Sprintf("* **Owner**: [%s](/r/akkadia/chunk:chunks?owner=%s) [[View on Explorer](%s/m/explorer/player?address=%s)]\n", owner, owner, admin.GetExplorerURL(), owner) worldID := meta["worldId"] output += ufmt.Sprintf("* **World**: [%s](/r/akkadia/chunk:worlds/%s/chunks)\n", worldID, worldID) output += ufmt.Sprintf("* **Coordinates**: (%s, %s)\n", meta["x"], meta["y"]) output += ufmt.Sprintf("* **World Type**: %s\n", meta["worldType"]) output += ufmt.Sprintf("* **Hash**: %s\n", meta["hash"]) return output } // renderChunksList renders a list of chunks for a specific world with pagination // Requires worldId query parameter for efficient pagination func renderChunksList(iUrl string) string { u, _ := url.Parse(iUrl) worldIdStr := u.Query().Get("worldId") if worldIdStr == "" { return `# All Chunks Please select a world to view chunks: [Browse Worlds](/r/akkadia/chunk:worlds) ` } worldID, err := strconv.ParseUint(worldIdStr, 10, 32) if err != nil { return "404\n" } output := ufmt.Sprintf("# Chunks in World %d\n\n", worldID) output += "[← Back to Worlds](/r/akkadia/chunk:worlds)\n\n" chunkCount := GetWorldMetadataSize(uint32(worldID)) if chunkCount == 0 { output += "*No chunks in this world.*\n" return output } output += ufmt.Sprintf("**Total Chunks**: %d\n\n", chunkCount) pages, err2 := pager.New(iUrl, chunkCount, pager.WithPageSize(10)) if err2 != nil { return output + "Invalid page" } currentPage := (pages.Offset() / pages.PageSize()) + 1 chunks := ListMetadataByWorld(uint32(worldID), currentPage, pages.PageSize()) for _, chunk := range chunks { key := chunk["id"] output += ufmt.Sprintf("### [%s](/r/akkadia/chunk:chunk?id=%s) [[View on Explorer](%s/m/explorer/chunk?id=%s)]\n\n", key, key, admin.GetExplorerURL(), key) owner := OwnerOf(grc721.TokenID(key)) output += ufmt.Sprintf("* **Owner**: %s [[View on Explorer](%s/m/explorer/player?address=%s)] [[Chunks by Owner](/r/akkadia/chunk:chunks?owner=%s)]\n", owner, admin.GetExplorerURL(), owner, owner) for k, v := range chunk { output += ufmt.Sprintf("* **%s**: %s\n", k, v) } output += "\n---\n\n" } if pages.HasPages() { output += pager.Picker(pages) } return output } // renderChunksByWorld renders chunks belonging to a specific world with pagination func renderChunksByWorld(iUrl string, worldID uint32) string { output := ufmt.Sprintf("# Chunks in World %d\n\n", worldID) output += "[← Back to Worlds](/r/akkadia/chunk:worlds)\n\n" chunkCount := GetWorldMetadataSize(worldID) if chunkCount == 0 { output += "*No chunks found in this world.*\n" return output } output += ufmt.Sprintf("**Total Chunks**: %d\n\n", chunkCount) pages, err := pager.New(iUrl, chunkCount, pager.WithPageSize(10)) if err != nil { return output + "Invalid page" } currentPage := (pages.Offset() / pages.PageSize()) + 1 chunks := ListMetadataByWorld(worldID, currentPage, pages.PageSize()) for _, chunk := range chunks { key := chunk["id"] output += ufmt.Sprintf("### [%s](/r/akkadia/chunk:chunk?id=%s&worldId=%d&page=%d) [[View on Explorer](%s/m/explorer/chunk?id=%s)]\n\n", key, key, worldID, currentPage, admin.GetExplorerURL(), key) owner := OwnerOf(grc721.TokenID(key)) output += ufmt.Sprintf("* **Owner**: %s [[View on Explorer](%s/m/explorer/player?address=%s)] [[Chunks by Owner](/r/akkadia/chunk:chunks?owner=%s)]\n", owner, admin.GetExplorerURL(), owner, owner) for k, v := range chunk { output += ufmt.Sprintf("* **%s**: %s\n", k, v) } output += "\n---\n\n" } if pages.HasPages() { output += pager.Picker(pages) } return output } // renderChunksByOwner renders chunks owned by a specific address with pagination func renderChunksByOwner(iUrl string, owner string) string { output := ufmt.Sprintf("# Chunks by %s\n\n", owner) output += "[← Home](/r/akkadia/chunk:)\n\n" ownerAddr := address(owner) chunkCount := GetOwnerTokenSize(ownerAddr) if chunkCount == 0 { output += "*No chunks found for this owner.*\n" return output } output += ufmt.Sprintf("**Total Chunks**: %d\n\n", chunkCount) pages, err := pager.New(iUrl, chunkCount, pager.WithPageSize(10)) if err != nil { return output + "Invalid page" } currentPage := (pages.Offset() / pages.PageSize()) + 1 ownerChunks := ListMetadataByOwner(ownerAddr, currentPage, pages.PageSize()) for _, chunk := range ownerChunks { tid := chunk["id"] output += ufmt.Sprintf("### [%s](/r/akkadia/chunk:chunk?id=%s) [[View on Explorer](%s/m/explorer/chunk?id=%s)]\n\n", tid, tid, admin.GetExplorerURL(), tid) for k, v := range chunk { output += ufmt.Sprintf("* **%s**: %s\n", k, v) } output += "\n---\n\n" } if pages.HasPages() { output += pager.Picker(pages) } return output } // renderWorldsList renders a list of all worlds with pagination func renderWorldsList(iUrl string) string { output := `# All System Worlds [← Home](/r/akkadia/chunk:) ` if GetTotalWorldSize() == 0 { output += "*No worlds created yet.*\n" return output } pages, err := pager.New(iUrl, GetTotalWorldSize(), pager.WithPageSize(10)) if err != nil { return output + "Invalid page" } worlds.ReverseIterateByOffset(pages.Offset(), pages.PageSize(), func(key string, value interface{}) bool { world := value.(map[string]string) worldIDStr := world["id"] worldID, _ := strconv.ParseUint(worldIDStr, 10, 32) chunkCount := GetWorldMetadataSize(uint32(worldID)) output += ufmt.Sprintf("* **ID**: %s\n", worldIDStr) output += ufmt.Sprintf("* **Name**: %s\n", world["name"]) output += ufmt.Sprintf("* **Biome**: %s\n", world["biome"]) output += ufmt.Sprintf("* **Seed**: %s\n", world["seed"]) output += ufmt.Sprintf("* **Slug**: `%s` [[View on Explorer](%s/m/explorer/primary-realm?slug=%s)]\n", world["slug"], admin.GetExplorerURL(), world["slug"]) output += ufmt.Sprintf("* **Chunks**: %d [[View Chunks](/r/akkadia/chunk:worlds/%s/chunks)]\n", chunkCount, worldIDStr) output += "\n---\n\n" return false }) if pages.HasPages() { output += pager.Picker(pages) } return output }