Search Apps Documentation Source Content File Folder Download Copy Actions Download

storage.gno

5.98 Kb ยท 218 lines
  1package block
  2
  3import (
  4	"strconv"
  5	"strings"
  6
  7	"gno.land/p/nt/avl"
  8)
  9
 10// personalWorldInstalled stores installed blocks in personal worlds
 11// Key: worldId (e.g., "1", "42")
 12// Value: map[chunkCoord]map[blockIndex]map[string]string
 13//   - chunkCoord: "{xIndex}_{zIndex}" (e.g., "0_0", "-1_2")
 14//   - blockIndex: block position within chunk (e.g., "100", "200")
 15var personalWorldInstalled = avl.NewTree()
 16
 17// systemChunkInstalled stores installed blocks in system chunks
 18// Key: chunkId (e.g., "sc-chunk-1", "alexandria")
 19// Value: map[chunkCoord]map[blockIndex]map[string]string
 20//   - chunkCoord: "{xIndex}_{zIndex}" (e.g., "0_0", "-1_2")
 21//   - blockIndex: block position within chunk (e.g., "100", "200")
 22var systemChunkInstalled = avl.NewTree()
 23
 24// mustGetInstalled gets installed block with panic
 25func mustGetInstalled(position string) map[string]string {
 26	store := getStore(position)
 27	storeKey, chunkCoord, blockIndex := parsePosition(position)
 28
 29	world, found := store.Get(storeKey)
 30	if !found {
 31		panic("block not found at position: " + position)
 32	}
 33
 34	worldMap := world.(map[string]map[string]map[string]string)
 35	chunkMap, found := worldMap[chunkCoord]
 36	if !found {
 37		panic("block not found at position: " + position)
 38	}
 39
 40	info, found := chunkMap[blockIndex]
 41	if !found {
 42		panic("block not found at position: " + position)
 43	}
 44
 45	return info
 46}
 47
 48// getInstalled gets installed block without panic (internal)
 49func getInstalled(position string) (map[string]string, bool) {
 50	store := getStore(position)
 51	storeKey, chunkCoord, blockIndex := parsePosition(position)
 52
 53	world, found := store.Get(storeKey)
 54	if !found {
 55		return nil, false
 56	}
 57
 58	worldMap := world.(map[string]map[string]map[string]string)
 59	chunkMap, found := worldMap[chunkCoord]
 60	if !found {
 61		return nil, false
 62	}
 63
 64	info, found := chunkMap[blockIndex]
 65	if !found {
 66		return nil, false
 67	}
 68
 69	return info, true
 70}
 71
 72// saveInstalled stores installed block info
 73// Structure: blockId, content, option, installer, shape, state
 74func saveInstalled(position string, blockID uint32, content string, option string, installer address, shape string, state string) map[string]string {
 75	store := getStore(position)
 76	storeKey, chunkCoord, blockIndex := parsePosition(position)
 77	blockIDStr := blockIDToString(blockID)
 78
 79	info := map[string]string{
 80		"blockId":   blockIDStr,
 81		"content":   content,
 82		"option":    option,
 83		"installer": installer.String(),
 84		"shape":     shape,
 85		"state":     state,
 86	}
 87
 88	// Get or create world map
 89	world, found := store.Get(storeKey)
 90	if !found {
 91		world = make(map[string]map[string]map[string]string)
 92		store.Set(storeKey, world)
 93	}
 94	worldMap := world.(map[string]map[string]map[string]string)
 95
 96	// Get or create chunk map
 97	chunkMap, found := worldMap[chunkCoord]
 98	if !found {
 99		chunkMap = make(map[string]map[string]string)
100		worldMap[chunkCoord] = chunkMap
101	}
102
103	// Set block info
104	chunkMap[blockIndex] = info
105
106	return info
107}
108
109// removeInstalled deletes installed block
110func removeInstalled(position string) {
111	store := getStore(position)
112	storeKey, chunkCoord, blockIndex := parsePosition(position)
113
114	world, found := store.Get(storeKey)
115	if !found {
116		return
117	}
118
119	worldMap := world.(map[string]map[string]map[string]string)
120	chunkMap, found := worldMap[chunkCoord]
121	if !found {
122		return
123	}
124
125	delete(chunkMap, blockIndex)
126
127	// Remove chunk entry if no more blocks
128	if len(chunkMap) == 0 {
129		delete(worldMap, chunkCoord)
130	}
131
132	// Remove world entry if no more chunks
133	if len(worldMap) == 0 {
134		store.Remove(storeKey)
135	}
136}
137
138// getStore returns the appropriate store based on position type
139func getStore(position string) *avl.Tree {
140	if isPersonalWorld(position) {
141		return personalWorldInstalled
142	}
143	if isSystemChunk(position) {
144		return systemChunkInstalled
145	}
146	panic("unknown position type: " + position)
147}
148
149// parsePosition parses position into its components
150// Format: "type|worldId|owner|xIndex|zIndex|blockIndex"
151// Returns: (storeKey, chunkCoord, blockIndex)
152// Example: "personal|1|g1abc...|0|0|100" -> ("1", "0_0", "100")
153// Example: "system|sc-1|g1abc...|0|0|100" -> ("sc-1", "0_0", "100")
154func parsePosition(position string) (storeKey string, chunkCoord string, blockIndex string) {
155	parts := strings.Split(position, "|")
156	if len(parts) != 6 {
157		panic("invalid position format: must be 'type|worldId|owner|xIndex|zIndex|blockIndex'")
158	}
159	// worldType := parts[0] // Used for store selection via getStore
160	worldId := parts[1]
161	// owner := parts[2] // Used for permission check, not storage
162	xIndex := parts[3]
163	zIndex := parts[4]
164	blockIndex = parts[5]
165
166	// storeKey: worldId only (type is handled by getStore)
167	storeKey = worldId
168	// chunkCoord: "{xIndex}_{zIndex}"
169	chunkCoord = xIndex + "_" + zIndex
170	return storeKey, chunkCoord, blockIndex
171}
172
173// isPersonalWorld checks if position is personal world
174func isPersonalWorld(position string) bool {
175	return strings.HasPrefix(position, "personal|")
176}
177
178// isSystemChunk checks if position is system chunk
179func isSystemChunk(position string) bool {
180	return strings.HasPrefix(position, "system|")
181}
182
183// extractPersonalWorldID extracts world ID from personal world position
184// Format: "personal|worldId|owner|xIndex|zIndex|blockIndex"
185// Panics if not personal world
186func extractPersonalWorldID(position string) uint32 {
187	if !isPersonalWorld(position) {
188		panic("not a personal world position: " + position)
189	}
190
191	parts := strings.Split(position, "|")
192	if len(parts) != 6 {
193		panic("invalid position format: " + position)
194	}
195
196	id, err := strconv.ParseUint(parts[1], 10, 32)
197	if err != nil {
198		panic("invalid world ID: " + parts[1])
199	}
200	return uint32(id)
201}
202
203// extractSystemChunkID extracts chunk token ID from system chunk position
204// Format: "system|worldId|owner|xIndex|zIndex|blockIndex"
205// Returns: "worldId:xIndex_zIndex" (chunk NFT token ID format)
206// Panics if not system chunk
207func extractSystemChunkID(position string) string {
208	if !isSystemChunk(position) {
209		panic("not a system chunk position: " + position)
210	}
211
212	parts := strings.Split(position, "|")
213	if len(parts) != 6 {
214		panic("invalid position format: " + position)
215	}
216
217	return parts[1] + ":" + parts[3] + "_" + parts[4]
218}