package chunk import ( "chain" "chain/runtime" "strconv" "gno.land/p/nt/avl" "gno.land/p/nt/ufmt" ) const ( CreateWorldEvent = "CreateWorld" UpdateWorldEvent = "UpdateWorld" SetWorldPropertyEvent = "SetWorldProperty" ) var ( worlds = avl.NewTree() // worldID -> map[string]string slugIndex = make(map[string]uint32) // slug -> worldID nameIndex = make(map[string]uint32) // name -> worldID ) func CreateWorld(cur realm, id uint32, biomeName, name, slug string, seed int, options string) { caller := validateUser() assertIsAdmin(caller) validateWorldID(id) validateBiomeName(biomeName) validateName(name) validateSlug(slug) validateSeed(seed) // Check uniqueness if _, found := worlds.Get(worldIDToKey(id)); found { panic("world already exists: " + ufmt.Sprintf("%d", id)) } if _, found := nameIndex[name]; found { panic("name already exists: " + name) } if _, found := slugIndex[slug]; found { panic("slug already exists: " + slug) } height := ufmt.Sprintf("%d", runtime.ChainHeight()) world := map[string]string{ "id": ufmt.Sprintf("%d", id), "name": name, "slug": slug, "seed": ufmt.Sprintf("%d", seed), "biome": biomeName, "createdAt": height, "updatedAt": height, "options": options, } worlds.Set(worldIDToKey(id), world) slugIndex[slug] = id nameIndex[name] = id chain.Emit( CreateWorldEvent, "id", ufmt.Sprintf("%d", id), "biome", biomeName, "name", name, "slug", slug, "seed", ufmt.Sprintf("%d", seed), "options", options, ) } func UpdateWorld(cur realm, worldID uint32, biomeName, name, slug, options string) { caller := validateUser() assertIsAdmin(caller) world := mustGetWorld(worldID) odlBiomeName := world["biome"] oldName := world["name"] oldSlug := world["slug"] if len(biomeName) != 0 && biomeName != odlBiomeName { validateBiomeName(biomeName) world["biome"] = biomeName } if len(name) != 0 && name != oldName { validateName(name) if _, found := nameIndex[name]; found { panic("name already exists: " + name) } delete(nameIndex, oldName) nameIndex[name] = worldID world["name"] = name } if len(slug) != 0 && slug != oldSlug { validateSlug(slug) if _, found := slugIndex[slug]; found { panic("slug already exists: " + slug) } delete(slugIndex, oldSlug) slugIndex[slug] = worldID world["slug"] = slug } world["options"] = options world["updatedAt"] = ufmt.Sprintf("%d", runtime.ChainHeight()) // Emit event chain.Emit( UpdateWorldEvent, "id", ufmt.Sprintf("%d", worldID), "biome", biomeName, "name", name, "slug", slug, "options", options, ) } func GetWorld(worldID uint32) map[string]string { return mustGetWorld(worldID) } func GetWorldBySlug(slug string) map[string]string { worldID, found := slugIndex[slug] if !found { panic("world not found by slug: " + slug) } return mustGetWorld(worldID) } func GetTotalWorldSize() int { return worlds.Size() } func ListWorlds(page, count int) []map[string]string { if page < 1 { panic("page must be at least 1") } if count < 1 { panic("count must be at least 1") } if count > listLimit { panic("count exceeds listLimit") } var result []map[string]string offset := (page - 1) * count worlds.IterateByOffset(offset, count, func(_ string, value any) bool { result = append(result, value.(map[string]string)) return false }) return result } func SetWorldProperty(cur realm, worldID uint32, key string, value string) { caller := validateUser() assertIsAdmin(caller) if key == "id" || key == "seed" || key == "createdAt" || key == "updatedAt" { panic("reserved key: " + key) } world := mustGetWorld(worldID) world[key] = value chain.Emit( SetWorldPropertyEvent, "worldID", ufmt.Sprintf("%d", worldID), "key", key, "value", value, ) } func mustGetWorld(worldID uint32) map[string]string { value, found := worlds.Get(worldIDToKey(worldID)) if !found { panic("world not found: " + ufmt.Sprintf("%d", worldID)) } return value.(map[string]string) } // worldIDToKey converts a world ID to a padded string key for AVL tree func worldIDToKey(worldID uint32) string { s := strconv.FormatUint(uint64(worldID), 10) // Pad to 10 digits for proper AVL tree sorting for len(s) < 10 { s = "0" + s } return s } func validateBiomeName(biomeName string) { if len(biomeName) == 0 { panic("biome name cannot be empty") } } func validateName(name string) { if len(name) == 0 { panic("name cannot be empty") } if len(name) > 100 { panic("name too long (max 100)") } } func validateSlug(slug string) { if len(slug) == 0 { panic("slug cannot be empty") } if len(slug) > 50 { panic("slug too long (max 50)") } // Allowed characters: a-z, 0-9, hyphen, underscore for _, ch := range slug { if !((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' || ch == '_') { panic("slug contains invalid characters (allowed: a-z, 0-9, -, _)") } } // Cannot start or end with hyphen if slug[0] == '-' || slug[len(slug)-1] == '-' { panic("slug cannot start or end with hyphen") } } func validateSeed(seed int) { if seed <= 0 { panic("seed must be positive") } } func validateWorldID(id uint32) { if id == 0 { panic("world id must be positive") } }