2010-02-02 8 views
10

Estoy tratando de portar una biblioteca de juegos al iPhone. A diferencia de SDL, esta biblioteca no toma el control total de su función main(), se comunica a través de funciones de retorno rápido desde su propio código. Así, por ejemplo, pseudocódigo obvia:¿Cómo puedo eliminar UIApplicationMain de una aplicación de iPhone?

int main() { 
    library_init(); 
    // game init code here 
    while(we_have_not_quit_the_game) { 
    library_message_loop(); 
    library_init_render(); 
    // render stuff 
    library_end_render(); 
    // update game state 
    } 
    library_shutdown(); 
} 

iPhone hace que esto sea difícil, ya que requiere que se llama a una función UIApplicationMain que nunca regresa. Simplemente no hay forma de que pueda volver al código de usuario después de library_init() ;.

No estoy seguro de que sea necesario, existe NSRunLoop que supuestamente podría usarse para manejar los eventos. Sin embargo, no sé si UIApplicationMain hace algo más importante. (Tenga en cuenta que no tengo planes de usar archivos .nib, que es la única otra cosa que he encontrado que UIApplicationMain).

Tengo tres ideas reales que se me ocurren, pero todas son un gran esfuerzo de implementación, así que me gustaría saber si alguien tiene experiencia con esto antes de quemarme un día probando ideas condenadas.

  • En Init, genere un nuevo hilo, ejecute UIApplicationMain en ese hilo. O comunique todos los eventos a través de subprocesos (ugh) o simplemente ponga el subproceso UIApplicationMain en suspensión y use un CFRunLoop en el subproceso principal. Escuché que a UIApplicationMain no le gusta que se ejecute en un hilo diferente, sin embargo.
  • Ignore UIApplicationMain por completo, solo use NSRunLoop. ¿Me voy a estar perdiendo una importante configuración de iPhone? ¡Quién sabe!
  • Haz algo horrible con longjmp() para salir del código UIApplicationMain después de la instalación, reza para que no haga nada importante durante el desmontaje.

Sugerencias?

Respuesta

0

¿El objetivo es tomar esa función main() y hacer que funcione sin modificaciones en el iPhone?

Parece que no hay manera de aislar por completo a los usuarios de la biblioteca de pensar en la plataforma de iPhone; tendrán que lidiar con XCode para la firma de código y ese tipo de cosas.

Dado que, decirle a los usuarios que tienen que dividir su función principal() en unas pocas partes a las que puede llamar desde applicationDidFinishLaunching y desde un temporizador adecuado no parece que le moleste demasiado a alguien.

+1

Más o menos, sí. Más específicamente, prefiero dejar al usuario con control total sobre el ciclo del juego: mi motor construido sobre la biblioteca en cuestión ya hace algunas cosas poco convencionales en Windows y OSX, y me gustaría preservar esa originalidad. Honestamente, me gustaría brindar esto, no solo con el propósito de permitir main() sin modificaciones (aunque ciertamente es un buen beneficio), sino porque creo que es un diseño de API mejor y más útil para juegos que el evento -Modificado diseño iPhone parece gustarle. A veces solo necesitas hacer cosas locas. – ZorbaTHut

0

¿Por qué no iniciar el bucle del juego desde UIApplication? (Sí, no puede tenerlo en main(), pero eso no debería importar). Algunos de los mensajes de delegado son buenos candidatos.

+3

Básicamente, es porque las bibliotecas que intentan hacerse cargo de todo el entorno de ejecución me molestan y molestan a las otras personas involucradas en la biblioteca también. Sí, ciertamente hay formas de hacer que esto funcione mientras domina todo el entorno de ejecución. Prefiero evitar eso. – ZorbaTHut

5

¡Parece que estoy respondiendo mi propia pregunta aquí! No acepto mi respuesta hasta que haya podido probarla en un hardware real y obtenerla en la tienda de aplicaciones. Dicho esto, voy a mantener mi información más actualizada aquí, incluidas las opciones que no funcionó.

Idea # 1: Resulta que cada NSRunLoop es específico de subprocesos. Si creo un UIApplicationMain en un hilo separado, no recibe ningún mensaje. Como efecto secundario, esto hace que sea imposible determinar cuándo se ha terminado de inicializar, por lo que si hay algo que no sea seguro para los hilos, simplemente no funcionará. Es posible que pueda enviar un mensaje a través de los hilos para averiguar cuándo se ha terminado de inicializar, pero por ahora llamo a esto un callejón sin salida.

Idea # 2: UIApplicationMain hace muchas cosas sutiles. No estoy seguro de a qué se restringe, pero no pude hacer que nada funcionara sin la participación de UIApplicationMain. La idea n. ° 2 está en claro.

Idea # 3: La recepción de las señales del sistema operativo es importante: debe saber si hay una superposición de llamadas telefónicas o si está a punto de salir. Además de eso, algunos de los mensajes de configuración parecen vitales para iniciar la aplicación correctamente. No pude encontrar ningún método para mantener los mensajes enviados sin estar dentro de UIApplicationMain. Las únicas opciones que surgieron fueron NSRunLoop y CFRunLoop. Ninguno de los dos funcionó; los mensajes no llegaron como yo quería. Puede que no esté usando estos derechos, pero en cualquier caso, la Idea # 3 está fuera.

