Formas de gestionar los datos cambiantes del diseñador junto con los datos cambiantes del jugador

14

Tengo un juego en línea donde los jugadores pueden dar forma al mundo de alguna manera, por ejemplo. La vivienda de Ultima Online, donde puedes construir tus casas directamente en ciertas partes del mapa mundial. Estos son cambios que deberían persistir en el tiempo como parte del mundo persistente.

Al mismo tiempo, el equipo de diseño está agregando contenido nuevo y modificando contenido antiguo para mejorar y ampliar el juego para nuevos jugadores. Lo harán en un servidor de desarrollo primero durante las pruebas y luego tendrán que fusionar su trabajo con el "trabajo" de los jugadores en el servidor en vivo.

Suponiendo que solucionemos los problemas de diseño del juego, por ejemplo. los jugadores solo pueden construir en áreas designadas, por lo que nunca chocan geográficamente con las ediciones del diseñador: ¿cuáles son buenas maneras de manejar los datos u organizar las estructuras de datos para evitar conflictos cuando los nuevos datos del diseñador se fusionan con los nuevos datos del jugador?

Ejemplo 1: un jugador crea un nuevo tipo de elemento, y el juego le asigna el ID 123456. Las instancias de ese elemento se refieren a 123456. Ahora imagine que los diseñadores del juego tienen un sistema similar, y un diseñador crea un nuevo elemento también numerado 123456. ¿Cómo se puede evitar esto?

Ejemplo 2: alguien crea un mod popular que le da a todos tus dragones un acento francés. Incluye una secuencia de comandos con un nuevo objeto llamado assignFrenchAccentque utilizan para asignar los nuevos recursos de voz a cada objeto dragón. Pero está a punto de implementar su DLC "Napoleon vs Smaug" que tiene un objeto del mismo nombre. ¿Cómo puede hacer esto sin muchos problemas de servicio al cliente?

He pensado en las siguientes estrategias:

  • Puede usar 2 archivos / directorios / bases de datos separados, pero luego sus operaciones de lectura son significativamente complicadas. "Mostrar todos los elementos" debe realizar una lectura en la base de datos del diseñador y otra en la base de datos del reproductor (y de alguna manera aún debe distinguir entre los 2).
  • Puede usar 2 espacios de nombres diferentes dentro de una tienda, por ejemplo. usando cadenas como clave principal y prefijándolas con "DISEÑO:" o "JUGADOR:", pero la creación de esos espacios de nombres puede no ser trivial y las dependencias no están claras. (p. ej., en un RDBMS, es posible que no pueda usar cadenas de manera eficiente como claves primarias. Puede usar números enteros y asignar todas las claves primarias por debajo de un cierto número, por ejemplo, 1 millón, para que sean datos de diseño, y todo lo que esté por encima de ese punto sea datos del jugador. Pero esa información es invisible para el RDBMS y los enlaces de claves externas cruzarán la 'división', lo que significa que todas las herramientas y los scripts deben solucionarlo explícitamente).
  • Siempre puede trabajar en la misma base de datos compartida en tiempo real, pero el rendimiento puede ser deficiente y el riesgo de dañar los datos del jugador puede aumentar. Tampoco se extiende a juegos que se ejecutan en más de 1 servidor con datos mundiales diferentes.
  • ... alguna otra idea?

Se me ocurre que, aunque esto es principalmente un problema para los juegos en línea, los conceptos también pueden aplicarse a la modificación, donde la comunidad crea modificaciones al mismo tiempo que los desarrolladores parchan su juego. ¿Se utilizan aquí algunas estrategias para reducir la posibilidad de que el mod se rompa cuando salen nuevos parches?

También he etiquetado esto como "control de versiones" porque en un nivel eso es lo que es: 2 ramas de desarrollo de datos que necesitan fusionarse. Quizás algunas ideas puedan venir de esa dirección.

EDITAR: algunos ejemplos agregados anteriormente para ayudar a aclarar el problema. Estoy empezando a pensar que el problema es realmente el espacio de nombres, que podría implementarse en una tienda a través de claves compuestas. Eso simplifica la estrategia de fusión, al menos. Pero puede haber alternativas que no estoy viendo.

