Hoy, cuando Node.js está en pleno apogeo, ya podemos usarlo para hacer todo tipo de cosas. Hace algún tiempo, el anfitrión de UP participó en el evento Geek Song. Durante este evento, tenemos la intención de crear un juego que permita que las "personas con cabeza" se comuniquen más. La función central es la interacción en tiempo real de la persona del concepto del Partido LAN. La carrera de los geeks fue solo 36 horas lamentablemente cortas, requiriendo que todo sea rápido y rápido. Bajo esa premisa, la preparación inicial parece un poco "natural". Solución de aplicación multiplataforma Elegimos Node-Webkit, que es bastante simple y cumple con nuestros requisitos.
Según los requisitos, nuestro desarrollo puede llevarse a cabo por separado de acuerdo con los módulos. Este artículo describe específicamente el proceso de desarrollo de espaciadores (nuestro marco de juego multijugador en tiempo real), incluida una serie de exploraciones e intentos, así como soluciones a algunas restricciones en la plataforma Node.js y WebKit en sí, y la propuesta de soluciones.
Empezando
Mirada espacial
Al principio, el diseño de la espaciadora definitivamente fue impulsado por la demanda. Esperamos que este marco pueda proporcionar las siguientes funciones básicas:
Puede distinguir un grupo de usuarios basados en habitaciones (o canales)
Capaz de recibir instrucciones de los usuarios en el grupo de recopilación
Al igualar entre cada cliente, los datos del juego se pueden transmitir con precisión de acuerdo con el intervalo especificado
Puede eliminar el impacto de la latencia de la red tanto como sea posible
Por supuesto, en las últimas etapas de codificación, proporcionamos espaciadores más funciones, incluida la detección del juego, generar números aleatorios consistentes entre cada cliente, etc. (por supuesto, estos pueden ser implementados por sí mismos de acuerdo con los requisitos, y no es necesario usar espaciadores, un marco que funciona en el nivel de comunicación, que es más del nivel de comunicación).
API
La espaciadores se divide en dos partes: delantero y trasero. El trabajo requerido por el servidor incluye mantener una lista de habitaciones y proporcionar las funciones de crear y unir una habitación. Nuestras API de clientes se ven así:
Spaceroom.connect (dirección, devolución de llamada) Conéctese al servidor
Spaceroom.createroom (devolución de llamada) Crear una habitación
Spacerom.JoRoom (Roomid) Únete a una habitación
Spaceroom.on (evento, devolución de llamada) escucha eventos
...
Después de que el cliente se conecta al servidor, recibe varios eventos. Por ejemplo, un usuario en una habitación puede recibir un evento en el que se une un nuevo jugador, o un evento donde comienza el juego. Le damos al cliente un "ciclo de vida", que estará en uno de los siguientes estados en cualquier momento:
Puede obtener el estado actual del cliente a través de Spaceroom.State.
Usar un marco del lado del servidor es relativamente simple. Si usa el archivo de configuración predeterminado, puede ejecutar el marco del lado del servidor directamente. Tenemos un requisito básico: el código del servidor puede ejecutarse directamente en el cliente sin la necesidad de un servidor separado. Los jugadores que han jugado PS o PSP deben saber de qué estoy hablando. Por supuesto, también es excelente ejecutar en un servidor especial.
La implementación del código lógico es breve aquí. La primera generación de espaciadores completó la función de un servidor de socket, que mantiene una lista de habitaciones, incluido el estado de la habitación, y las comunicaciones de tiempo del juego correspondientes a cada habitación (colección de instrucciones, transmisión de deseos, etc.). Para una implementación específica, consulte el código fuente.
Algoritmo sincrónico
Entonces, ¿cómo podemos hacer que las cosas se muestren entre cada cliente sean consistentes en tiempo real?
Esta cosa suena interesante. Piénselo cuidadosamente, ¿qué necesitamos que el servidor nos ayude a entregar? Naturalmente, pensará en lo que puede causar inconsistencias lógicas entre varios clientes: instrucciones del usuario. Dado que los códigos que tratan con la lógica del juego son los mismos, dadas las mismas condiciones, el código ejecuta el mismo resultado. La única diferencia son los diversos comandos de jugadores recibidos durante el juego. Correcto, necesitamos una forma de sincronizar estas instrucciones. Si todos los clientes pueden obtener la misma instrucción, entonces todos los clientes pueden tener el mismo resultado de la operación.
Los algoritmos de sincronización de los juegos en línea son todo tipo de escenarios extraños y aplicables. Spacerom utiliza algoritmos de sincronización similares al concepto de bloqueo de cuadros. Dividamos la línea de tiempo en intervalos uno por uno, y cada intervalo se llama cubo. Bucket se usa para cargar instrucciones y el lado del servidor mantiene. Al final de cada período de tiempo, el servidor transmite el balde a todos los clientes. Después de que el cliente obtiene el cubo, las instrucciones se recuperan y luego se ejecutan después de la verificación.
Para reducir el impacto del retraso de la red, cada instrucción recibida por el servidor del cliente se entregará al cubo correspondiente de acuerdo con un determinado algoritmo, y los siguientes pasos se siguen específicamente:
Deje que Order_Start sea la hora del comando llevado por el comando, y t es la hora de inicio del cubo donde se encuentra el orden_start.
Si t + retraso_time <= tiempo de inicio del cubo que actualmente está recopilando la instrucción, entregue el comando al cubo que actualmente está recolectando la instrucción, de lo contrario continúa el paso 3
Entregar las instrucciones al cubo correspondiente de t + demandado_time
Donde Delay_Time es el tiempo de retraso del servidor acordado, que puede tomarse como el retraso promedio entre los clientes. El valor predeterminado en la espaciadores es 80, y el valor predeterminado de la longitud del cubo es 48. Al final de cada período de tiempo, el servidor transmite este cubo a todos los clientes y comienza a recibir instrucciones para el siguiente cubo. El cliente controla el error de tiempo dentro de un rango aceptable cuando realiza automáticamente la coincidencia en la lógica en función del intervalo de cubo recibido.
Esto significa que en circunstancias normales, el cliente recibirá un cubo enviado desde el servidor cada 48 ms. Cuando el momento en que se necesita procesar el cubo, el cliente lo manejará en consecuencia. Suponiendo que el cliente FPS = 60, recibirá un cubo cada 3 cuadros más o menos, y la lógica se actualizará de acuerdo con este cubo. Si el balde no se ha recibido después de que el tiempo ha expirado debido a las fluctuaciones de la red, el cliente detiene la lógica del juego y espera. A la vez dentro de un cubo, la lógica se puede actualizar utilizando el método LERP.
En el caso de Delay_time = 80, Bucket_Size = 48, cualquiera de las instrucciones se retrasará al menos en la ejecución de 96 ms. Cambie estos dos parámetros, por ejemplo, en el caso de Delay_time = 60, Bucket_Size = 32, cualquiera de las instrucciones se retrasará al menos 64 ms.
Un incidente sangriento causado por un temporizador
Mirando todo, nuestro marco debe tener un temporizador preciso cuando se esté ejecutando. Ejecute la transmisión del cubo bajo un intervalo fijo. Por supuesto, primero pensamos en usar SetInterval (), pero el siguiente segundo nos dimos cuenta de lo poco confiable que es esta idea: el Naughty SetInterval () parece tener errores muy graves. Y lo que es tan malo es que cada error se acumulará, causando consecuencias cada vez más graves.
Por lo tanto, pensamos inmediatamente en usar SetTimeOut () para que nuestra lógica sea aproximadamente estable alrededor del intervalo especificado corrigiendo dinámicamente el tiempo de la siguiente llegada. Por ejemplo, este tiempo setTimeOut () es 5 ms menos de lo esperado, por lo que lo dejaremos 5 ms con anticipación la próxima vez. Sin embargo, los resultados de la prueba no son satisfactorios, y esto no es lo suficientemente elegante sin importar cómo lo mire.
Entonces necesitamos cambiar nuestro pensamiento. ¿Es posible hacer que SetTimeout () expire lo más rápido posible, y luego verificamos si la hora actual ha alcanzado el tiempo objetivo? Por ejemplo, en nuestro bucle, usando SetTimeout (devolución de llamada, 1) para seguir revisando el tiempo, lo que parece una buena idea.
Temporizador decepcionante
Inmediatamente escribimos un código para probar nuestras ideas, y los resultados fueron decepcionantes. En la última versión estable de node.js (V0.10.32) y la plataforma Windows, ejecute este código:
La copia del código es la siguiente:
var sum = 0, count = 0;
función test () {
var ahora = date.now ();
setTimeOut (function () {
var diff = date.now () - ahora;
suma += diff;
contar ++;
prueba();
});
}
prueba();
Después de un período de tiempo, ingrese la suma/cuenta en la consola y puede ver un resultado, similar a:
La copia del código es la siguiente:
> suma / recuento
15.624555160142348
¡¿Qué?! ¡Quiero un intervalo de 1 ms, pero me dices que el intervalo promedio real es de 15.625 ms! Esta imagen es simplemente demasiado hermosa. Hicimos la misma prueba en Mac y el resultado fue de 1,4 ms. Entonces estábamos confundidos: ¿Qué demonios es esto? Si fuera fanático de Apple, podría haber concluido que Windows era demasiado basura y renunció a Windows. Afortunadamente, era un ingeniero frontal riguroso, así que comencé a seguir pensando en este número.
Espera, ¿por qué este número es tan familiar? ¿Este número será demasiado similar al intervalo máximo del temporizador en Windows? Inmediatamente descargué un Clockres para las pruebas, y después de ejecutar la consola, obtuve los siguientes resultados:
La copia del código es la siguiente:
Intervalo máximo del temporizador: 15.625 ms
Intervalo mínimo del temporizador: 0.500 ms
Intervalo del temporizador actual: 1.001 ms
¡Efectivamente! Mirando el manual Node.js, podemos ver una descripción de SetTimeOut como esta:
El retraso real depende de factores externos como la granularidad del temporizador del sistema operativo y la carga del sistema.
Sin embargo, los resultados de la prueba muestran que este retraso real es el intervalo máximo del temporizador (tenga en cuenta que el intervalo del temporizador actual del sistema es de solo 1.001 ms), lo que es inaceptable en cualquier caso. La fuerte curiosidad nos impulsa a mirar a través del código fuente de nodo.js para ver la verdad.
Error en node.js
Creo que la mayoría de ustedes y yo tenemos una cierta comprensión del mecanismo de bucle uniforme de Node.js. Al observar el código fuente de la implementación del temporizador, podemos comprender aproximadamente el principio de implementación del temporizador. Comencemos con el bucle principal del bucle de eventos:
La copia del código es la siguiente:
while (r! = 0 && loop-> stop_flag == 0) {
/* Actualizar el tiempo global*/
uv_update_time (bucle);
/* Verifique si el temporizador expira y ejecuta la devolución de llamada del temporizador correspondiente*/
uv_process_timers (bucle);
/* Llame a las devoluciones de llamada inactivas si no es nada que hacer. */
if (loop-> pending_reqs_tail == null &&
bucle-> endgame_handles == null) {
/* Evitar que el bucle de eventos salga*/
uv_idle_invoke (bucle);
}
uv_process_reqs (bucle);
uv_process_endgames (bucle);
uv_prepare_invoke (bucle);
/* Recopilar eventos IO*/
(*Poll) (Loop, Loop-> Idle_handles == NULL &&
bucle-> Pending_reqs_tail == NULL &&
bucle-> endgame_handles == null &&
! loop-> stop_flag &&
(Loop-> Active_Handles> 0 ||
! ngx_queue_empty (& loop-> activo_reqs)) &&
! (MODE & UV_RUN_NOWAIT));
/* setimmediate () etc*/
uv_check_invoke (bucle);
r = uv__loop_alive (bucle);
if (mode & (uv_run_once | uv_run_nowait))
romper;
}
El código fuente de la función uv_update_time es el siguiente: (https://github.com/joyent/libuv/blob/v0.10/src/win/timer.c))))
La copia del código es la siguiente:
void uv_update_time (uv_loop_t* bucle) {
/* Obtenga la hora actual del sistema*/
DWord ticks = getTickCount ();
/ * La suposición se hace de que grande_integer.quadpart tiene el mismo tipo */
/* bucle-> tiempo, que resulta ser. ¿Hay alguna forma de afirmar esto? */
Grande_integer* time = (gran_integer*) & loop-> time;
/* Si el temporizador ha envuelto, agregue 1 a su DWORD de alto orden. */
/ * uv_poll debe asegurarse de que el temporizador nunca pueda desbordarse más que */
/* Una vez entre dos llamadas UV_UPDate_Time posteriores. */
if (ticks <Time-> LowPart) {
tiempo-> HighPart += 1;
}
TIME-> LowPart = Ticks;
}
La implementación interna de esta función utiliza la función getTickCount () de Windows para establecer la hora actual. En pocas palabras, después de llamar a la función SetTimeOut, después de una serie de luchas, el temporizador interno- Due se establecerá en el tiempo de bucle actual + Tiempo de espera. En el bucle de eventos, primero actualice la hora del bucle actual a través de UV_UPDATE_TIME, y luego verifique si el temporizador expira en UV_Process_Times. Si es así, ingrese al mundo de JavaScript. Después de leer todo el artículo, el bucle del evento es más o menos como este proceso:
Actualizar el tiempo global
Revise el temporizador. Si el temporizador expira, ejecute la devolución de llamada.
Verifique la cola de reqs y ejecute la solicitud de espera
Ingrese la función de encuesta para recopilar eventos IO. Si llega un evento IO, agregue la función de procesamiento correspondiente a la cola de reqs para su ejecución en el siguiente bucle de eventos. Dentro de la función de la encuesta, se llama a un método del sistema para recopilar eventos IO. Este método hará que el proceso bloquee hasta que llegue un evento IO o se alcanza el tiempo de tiempo de espera establecido. Cuando se llama a este método, el tiempo de tiempo de espera se establece en el momento en que expira el temporizador más reciente. Significa que los eventos de IO se recopilan mediante el bloqueo y el tiempo de bloqueo máximo es el tiempo final del siguiente temporizador.
Código fuente de una de las funciones de la encuesta en Windows:
La copia del código es la siguiente:
void estático uv_poll (uv_loop_t* bucle, int block) {
DWORD bytes, tiempo de espera;
Tecla ULONG_PTR;
Superpuesto* superpuesto;
uv_req_t* req;
if (block) {
/* Tome el tiempo de vencimiento del temporizador más reciente*/
TIMEOUT = UV_GET_POLL_TIMEOUT (bucle);
} demás {
tiempo de espera = 0;
}
GetqueedcompletionStatus (loop-> IOCP,
& bytes,
&llave,
& superpuesto,
/* A lo máximo de bloqueo hasta que expire el siguiente temporizador*/
se acabó el tiempo);
if (superpuesto) {
/ * El paquete fue dequeuado */
req = uv_overlapped_to_req (superpuesto);
/* Inserte eventos IO en la cola*/
uv_insert_pending_req (bucle, req);
} else if (getLasterRor ()! = wait_timeout) {
/ * Error grave */
uv_fatal_error (getLasterRor (), "getqueedcompletionStatus");
}
}
Siguiendo los pasos anteriores, suponiendo que establecemos un temporizador de tiempo de espera = 1MS, la función de la encuesta bloqueará como máximo 1 ms y luego recuperará después de la recuperación (si no hay eventos IO durante el período). Cuando continúe ingresando el bucle de eventos, UV_UPDATE_TIME actualizará la hora y luego UV_Process_Timers encontrará que nuestro temporizador expira y ejecutará la devolución de llamada. Entonces, el análisis preliminar es que el UV_UPDATE_TIME tiene un problema (la hora actual no se actualiza correctamente), o la función de encuesta espera 1MS y luego se recupera. Hay un problema con este 1Ms esperando.
Mirando MSDN, sorprendentemente descubrimos una descripción de la función GetTickCount:
La resolución de la función GetTickCount se limita a la resolución del temporizador del sistema, que generalmente está en el rango de 10 millones de segundos a 16 millones de segundos.
¡La precisión de GetTickCount es muy difícil! Suponga que la función de encuesta bloquea correctamente el tiempo de 1 ms, pero la próxima vez que se ejecute UV_UPDATE_TIME, ¡el tiempo de bucle actual no se actualiza correctamente! Por lo tanto, nuestro temporizador no se consideró expirado, por lo que la encuesta esperó otros 1 ms e ingresó al siguiente bucle de eventos. Hasta que GetTickCount finalmente se actualice correctamente (el llamado 15.625MS se actualiza una vez), se actualiza el tiempo actual del bucle y se considera que nuestro temporizador expira en UV_Process_Timers.
Pedir ayuda a Webkit
Este código fuente de Node.js es muy indefenso: usó una función de tiempo con baja precisión y no hizo nada. Pero inmediatamente pensamos que, dado que usamos Node-Webkit, además de Node.js 'SetTimeout, también tenemos SetTimeout de Chromium. Escriba un código de prueba y use nuestro navegador o nodo-Webkit para ejecutar: http://marks.lrednight.com/test.html#1 (# seguido del número indica el intervalo a medir). El resultado es el siguiente:
Según las especificaciones de HTML5, el resultado teórico debe ser que los primeros 5 resultados son 1 ms, y los siguientes resultados son 4 ms. Los resultados que se muestran en el caso de prueba comienzan desde la tercera vez, lo que significa que los datos en la tabla deberían ser teóricamente 1 ms durante las primeras tres veces, y los resultados posteriores son 4 ms. El resultado tiene ciertos errores, y según las regulaciones, el resultado teórico más pequeño que podemos obtener es 4 ms. Aunque no estamos satisfechos, obviamente es mucho más satisfactorio que el resultado de Node.js. Tendencia de curiosidad fuerte, echemos un vistazo al código fuente de cromo para ver cómo se implementa. (https://chromium.googlesource.com/chromium/src.git/+/38.0.2125.101/base/time/time_win.cc)
Primero, Chromium usa la función TimeGetTime () para determinar la hora actual del bucle. Al observar MSDN, puede encontrar que la precisión de esta función se ve afectada por el intervalo de temporizador actual del sistema. En nuestra máquina de prueba, es teóricamente los 1.001ms mencionados anteriormente. Sin embargo, por defecto, el intervalo del temporizador es su valor máximo (15.625 ms en la máquina de prueba), a menos que la aplicación modifique el intervalo del temporizador global.
Si sigue las noticias en la industria de TI, debe haber visto tales noticias. ¡Parece que nuestro cromo ha establecido el intervalo del temporizador muy pequeño! ¿Parece que no tenemos que preocuparnos por el intervalo del temporizador del sistema? No seas demasiado feliz demasiado temprano, tal reparación nos dio un golpe. De hecho, este problema se ha solucionado en Chrome 38. ¿Deberíamos usar la reparación del nodo anterior? Obviamente, esto no es lo suficientemente elegante y nos impide usar una versión más realizada de Chromium.
Mirando aún más el código fuente de Chromium, podemos encontrar que cuando hay un temporizador y el tiempo de espera del tiempo de espera <32 ms, Chromium cambiará el intervalo de temporizador global del sistema para lograr un temporizador con una precisión de menos de 15.625 ms. (Ver código fuente) Al comenzar el temporizador, se habilitará algo llamado HighResolutionTimermanager. Esta clase llamará a la función EnableHighResolutionTimer en función del tipo de potencia del dispositivo actual. Específicamente, cuando el dispositivo actual use batería, llamará a EnableHighResolutionTimer (FALSE), y True se pasará cuando se use Power. La implementación de la función EnableHighResolutionTimer es la siguiente:
La copia del código es la siguiente:
TIEMPO VOCAL :: EnableHighResolutionTimer (Bool Enable) {
Base :: Autolock Lock (g_high_res_lock.get ());
if (g_high_res_timer_enabled == enable)
devolver;
g_high_res_timer_enabled = enable;
if (! g_high_res_timer_count)
devolver;
// Desde G_HIGH_RES_TIMER_COUNT! = 0, un activadohighresolutionTimer (verdadero)
// fue llamado que llamado TimebeginPeriod con G_HIGH_RES_TIMER_ENABLED
// con un valor que es el opuesto de | Enable |. Con esa información nosotros
// Llame a TimeendPeriod con el mismo valor utilizado en TimebeginPeriod y
// Por lo tanto, deshacer el efecto de período.
if (habilitar) {
TimeEndPeriod (kMintimerIntervallowResms);
TimebeginPeriod (kMintimerIntervalhighResms);
} demás {
TimeEndPeriod (kMintimerIntervalhighResms);
TimebeginPeriod (kMintimerIntervallowResms);
}
}
donde, kMintimerIntervallowResms = 4 y kMintimerIntervalhighResms = 1. TimebeginPeriod y TimeendPeriod son funciones proporcionadas por Windows para modificar el intervalo del temporizador del sistema. Es decir, cuando se conecta a la fuente de alimentación, el intervalo de temporizador más pequeño que podemos obtener es 1 ms, mientras que cuando se usa la batería, es 4 ms. Dado que nuestro bucle llama continuamente SetTimeOut, de acuerdo con la especificación W3C, el intervalo mínimo también es de 4 ms, por lo que me siento aliviado, esto tiene poco impacto en nosotros.
Otro problema de precisión
Volviendo al principio, encontramos que los resultados de la prueba muestran que el intervalo de SetTimeOut no es estable a los 4 ms, sino que fluctúa continuamente. Los resultados de las pruebas http://marks.lrednight.com/test.html#48 también muestran que los intervalos superan entre 48 ms y 49ms. La razón es que en el bucle de eventos de Chromium y Node.js, la precisión de la llamada de la función de Windows que espera el evento IO se ve afectada por el temporizador del sistema actual. La implementación de la lógica del juego requiere la función SoldingAnimationFrame (actualizando constantemente el lienzo), lo que puede ayudarnos a establecer el intervalo del temporizador en al menos kMintimerIntervallowResms (porque necesita un temporizador de 16 ms, que desencadena el requisito de un temporizador de alta precisión). Cuando la máquina de prueba usa energía, el intervalo del temporizador del sistema es 1 ms, por lo que el resultado de la prueba tiene un error de ± 1 ms. Si su computadora no ha cambiado el intervalo del temporizador del sistema y ejecuta la prueba #48 anterior, Max puede alcanzar 48+16 = 64ms.
Usando la implementación de SetTimeOut de Chromium, podemos controlar el error de SetTimeOut (FN, 1) a aproximadamente 4 ms, mientras que el error de SetTimeOut (FN, 48) puede controlar el error de SetTimeOut (FN, 48) a aproximadamente 1 m. Entonces, tenemos un nuevo plan en nuestras mentes, lo que hace que nuestro código se vea así:
La copia del código es la siguiente:
/ * Obtenga la decoración del intervalo máximo */
Var decoración = getMaxIntervalDeviation (bucketSize); // bucketssize = 48, desviación = 2;
function gameloop () {
var ahora = date.now ();
if (anteriorbucket + bucketSize <= ahora) {
Anteriorbucket = ahora;
dologic ();
}
if (anteriorbucket + bucketsize - date.now ()> decoración) {
// espera 46 ms. El retraso real es inferior a 48 ms.
setTimeout (gameloop, bucketSize - diseño);
} demás {
// ocupado esperando. Use setimmediate en lugar de proceso.nexttick porque el primero no bloquea los eventos de IO.
setimmediate (gameloop);
}
}
El código anterior nos permite esperar un tiempo con un error menor que bucket_size (definición de bucket_size) en lugar de igualar directamente un bucket_size. Incluso si el error máximo ocurre en el retraso de 46 ms, de acuerdo con la teoría anterior, el intervalo real es inferior a 48 ms. El resto del tiempo usamos el método de espera ocupado para asegurarnos de que nuestro Gameloop se ejecute bajo un intervalo con suficiente precisión.
Si bien resolvimos el problema hasta cierto punto con el cromo, obviamente esto no es lo suficientemente elegante.
¿Recuerdas nuestra solicitud inicial? Nuestro código del lado del servidor debería poder ejecutarse directamente en una computadora con un entorno Node.js sin el cliente Node-Webkit. Si ejecuta el código anterior directamente, el valor de la definición es al menos 16 ms, lo que significa que en cada 48 ms, tenemos que esperar 16 m. La tasa de uso de la CPU aumentó.
Sorpresa inesperada
Es tan molesto. ¿Nadie notó un error tan grande en Node.js? La respuesta realmente nos hace contentos. Este error se ha solucionado en la versión V.0.11.3. También puede ver los resultados modificados al ver directamente la rama maestra del código libuv. El enfoque específico es agregar un tiempo de espera a la hora actual del bucle después de que la función de la encuesta esté esperando la finalización. De esta manera, incluso si GetTickCount no reaccionó, aún agregamos este tiempo de espera después de que la encuesta estaba esperando. Entonces el temporizador puede expirar suavemente.
En otras palabras, el problema que se ha trabajado duro durante mucho tiempo se ha resuelto en V.0.11.3. Sin embargo, nuestros esfuerzos no fueron en vano. Porque incluso si se elimina la función GetTickCount, la función de la encuesta en sí se ve afectada por el temporizador del sistema. Una solución es escribir el complemento Node.js para cambiar los intervalos de los temporizadores del sistema.
Sin embargo, la configuración inicial para nuestro juego esta vez no está libre de servidores. Después de que el cliente crea una habitación, se convierte en un servidor. El código del servidor puede ejecutarse en el entorno Node-WebKit, por lo que la prioridad de los problemas de temporizador en los sistemas de Windows no es la más alta. Después de la solución que dimos anteriormente, los resultados son suficientes para satisfacernos.
final
Después de resolver el problema del temporizador, nuestra implementación del marco básicamente no estará más obstaculizada. Brindamos soporte de WebSocket (en entornos HTML5 puros) y personalizamos el protocolo de comunicación para lograr el soporte de socket de mayor rendimiento (en entornos de nodo-webkit). Por supuesto, las funciones de espaciadores eran relativamente simples al principio, pero a medida que se propuso la demanda y aumentó el tiempo, estamos mejorando gradualmente este marco.
Por ejemplo, cuando encontramos que cuando necesitamos generar números aleatorios consistentes en nuestro juego, agregamos esta función a la espaciación. Al comienzo del juego, Spacerom distribuirá semillas de números aleatorios. La espaciadora del cliente proporciona un método para usar la aleatoriedad de MD5 para generar números aleatorios con la ayuda de semillas de números aleatorios.
Parece bastante aliviado. También aprendí mucho en el proceso de escribir tal marco. Si está interesado en la separación, también puede participar en ella. Creo que el espacio para el espacio para los puños y los pies usará sus puños y pies en más lugares.