A estrenar idea # 4: Es posible usar setjmp/longjmp para corutinas falsas en C/C++. El truco consiste en establecer primero el puntero de la pila en algún valor que no bloquee nada importante, luego comenzar su segunda rutina, luego saltar hacia adelante y hacia atrás, simulando que tiene dos pilas. Las cosas se ponen un poco complicadas si su "segunda coroutine" decide regresar de su función principal, pero afortunadamente, UIApplicationMain nunca vuelve, así que esto no es un problema.

No sé si hay una manera de establecer el puntero de la pila de forma explícita en el hardware real, por ejemplo, en una porción de datos que asigné sobre la marcha. Afortunadamente, no importa. El iPhone tiene una pila de 1MB por defecto, que es lo suficientemente fácil para caber unas corutinas.

Lo que estoy haciendo actualmente es usar alloca() para empujar el puntero de la pila hacia delante en 768 kilobytes, y luego UIApplicationMain, luego usando setjmp/longjmp para rebotar entre mi "rutina de UI" y mi "rutina principal". Hasta ahora, esto está funcionando.

Advertencias:

  • Es imposible saber cuándo la "rutina de interfaz de usuario" no tiene mensajes de manejar, y cuando no tiene mensajes de manejar, se acaba de bloquear de forma indefinida hasta que ya no es el caso. Estoy resolviendo esto haciendo un temporizador que se dispara cada 0.1 milisegundos. Cada vez que se dispara el temporizador, abandono mi "rutina principal", hago un bucle de un solo juego, luego vuelvo a la "rutina UI" para otro tic del temporizador. La lectura de la documentación indica que no acumulará "llamadas de temporizador" indefinidamente. Parece que recibo el mensaje de "finalización" de forma apropiada, aunque no he podido probarlo a fondo todavía, y no he probado ningún otro mensaje importante. (Afortunadamente, solo hay cuatro mensajes en total, y uno de ellos está relacionado con la configuración.)

  • La mayoría de los sistemas operativos modernos no asignarán toda la pila a la vez. El iPhone es probablemente uno de estos. Lo que no sé es si golpear el puntero de la pila 3/4 de un meg adelante asignará todo "detrás", por así decirlo. Si es así, puedo estar desperdiciando 3/4 de un mego de RAM, lo cual, en el iPhone, es significativo. Esto podría ser manejado al empujar el puntero hacia delante una cantidad menor, pero esto realmente es cortejar el desastre del tamaño de la pila: efectivamente limita tu stack por más lejos que toques el puntero, y tendrás que resolverlo con anticipación. Algunos datos centinelas en la pila, junto con un buen monitoreo y un sistema de registro para problemas de tamaño de pila, probablemente puedan resolver esto, pero es un problema no trivial. (Alternativamente, si puedo encontrar la manera de ensuciar con el puntero de la pila directamente en el hardware nativo, puedo malloc()/new [] algunos kilobytes, apuntar el puntero de la pila y usarlo como mi nueva pila. Tendré que averiguar cuánto espacio necesita pero dudo que sea mucho, teniendo en cuenta que no está haciendo demasiado.)

  • Esto no se ha probado actualmente en el hardware real (le daría una semana o dos , Tengo otro proyecto para terminar primero.)

  • No tengo idea si Apple descubrirá lo que estoy haciendo y le pegará una pegatina gigante RECHAZADA cuando intente enviarla a la tienda de aplicaciones. Esto es, digamos, un poco fuera de sus intenciones para la API. Dedos cruzados.

Mantendré esta publicación actualizada, y la aceptaré oficialmente una vez que haya verificado que, ya sabes, funciona.

Actualización tardía: Me distraje por una variedad de otras cosas. Desde entonces, he tenido algunos cambios que me hacen mucho menos interesado en el desarrollo de Apple. Mi enfoque actual no mostró signos de no funcionando, pero en realidad no tengo la motivación para seguir desarrollándolo. ¡Lo siento! Si alguna vez cambio de parecer, lo actualizaré más, pero Outlook no es tan bueno.

+0

Casi positivo serás rechazado en esto. Bummer, pero las aplicaciones son rechazadas por usar un solo método fuera de la API estándar, por lo que subvertirlo de alguna manera probablemente será un no ir. – typeoneerror

+0

Ese es el truco, sin embargo, no estoy usando nada fuera de la API, cada función que estoy usando es 100% compatible y permitida. Solo estoy reorganizando la pila un poco. Debería ser completamente imposible detectar esto sin desmontar realmente la base de código. – ZorbaTHut

+1

FWIW, responder su propia pregunta está perfectamente bien y es una buena adición a SO (y práctica aceptada), si nadie más puede hacerlo y lo resuelve después de un tiempo. –

1

NSApplicationMain() lee Info.plist y hace cosas para la aplicación (como obtener el archivo inicial de punta) así que supongo que UIApplicationMain() hace lo mismo para iPhone (como obtener el estilo de barra de estado inicial, aumentar el valor predeterminado.png imagen en etc.). Eso no está expuesto en ninguna otra parte, por lo que las funciones que llama deberían ejecutarse para que la aplicación se inicie sin efectos secundarios. Tu única apuesta es hacer una ingeniería inversa de los mismos y copiarlos (y esperar que todo lo que hagan esté en el SDK público).

+0

¿Dónde podemos encontrar el código fuente de esta función? Tengo un problema similar, quiero escribir una aplicación para iPhone sin interfaz de usuario, y parece que la mayoría del SDK (NSFileManager, por ejemplo) no funciona sin UIApplicationMain ... ¿Alguna idea de cómo se puede lograr? –

Cuestiones relacionadas