Kylotan
fuente
1
Me imagino que la respuesta a esto podría depender, al menos en parte, de qué tipo de datos están agregando los diseñadores y qué están agregando los jugadores.
lathomas64
Podría, pero estoy más interesado en soluciones genéricas para 2 partes que contribuyen a un solo almacén de datos.
Kylotan
1
Mucha gente se está perdiendo el punto de esta pregunta: no son los cambios de los jugadores los que están en conflicto: más bien las actualizaciones de contenido, etc., que rompen los diseños existentes. @Kylotan estoy en lo cierto?
Jonathan Dickinson
Es donde las actualizaciones de contenido para desarrolladores potencialmente chocan con las actualizaciones de contenido del reproductor. No estoy realmente interesado en soluciones de diseño de juegos (por ejemplo, solo dejar que los jugadores construyan en ciertos lugares), sino en soluciones de estructura de datos (por ejemplo, solo dejar que los jugadores creen cosas con identificaciones superiores a 1 millón).
Kylotan
2
No especificó, ¿espera hacer actualizaciones en tiempo real para un mundo en vivo? Una nota al margen: 2 partes que contribuyen a un único almacén de datos ES una base de datos, eso es lo que hacen las bases de datos, no se puede evitar ese hecho y sería una locura ignorar décadas de conocimiento sobre cómo evitar problemas con los datos compartidos.
Patrick Hughes

Respuestas:

2

Creo que las respuestas que proponen soluciones DB están saltando a una implementación específica sin comprender el problema. Las bases de datos no facilitan las fusiones, solo le brindan un marco en el que almacenar sus datos. Un conflicto sigue siendo un conflicto incluso si está en una base de datos. Y pagar es la solución de un hombre pobre al problema: funcionará, pero a un costo devastador para su usabilidad.

Lo que está hablando aquí cae dentro del modelo de desarrollo distribuido del problema. Creo que el primer paso es no pensar en los jugadores y diseñadores como tipos separados de creadores de contenido. Eso elimina una dimensión artificial de su problema que no afecta la solución.

Efectivamente tiene su línea principal: la versión canónica aprobada por el desarrollador. Es posible que (probablemente) también tenga otras sucursales: servidores en vivo donde las personas están desarrollando activamente y compartiendo mods. Se puede agregar contenido en cualquier sucursal. Crucialmente, sus diseñadores no son nada especial aquí: solo son creadores de contenido que viven en la casa (y puede buscarlos y golpearlos cuando se equivocan).

Entonces aceptar contenido generado por el usuario es un problema de fusión estándar. Debe volver a colocar sus cambios en la línea principal, fusionarlos, luego empujarlos hacia afuera nuevamente, o colocar los cambios de la línea principal en su rama y fusionarlos (dejando la línea principal "limpia" de las cosas generadas por el usuario). Como de costumbre, ir a su sucursal y arreglarlo allí es más amigable que pedirle a otras personas que retiren sus cambios y luego tratar de arreglarlo de forma remota.

Una vez que está trabajando con ese tipo de modelo, se aplican todos los procesos normales para evitar conflictos de fusión. Algunos de los más obvios:

  • Fomentar el uso liberal de espacios de nombres para limitar el contenido de un autor / mod / equipo en particular.
  • Cuando el contenido necesite interactuar, establezca convenciones claras de llamadas / uso, convenciones de nomenclatura y otras 'reglas' sueltas que guíen el desarrollo para que sea fácil fusionarlas. Proporcione herramientas que permitan a los creadores de contenido saber si están siguiendo esas reglas, idealmente integradas en la creación de contenido en sí.
  • Proporcione herramientas de informes / análisis para detectar posibles fallas de fusión antes de que ocurran. Arreglarlo después de la fusión es probablemente muy doloroso. Asegúrate de que se pueda verificar un fragmento de contenido en particular y que todo esté claro como listo para la fusión, de modo que la fusión sea indolora
  • Haga su fusión / integración robusta. Permitir retrocesos fáciles. Realice pruebas rigurosas del contenido combinado: si falla la prueba, ¡no lo combine! Itere ya sea su contenido o el suyo hasta que la fusión continúe limpiamente.
  • Evite usar ID de enteros incrementales para cualquier cosa (no tiene una forma confiable de repartirlos entre los creadores). Eso solo funciona en una base de datos porque la base de datos en sí misma es un proveedor canónico de ID para que nunca obtenga duplicados; sin embargo, también introduce un único punto de falla / carga en su sistema.
  • En su lugar, use GUID: cuesta más almacenarlos, pero son específicos de la máquina y, por lo tanto, no causarán colisiones. Alternativamente, use identificadores de cadena, esto es mucho más fácil de depurar / resolver, pero es más costoso para el almacenamiento y la comparación.
