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 assignFrenchAccent
que 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.
fuente
Respuestas:
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:
fuente
Almacene todo como un atributo (o decorador), con puntos de montaje. Tomemos una casa que el jugador ha diseñado como ejemplo:
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).
fuente
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:
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
Se convierte
fuente
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.
fuente
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 123456
tan 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.
fuente
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.
fuente
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.
fuente