package block import ( "strconv" "strings" "gno.land/p/nt/avl" ) // personalWorldInstalled stores installed blocks in personal worlds // Key: worldId (e.g., "1", "42") // Value: map[chunkCoord]map[blockIndex]map[string]string // - chunkCoord: "{xIndex}_{zIndex}" (e.g., "0_0", "-1_2") // - blockIndex: block position within chunk (e.g., "100", "200") var personalWorldInstalled = avl.NewTree() // systemChunkInstalled stores installed blocks in system chunks // Key: chunkId (e.g., "sc-chunk-1", "alexandria") // Value: map[chunkCoord]map[blockIndex]map[string]string // - chunkCoord: "{xIndex}_{zIndex}" (e.g., "0_0", "-1_2") // - blockIndex: block position within chunk (e.g., "100", "200") var systemChunkInstalled = avl.NewTree() // mustGetInstalled gets installed block with panic func mustGetInstalled(position string) map[string]string { store := getStore(position) storeKey, chunkCoord, blockIndex := parsePosition(position) world, found := store.Get(storeKey) if !found { panic("block not found at position: " + position) } worldMap := world.(map[string]map[string]map[string]string) chunkMap, found := worldMap[chunkCoord] if !found { panic("block not found at position: " + position) } info, found := chunkMap[blockIndex] if !found { panic("block not found at position: " + position) } return info } // getInstalled gets installed block without panic (internal) func getInstalled(position string) (map[string]string, bool) { store := getStore(position) storeKey, chunkCoord, blockIndex := parsePosition(position) world, found := store.Get(storeKey) if !found { return nil, false } worldMap := world.(map[string]map[string]map[string]string) chunkMap, found := worldMap[chunkCoord] if !found { return nil, false } info, found := chunkMap[blockIndex] if !found { return nil, false } return info, true } // saveInstalled stores installed block info // Structure: blockId, content, option, installer, shape, state func saveInstalled(position string, blockID uint32, content string, option string, installer address, shape string, state string) map[string]string { store := getStore(position) storeKey, chunkCoord, blockIndex := parsePosition(position) blockIDStr := blockIDToString(blockID) info := map[string]string{ "blockId": blockIDStr, "content": content, "option": option, "installer": installer.String(), "shape": shape, "state": state, } // Get or create world map world, found := store.Get(storeKey) if !found { world = make(map[string]map[string]map[string]string) store.Set(storeKey, world) } worldMap := world.(map[string]map[string]map[string]string) // Get or create chunk map chunkMap, found := worldMap[chunkCoord] if !found { chunkMap = make(map[string]map[string]string) worldMap[chunkCoord] = chunkMap } // Set block info chunkMap[blockIndex] = info return info } // removeInstalled deletes installed block func removeInstalled(position string) { store := getStore(position) storeKey, chunkCoord, blockIndex := parsePosition(position) world, found := store.Get(storeKey) if !found { return } worldMap := world.(map[string]map[string]map[string]string) chunkMap, found := worldMap[chunkCoord] if !found { return } delete(chunkMap, blockIndex) // Remove chunk entry if no more blocks if len(chunkMap) == 0 { delete(worldMap, chunkCoord) } // Remove world entry if no more chunks if len(worldMap) == 0 { store.Remove(storeKey) } } // getStore returns the appropriate store based on position type func getStore(position string) *avl.Tree { if isPersonalWorld(position) { return personalWorldInstalled } if isSystemChunk(position) { return systemChunkInstalled } panic("unknown position type: " + position) } // parsePosition parses position into its components // Format: "type|worldId|owner|xIndex|zIndex|blockIndex" // Returns: (storeKey, chunkCoord, blockIndex) // Example: "personal|1|g1abc...|0|0|100" -> ("1", "0_0", "100") // Example: "system|sc-1|g1abc...|0|0|100" -> ("sc-1", "0_0", "100") func parsePosition(position string) (storeKey string, chunkCoord string, blockIndex string) { parts := strings.Split(position, "|") if len(parts) != 6 { panic("invalid position format: must be 'type|worldId|owner|xIndex|zIndex|blockIndex'") } // worldType := parts[0] // Used for store selection via getStore worldId := parts[1] // owner := parts[2] // Used for permission check, not storage xIndex := parts[3] zIndex := parts[4] blockIndex = parts[5] // storeKey: worldId only (type is handled by getStore) storeKey = worldId // chunkCoord: "{xIndex}_{zIndex}" chunkCoord = xIndex + "_" + zIndex return storeKey, chunkCoord, blockIndex } // isPersonalWorld checks if position is personal world func isPersonalWorld(position string) bool { return strings.HasPrefix(position, "personal|") } // isSystemChunk checks if position is system chunk func isSystemChunk(position string) bool { return strings.HasPrefix(position, "system|") } // extractPersonalWorldID extracts world ID from personal world position // Format: "personal|worldId|owner|xIndex|zIndex|blockIndex" // Panics if not personal world func extractPersonalWorldID(position string) uint32 { if !isPersonalWorld(position) { panic("not a personal world position: " + position) } parts := strings.Split(position, "|") if len(parts) != 6 { panic("invalid position format: " + position) } id, err := strconv.ParseUint(parts[1], 10, 32) if err != nil { panic("invalid world ID: " + parts[1]) } return uint32(id) } // extractSystemChunkID extracts chunk token ID from system chunk position // Format: "system|worldId|owner|xIndex|zIndex|blockIndex" // Returns: "worldId:xIndex_zIndex" (chunk NFT token ID format) // Panics if not system chunk func extractSystemChunkID(position string) string { if !isSystemChunk(position) { panic("not a system chunk position: " + position) } parts := strings.Split(position, "|") if len(parts) != 6 { panic("invalid position format: " + position) } return parts[1] + ":" + parts[3] + "_" + parts[4] }