MrCranky
fuente
Lamentablemente, algo de esto no es útil para mi problema (por ejemplo, hacer que los jugadores sigan ciertas reglas, ya que todo esto debe hacerse automáticamente del lado del servidor) y no creo que sea práctico apoyar el grado de gestión de fusión y transaccional menciona la semántica, pero el enfoque general de asignación de ID únicos garantizados, tal vez GUID, es probablemente el más cercano a lo que utilizaré.
Kylotan
Ah, ya veo. Bueno, ya que controlas sus herramientas de construcción, al menos hacer cumplir esos enfoques amigables para la fusión (espacios de nombres, etc.) es algo que puedes hacer sin que los jugadores tengan que estar de acuerdo.
MrCranky
¿Qué haces si dos jugadores crean contenido duplicado? ¿O las instancias separadas de tu mundo de juego se tratan como únicas? En cuyo caso, tal vez sea un enfoque útil verificar automáticamente cada instancia única que conozca en su rama principal / principal para detectar conflictos que ocurrirán cuando elimine esos cambios a las instancias. Si no puede controlar a los jugadores, al menos puede advertir a su equipo interno que el trabajo que están haciendo entra en conflicto con la instancia X del mundo, al principio del desarrollo.
MrCranky
El concepto de espacios de nombres no es tanto el problema: ¡elegir espacios de nombres adecuados del espacio de nombres de todos los espacios de nombres posibles sí lo es! :) Y el contenido duplicado para mí no es un problema, son solo 2 instancias de algo que es equivalente. Lo importante es que no se produzcan fusiones o sobrescrituras perjudiciales. En cuanto a las comprobaciones automáticas de colisión, eso detiene el daño hecho en la escritura, pero no resuelve el problema de nomenclatura original. (Cambiar el nombre de las cosas para evitar una colisión puede no ser trivial, debido a las referencias cruzadas).
Kylotan
Ah sí, ya veo, no son los espacios de nombres en sí mismos, sino la elección del nombre. En ese caso, los GUID son probablemente la respuesta nuevamente: tener contenido efectivamente guardado en su propia pequeña área. Se puede dar un nombre decorativo, pero el juego usaría el GUID.
MrCranky
1

Almacene todo como un atributo (o decorador), con puntos de montaje. Tomemos una casa que el jugador ha diseñado como ejemplo:

o House: { Type = 105 } // Simple square cottage.
 o Mount point: South Wall:
  o Doodad: Chair { Displacement = 10cm }
   o Mount point: Seat:
    o Doodad: Pot Plant { Displacement = 0cm, Flower = Posies } // Work with me here :)
 o Mount point: North Wall:
  o Doodad: Table { Displacement = 1m }
    o Mount point: Left Edge:
     o Doodad: Food Bowl { Displacement = 20cm, Food = Meatballs}

Por lo tanto, cada entidad puede tener uno o más puntos de montaje: cada punto de montaje puede aceptar cero o más componentes. Estos datos se almacenarían con la versión en la que se guardaron, junto con las propiedades relevantes (como desplazamiento, etc. en mi ejemplo) - NoSQL probablemente encajaría muy bien aquí (Clave = ID de entidad, Valor = Binario serializado Datos).

Cada componente necesitaría poder 'actualizar' los datos antiguos de una versión anterior (nunca eliminar campos de datos serializados, solo 'anularlos'): esta actualización ocurre en el momento en que se carga (luego se volvería a almacenar de inmediato en La última versión disponible). Digamos que nuestra casa ha cambiado sus dimensiones. El código de actualización resolvería relativamente la distancia entre los muros norte y sur y alteraría proporcionalmente los desplazamientos de todas las entidades contenidas. Como otro ejemplo, nuestro plato de carne podría eliminar el campo 'Alimentos' y, en su lugar, obtener una 'Variedad' (Carne) y 'Receta' (Bolas). El script de actualización convertiría 'Meat Balls' en 'Meat', 'Balls'. Cada componente también debe saber cómo manejar los cambios en los puntos de montaje, p. Ej.

