Desarrollo / ROBLOX
Copia de inventario en Luau: por qué los cambios con table.clone afectan al original
Compara asignación, copia superficial y registros copiados por separado con un inventario ficticio pequeño. Un ejemplo verificado muestra cómo editar un borrador manteniendo la cantidad original y dónde terminan las capacidades de este modelo de dos niveles.
Define el comportamiento previsto del borrador #
Imagina un inventario didáctico con dos materiales: copper contiene count=3 y wood contiene count=1. Quieres abrir un borrador de edición y poner su cantidad de cobre en 9, conservando 3 en los datos originales. Hay que comprobar dos valores juntos; una etiqueta en un solo panel no basta. Escribe primero el par esperado, original=3 y draft=9. Después elige el método de copia que debería garantizar exactamente esa separación.
Los materiales y campos label y equipped son inventados para el ejercicio. Son tablas ordinarias en memoria, no el inventario real de un jugador Roblox. Verificamos la lógica autónoma en Luau sin conectar GUI, servidor, RemoteEvent ni guardados. Esa frontera permite describir honestamente el ejemplo en vez de presentar un sistema terminado de objetos. Una comprobación exitosa de la copia no demuestra entrega de recursos en una experience publicada.
Distingue un nombre nuevo de una tabla nueva #
Escribir draft=original da otro nombre a la misma tabla. La guía oficial explica las tablas como valores referenciados; una asignación ordinaria no crea una instantánea independiente. Cambiar draft.copper.count se puede observar también mediante original.copper.count. El problema no es la representación del texto. Ambas variables conducen a los mismos datos, por lo que la separación requerida falta antes de intervenir una interfaz o de que un jugador vea un panel.
Para la primera comprobación, crea datos nuevos y utiliza ese segundo nombre para poner la cantidad en 7. En nuestra prueba autónoma, alias==source es verdadero y source.copper.count pasa a 7. Es un ejemplo verificado de objeto compartido. No lo llames copia solo por tener otro nombre de variable. Antes del siguiente caso, crea otro conjunto con cantidad inicial 3 para que una modificación previa no distorsione la comparación.
Comprende el límite de table.clone #
table.clone crea una tabla exterior nueva, pero copia su contenido superficialmente. Si un valor era otra tabla, esa referencia sigue compartida. Por eso draft~=original puede ser verdadero al mismo tiempo que draft.copper==original.copper. No hay contradicción: los contenedores exteriores difieren y un registro anidado es común. Un editor debe comprobar la profundidad donde realiza su cambio, en vez de considerar una referencia exterior diferente como independencia completa.
En la segunda prueba autónoma, cambiar la cantidad anidada mediante una copia superficial a 8 también deja el original en 8. Sin embargo, quitar wood de la copia exterior no elimina wood del contenedor inicial. Las acciones dan resultados distintos porque afectan niveles diferentes. No las combines en copiar funciona o no funciona. Indica campo y nivel examinados y conserva ambas observaciones, en lugar de descartar el resultado que contradice una conclusión demasiado general.
| Método | ¿Registro copper compartido? |
|---|---|
| Asignación | Sí |
| table.clone | Sí |
| Registros copiados | No |
Compara referencias y valores por separado #
El valor count responde qué cantidad está guardada. Comparar tablas con == responde si son la misma tabla en este ejemplo. Números iguales no prueban independencia. Dos registros distintos pueden contener 3 antes de cambiar cualquiera. Comprueba primero la estructura de referencias, después la acción y ambos valores resultantes. De lo contrario, una relación compartida no deseada queda oculta hasta empezar la edición, cuando ya se modificaron datos originales.
Prepara un registro breve: tablas exteriores diferentes, registros copper diferentes y edición del borrador sin cambio en el origen. Para la dirección inversa, cambia wood en el original y compara el borrador. Probar ambos sentidos resulta útil cuando un objeto se actualiza mientras editas otro. Nuestra prueba realiza esas comparaciones, pero no mide comportamiento de red simultáneo ni establece que se hayan eliminado todos los conflictos posibles dentro de una experience.
Copia registros para una forma de datos explícita #
La función original copyInventoryDraft admite un diccionario con identificadores de objetos que sean cadenas no vacías. Cada valor es una tabla simple de registro cuyos campos con nombre contienen solo cadenas, números o booleanos. La función copia primero el contenedor exterior y después cada registro por separado. Para esta forma, cantidad, etiqueta y equipped=false conservan sus valores, mientras las tablas de registros ya no comparten identidad con el original.
El contrato está limitado deliberadamente. Metatablas, una tabla metadata anidada, funciones y claves numéricas de campos quedan fuera y se rechazan expresamente. No es una copia profunda universal de datos arbitrarios. Ese límite vuelve comprensible y comprobable el ejemplo sin prometer manejo de ciclos ni preservación de grafos complejos de referencias. Si tu estructura difiere, descríbela y decide las reglas de copia antes de quitar comprobaciones para lograr que el código se ejecute.
-- Teaching model only: string item IDs -> plain records -> scalar fields.
-- This is not a universal deep-copy function or a server validation layer.
local function copyInventoryDraft(original)
assert(type(original) == "table", "Expected an inventory table")
assert(getmetatable(original) == nil, "Root metatables are outside this model")
local draft = table.clone(original)
for id, record in pairs(original) do
assert(type(id) == "string" and id ~= "", "Expected a nonempty item ID")
assert(type(record) == "table", "Expected an item record")
assert(getmetatable(record) == nil, "Record metatables are outside this model")
for field, value in pairs(record) do
assert(type(field) == "string", "Expected named record fields")
local valueType = type(value)
assert(valueType == "string" or valueType == "number" or valueType == "boolean",
"Only scalar record fields are supported")
end
draft[id] = table.clone(record)
end
return draft
end
return copyInventoryDraftEjecuta el ejemplo pequeño de uso #
Para Luau autónomo, guarda el ayudante como table-copy-example.luau y el ejemplo de uso como table-copy-usage.luau en la misma carpeta. El segundo archivo obtiene la función mediante require con ruta relativa, crea dos registros didácticos y cambia draft.copper.count a 9. Esa disposición de ejecución está verificada. Si adaptas la idea a un ModuleScript de Roblox, revisa acceso al módulo y entorno aparte; un archivo con ruta relativa no constituye una configuración completa de Studio.
La salida verificada es GUIDEBOOK_TABLE_COPY_USAGE original=3 draft=9. Las assertions comparan además ambas cantidades y mantienen equipped=false. El artículo presenta el ayudante limitado completo y su aplicación corta. Ninguno dibuja una interfaz ni concede objetos. Si tu salida difiere, revisa el archivo ejecutado, de dónde procede la función y si los datos originales habían cambiado previamente. Conserva ese contexto, en vez de confiar únicamente en una etiqueta conocida de salida.
local copyInventoryDraft = require("./table-copy-example")
local original = {
copper = {count = 3, label = "Copper", equipped = false},
wood = {count = 1, label = "Wood", equipped = true},
}
local draft = copyInventoryDraft(original)
draft.copper.count = 9
assert(original.copper.count == 3)
assert(draft.copper.count == 9)
assert(draft.copper.equipped == false)
print("GUIDEBOOK_TABLE_COPY_USAGE original=" .. original.copper.count .. " draft=" .. draft.copper.count)Comprueba modificaciones desde ambos lados #
Tras copiar los registros separadamente, editar la cantidad de cobre del borrador deja el original en 3. Cambiar después wood en el origen no modifica el valor previo de wood en el borrador. Insertar y quitar entradas exteriores del borrador tampoco inserta ni elimina entradas originales. Son observaciones de la forma de datos probada. Aportan más que un mensaje de llamada exitosa a la función, que no dice nada sobre independencia de ediciones posteriores.
Si dos identificadores originales apuntan a un solo registro, el ayudante crea una copia separada para cada identificador. Nuestra prueba confirma que los registros nuevos son distintos y modificar uno no modifica el otro. La relación compartida inicial no se conserva. Es una característica intencional del ayudante. Un proyecto donde ese registro compartido tenga significado necesita otra política; no describas el resultado como copia exacta de cualquier grafo de objetos o relación de identidad.
No confundas freeze con independencia profunda #
La biblioteca oficial explica que table.freeze cambia la tabla recibida en su propio lugar y no congela automáticamente valores anidados. Nuestra prueba congela el inventario exterior y comprueba que copper sigue siendo modificable. Cambiar su count continúa siendo posible. Por tanto, un contenedor exterior inmutable no demuestra protección de todos los datos que contiene ni su separación respecto a otras referencias. Son propiedades diferentes que necesitan observaciones más específicas.
table.clone de una tabla congelada devuelve una copia exterior no congelada, mientras el registro anidado continúa compartido en la copia superficial. Lo comprobamos aparte y después usamos el ayudante limitado para crear registros independientes. No utilices una marca frozen como prueba de todas las propiedades del resultado. Decide primero si necesitas copiar, mantener referencias comunes o impedir cambios. Son requisitos distintos y deben comprobarse mediante acciones distintas, no deducirse del mismo indicador.
Conserva los límites de la verificación autónoma #
Las pruebas abarcan asignación, copia superficial, independencia de registros de dos niveles, false, diccionario vacío, registros originales compartidos y formas explícitamente no admitidas. El comportamiento del contenedor exterior congelado se comprueba aparte. No generalices a cualquier estructura recursiva: el ayudante no recorre ese grafo. Rechazar un registro más profundo forma parte del contrato, no es un fallo universal de Luau o Roblox ni prueba de una biblioteca general para copiar grafos.
Copiar en memoria no demuestra autoridad del jugador para editar un inventario real ni sustituye validación del servidor. Tampoco guarda progreso entre sesiones. Una interfaz necesita pruebas propias de actualización, abandono del borrador y recepción de datos nuevos; persistencia necesita otro protocolo. No se realizaron para este artículo. Entrega estos límites junto con la salida verificada a otro desarrollador, para que el ayudante no parezca un sistema de juego completo.
| Caso | Resultado |
|---|---|
| Editar borrador | Original conserva3 |
| Editar original | Borrador no cambia |
| Objeto más profundo | Rechazado por contrato |
| GUI y guardados | Todavía sin probar |
Elige la copia para una acción concreta #
El registro final debe identificar el nivel copiado, las referencias todavía compartidas y los cambios realmente aislados. Para el ejercicio, original=3 y draft=9 corresponden a la tarea de edición elegida. Si solo necesitas una lista nueva de referencias a objetos existentes, una copia superficial puede ser intencional. Define la acción y su efecto esperado antes de elegir un método, en lugar de aplicar automáticamente la copia más compleja a cada conjunto de datos.
Los registros, ayudante y pruebas originales están disponibles para aprender y adaptar. Conserva con ellos la forma de datos y las limitaciones; añadir un campo anidado exige revisar el contrato y repetir las comprobaciones pertinentes. No prometas mayor velocidad, ahorro de memoria o reparación de un inventario real sin mediciones y pruebas de integración. Consulta los detalles actuales de table.clone y table.freeze en fuentes oficiales y confirma una implementación concreta ejecutándola realmente.
Fuentes originales
Luau — Standard LibraryRoblox Creator Hub — Tables