Todo esto deja exactamente un problema abierto: ¿qué sucede si dos objetos chocan entre sí (no sus puntos de montaje del contenedor lo protegen de eso)? Después de una actualización, debe verificar las colisiones e intentar resolverlas (separando las cosas, un poco como SAT). Si no puede encontrar la manera de resolver la colisión, retire uno de los objetos y colóquelo en un escondite, donde pueden comprar estos artículos eliminados (gratis) o venderlos (a precio completo); y obviamente notificar al jugador que la actualización rompió parte de su diseño, posiblemente con una función de "acercamiento" para que puedan ver el problema.

En última instancia, debe dejar cambios complejos en las manos de los jugadores (fallar rápidamente) ya que ningún algoritmo puede dar cuenta de la estética; simplemente debe poder darle al jugador el contexto en el que solía estar el elemento (para que puedan recordar, no solo aterrizar con todos estos elementos en su escondite y no saber dónde estaban).

Jonathan Dickinson
fuente
Esto se centra un poco demasiado en el posicionamiento de objetos, que no es realmente el problema clave que estoy tratando de resolver. Se trata más de tener identificadores únicos en conjuntos de datos concurrentes y la necesidad de poder fusionarlos sin ningún posible riesgo de conflicto. Agregué 2 ejemplos a mi publicación el otro día para intentar explicar un poco más.
Kylotan
1

Estoy tratando de asociar esto con algo que entiendo, así que estoy pensando en términos de Minecraft en este momento. Me imagino un servidor en vivo con jugadores que realizan cambios en tiempo real mientras los desarrolladores se ejecutan en un servidor de prueba arreglando / creando nuevo contenido.

Su pregunta casi parece 2 preguntas únicas:

  1. Cómo asegurar que las ID de objeto sean únicas
  2. Cómo asegurarse de que los espacios de nombres de script no colisionen

Intentaría resolver el n. ° 1 a través de un sistema de referencia temporal. Por ejemplo, cuando alguien crea un nuevo objeto, podría marcarse como volátil o temporal. Me imagino que todo el contenido nuevo creado en el servidor de prueba se marcaría como volátil (aunque también puede hacer referencia a contenido no volátil).

Cuando esté listo para traer nuevo contenido al servidor en vivo, su proceso de importación encontrará los objetos volátiles y les asignará ID de objetos del servidor en vivo que están establecidos en piedra. Esto es diferente a una importación / fusión directa porque necesita poder hacer referencia a objetos no volátiles existentes en caso de que necesite corregirlos o actualizarlos.

Para el n. ° 2, parece que realmente necesita tener algún nivel de transmutación de script intermedio que pueda cambiar el nombre de la función a un espacio de nombres único. es decir

assignFrenchAccent

Se convierte

_Z11assignFrenchAccent
Error 454
fuente
0

Si los archivos para los datos son texto en lugar de binarios y los diseñadores y los jugadores están modificando diferentes áreas, puede intentar una fusión SVN.

lathomas64
fuente
0

Creo que una base de datos / sistema de archivos replicado en todos los entornos con un procedimiento de 'salida' sería lo mejor.

Por lo tanto, cada vez que un diseñador quiera realizar alguna modificación en el mundo, desprotegerá / bloqueará todos los activos que quiera crear / modificar en todas las copias de la base de datos (desarrollo y producción), para que ningún otro jugador o diseñador pueda modificarla. . Luego trabajaría en la base de datos de desarrollo hasta que finalizara el nuevo diseño, y en ese momento los cambios se fusionarían con la base de datos de producción y esos activos se registrarían / ​​desbloquearían en todos los entornos.

Las ediciones de los jugadores funcionarían de la misma manera, excepto que las funciones de la base de datos / sistema de archivos se revertirían: funcionan en la base de datos de producción, y todas las actualizaciones se cargan en dev cuando finaliza.

El bloqueo de activos se puede limitar a las propiedades en las que desea garantizar que no haya conflictos: en el Ejemplo 1, se bloquearía ID 123456tan pronto como el jugador comience a crearlo, por lo que no se asignará esa identificación a los desarrolladores. En el ejemplo 2, sus desarrolladores habrían bloqueado el nombre del scriptassignFrenchAccent durante el desarrollo, por lo que el jugador tendría que elegir un nombre diferente al desarrollar su modificación (esa pequeña molestia puede reducirse mediante el espacio de nombres, pero eso por sí solo no evitará conflictos a menos que le dé a cada uno usuario / desarrollador un espacio de nombres específico, y luego tendrá el mismo problema con la gestión de los espacios de nombres). Esto significa que todo el desarrollo tendría que leerse desde una única base de datos en línea, pero todo lo que necesita de esa base de datos en estos ejemplos son los nombres de los objetos, por lo que el rendimiento no debería ser un problema.

En términos de implementación, tener una sola tabla con todas las claves y el estado del activo (disponible, bloqueado desde dev, bloqueado desde prod) sincronizado / accesible en tiempo real en todos los entornos debería ser suficiente. Una solución más compleja implementaría un sistema completo de control de versiones: puede usar un sistema existente como CVS o SVN si todos sus activos están en un sistema de archivos.

SkimFlux
fuente
Por lo general, no es práctico bloquear ningún dato a nivel mundial: los jugadores pueden estar editando el mundo solo a través del juego normal, y no querrás que esa jugada impida que los diseñadores puedan trabajar. Si permite bloqueos globales, entonces una operación de fusión es básicamente una operación de sobrescritura, lo cual es fácil, pero si no tiene bloqueos globales, ¿qué sucede entonces?
Kylotan
Como mencionó lathomas64, la respuesta dependería de qué tipo de datos esté hablando. Sin bloqueos globales, creo que tendrías que tener un sistema de versiones y un conjunto de reglas para resolver cualquier conflicto; estas reglas dependerían de los datos y los requisitos de juego. Una vez que los tenga, supongo que cada fusión se reduce a una simple operación de sobrescritura.
SkimFlux
0

Creo que el punto aquí es aceptar limpiamente su responsabilidad. 1) El servidor dice qué es aceptable actualmente y la API para acceder. La base de datos se está modificando, de acuerdo con ciertas reglas. 2) Los creadores pueden crear contenido, pero debe poder reproducirse después de las actualizaciones. Esto es puramente su responsabilidad: cualquier actualización debe ser capaz de analizar estructuras de datos antiguas, preferiblemente de la manera más limpia y fácil posible.

La idea de punto de montaje tiene méritos si está interesado en realizar un seguimiento de elementos y posiciones únicos dentro de una estructura maleable, especialmente si aceptamos que toda la estructura de 'hogar' de un jugador sufrirá un cambio dramático, y desea mantener eso pequeñas cosas deco en sus respectivos casilleros.

Es un problema enormemente complicado, ¡buena suerte! Probablemente no exista ninguna respuesta.

Karmington
fuente
0

No creo que esto tenga un gran problema ya que lo estás haciendo.

Simplemente sobreescribiría los mods creados por el usuario, con una advertencia que indica que "Mod X puede no funcionar correctamente con esta versión", deje que los creadores del mod cambien su trabajo. No creo que esta sea una expectativa poco realista de que las actualizaciones puedan deshabilitar ciertas modificaciones.

Lo mismo para el contenido creado por el usuario, solo haga una copia de seguridad y sobrescriba.

No tengo experiencia real en esto, solo hago sugerencias.

Woody Zantzinger
fuente
Creo que si fuera solo para modificaciones proporcionadas por el usuario, entonces estaría en lo cierto. Pero algunos juegos son explícitamente sobre el contenido creado por el usuario, por lo que no puedes simplemente destruirlo.
Kylotan
Luego, deje espacio en el sistema para el contenido que pueda agregar más tarde. Si está utilizando números de identificación, reserve 1-1000. O si los usuarios pueden nombrar sus activos, no permita que los usuarios comiencen el nombre con "FINAL-" o algo así (resérvelo para sus propios activos). EDITAR: o mejor aún, hacerlo a la inversa, obligando al contenido del usuario a un rango o prefijo
Woody Zantzinger