Los strings también son mala gente

Started by Arcontus, May 27, 2018, 01:22:56 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Arcontus

Hola chicos,
tengo un problema muy extraño con mi proyecto. Debido a su funcionamiento en determinados momentos un enemigo puede recibir múltiples impactos y por cada uno muestro un registro flotante de este en pantalla. De esta manera hay momentos donde pueden juntarse muchos textos en pantalla. El caso es que todo parece ir bien, pero al llevar unos 20 minutos de ejecución me da crashes aleatorios y no siempre.

Sospecho que el problema pueda originarse con la gestión de strings y me he hecho un programilla para hacer un uso intensivo de strings y pese a que no he conseguido que el programa reviente, si que aparecen resultados inesperados.

Este es el código:import "mod_key"
import "mod_video"
import "mod_text"
import "mod_map"
import "mod_string"
import "mod_rand"
import "mod_say"


PROCESS creaTexto(int miX, int miY, string cadena)
PRIVATE
    int txtid;
    int temporizador;
    string miCadena;
END
BEGIN
    txtid = -1;
    x = miX;
    y = miY;
    miCadena = cadena;
    temporizador = 360;
    txtid = write_string(0, x, y, 0, &miCadena);

    while(temporizador > 0)
        temporizador--;
        move_text(txtid, x, y);
        FRAME;
    END
OnExit:
    if (txtid >= 0) delete_text(txtid); END
    //say(cadena);
END

PROCESS main()
PRIVATE
    int indice, contador, contador2, txtid;
    string miCadena, micadena2;

END
BEGIN
    set_fps(200,0);
    set_mode(800,600,32, mode_window);
    x = 30;
    y = 550;
    txtid = write_string(0, 30,550, 0, &micadena2);
    while(!(key(_ESC)))
            contador2++;
            for (indice = 0; indice < 100; indice++)
                contador++;
                miCadena = "Hola caracola: " + contador;
                miCadena2 = "Total: " + contador;
                creaTexto(rand(10,200), rand(10,500), miCadena);
            END
            if (contador2%800 == 0)
                if (txtid >= 0)
                    delete_text(txtid);
                    txtid = write_string(0, 30,550, 0, &micadena2);

                END
            END
            if (key(_q))
                if (txtid >= 0) delete_text(txtid); txtid = -1; END
            END
            if (txtid < 0)
                txtid = write_string(0, 30,550, 0, &micadena2);
            else
                move_text(txtid, x, y);
            END
        FRAME;
    END
END


El programa trata de pintar "hola caracola" en procesos independientes y aqui es donde esta el fallo, hay una segunda cadena "micadena2" dentro del main que debería tener el valor "Total: N" donde N es el numero de strings pintadas. Lo que me sucede es que al pasar unos pocos segundos miCadena2, ¡vale lo mismo que miCadena! ¿¡WTF!?
Ni dandole a la Q para que repinte el string se arregla el problema, por lo que parece que el problema esté en la cadena y no en el write.

¿Alguna alma caritativa puede probar el código con su versión de Bennu y comentar el resultado?

Y una última cosa es que tras unos segundos, si no repinto la string miCadena2 esta se borra por "arte de magia". Para hacer la prueba comentad la linea 67 "move_text(txtid, x, y);" ¿Esto es normal y deseable?

5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Futu-block

Si, se borra automaticamente cuando le dá la gana...

SplinterGU

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

#3
aca esta el problema


                if (txtid >= 0) delete_text(txtid); END


debe ser


                if (txtid >= 0) delete_text(txtid); txtid = -1; END


el tema es que borras txtid pero nunca reseteas el id, entonces el texto desaparece, y el sistema intenta mover el texto a x,y, al no haber nada, no hace nada, el txtid que se intenta usar no existe... pero llega un momento en que el sistema reusa ese id para otro write_string y de ahi hace el move de un nuevo texto creado por creaTexto, al lugar de donde estaba "Total ..."

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

#4
Hola SplinterGU,
si, he visto el fallo, bien visto. De todas maneras sigue teniendo un comportamiento extraño, el valor de miCadena2 no es el que le correspondería. Actualizo el código de arriba.
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

no se que valor incorrecto te da, yo llegue a ver Total: 33000, y lo corte porque me canse...

da mas informacion

en un nuevo post, no edites, porque no se que es nuevo y que es viejo, no tengo tanta memoria...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

micadena2 tiene el valor correcto, usar write_text + move no es lo adecuado para probar esto...

usa say, pero solo de micadena2... vas a ver que siempre tiene el mismo valor...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Quote from: SplinterGU on May 29, 2018, 02:52:13 AM
no se que valor incorrecto te da, yo llegue a ver Total: 33000, y lo corte porque me canse...

da mas informacion

en un nuevo post, no edites, porque no se que es nuevo y que es viejo, no tengo tanta memoria...
Hola SplinterGU,
tienes razón de que quizás no expliqué bien la casuistica. El caso es que el contenido de miCadena2 cambia (en mi caso al sobrepasar los 33000) pero no a Total: N sino a Hola Caracola: N. Y esto no me deja nada tranquilo hasta no saber exactamente porque.
Adjunto dos imagenes para que se vea el resultado que a mi me devuelve. AM.png es el resultado esperado, pero en AM 001.png se ve como ha cambiado el téxto de miCadena2 de manera "inexplicable".
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

Arcontus

#8
Modifico el código para dejarlo más simplificado donde se observa el mismo problema que comentaba:
import "mod_key"
import "mod_video"
import "mod_text"
import "mod_map"
import "mod_string"
import "mod_rand"
import "mod_say"


PROCESS creaTexto(int miX, int miY, string cadena)
PRIVATE
    int txtid;
    int temporizador;
    string miCadena;
END
BEGIN
    txtid = -1;
    x = miX;
    y = miY;
    miCadena = cadena;
    temporizador = 360;
    txtid = write_string(0, x, y, 0, &miCadena);

    while(temporizador > 0)
        temporizador--;
        move_text(txtid, x, y);
        FRAME;
    END
OnExit:
    if (txtid >= 0) delete_text(txtid); END
    //say(cadena);
END

PROCESS main()
PRIVATE
    int indice, contador, contador2, txtid;
    string miCadena, micadena2;

END
BEGIN
    set_fps(200,0);
    set_mode(800,600,32, mode_window);
    x = 30;
    y = 550;
    txtid = write_string(0, 30,550, 0, &micadena2);
    while(!(key(_ESC)))
            contador2++;
            for (indice = 0; indice < 100; indice++)
                contador++;
                miCadena = "Hola caracola: " + contador;
                miCadena2 = "Total: " + contador;
                creaTexto(rand(10,200), rand(10,500), miCadena);
            END
            move_text(txtid, x, y);
            say (miCadena2);
        FRAME;
    END
END

En mi caso al pasar de las 33000 lineas (aprox) el valor mostrado de miCadena2 por el write cambia inexplicablemente y quiero descartar que esto pueda provocar un crash. Obviamente esto es una prueba que trata de alcanzar el límite de strings lo más rápidamente posible para ver su comportamiento, y me da la impresión de que algo malo está sucediendo.
Con Say se muestra el valor correcto de miCadena2 en todo momento, por lo que parece que el problema puede estar en la función write al alcanzar dicho limite de strings. Si se comenta la linea move_text(txtid, x, y); al alcanzar las 33000 el texto simplemente desaparece y no vuelve a aparecer.

Como reflexión, no solo en mi proyecto sino cualquier proyecto con un alto número de strings (ej: juegos de rol conversacionales) puede alcanzar este límite y tener como mínimo un funcionamiento no deseado.
Mi comentario anterior tiene unas capturas del error.
5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

FreeYourMind

independientemente del resto tantos strings en un juego de rol no serian problema, porque en ningun momento se enseñan tantos textos en pantalla. y si el juego llega a esa cifra, tampoco tienes que guardar todo el texto en el mismo array, puedes tener varios arrays con texto, de hecho es lo mas recomendable, uno para cada mundo o fase por ejemplo

SplinterGU

el problema es que estas moviendo un texto que muestra micadena a la posicion de micadena2...

si say da correcto, no hay problema con las strings... simplemente estas poniendo en posicion de micadena2 un texto de micadena...

"0" (cero) tampoco es un valor correcto para id de texto... quizas tengas algo ahi, ya que tu codigo considera 0 como valido...

"0" (cero) se retorna cuando hay demasiados textos en pantalla, cuando se llega a 512.

un delete_text(0) elimina todos los textos...

un solo delete_text(0) eliminaria los textos, y sin saberlo podrias estar moviendo un nuevo texto de micadena al lugar de micadena2, ya que el id de micadena2 (que fue borrado por delete_text) ahora apunta a un write de micadena.

corregi el codigo, limpia adecuadamente los ids cada vez que borras un texto y no consideres 0 como valido, 0 es error
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

Arcontus

Quote from: FreeYourMind on May 29, 2018, 10:00:35 AM
independientemente del resto tantos strings en un juego de rol no serian problema, porque en ningun momento se enseñan tantos textos en pantalla.
Eso es lo que yo me pensaba, pero no parece comportarse así, a ver como me explico para que se entienda bien:
Yo pensaba que con tal de no llegar al límite de strings en pantalla para que no se produzciera un colapso y se borraran (como mínimo) no pasaría nada, pero no es lo que me está sucediendo en mi proyecto.
Por lo que me ha parecido observar el problema es que al imprimir strings estos llegan a un número límite. En mi proyecto los enemigos van recibiendo impactos y voy mostrando sus valores por pantalla en un registro. Después de unos 20 minutos de juego, lo mismo solo hay 10 strings en ese momento en pantalla pero al entrar la 11ª string en pantalla, ciertas strings se borran o cambian de valor y poco después todo hace un crash.


Entonces, lo que trataba de hacer con ese programa de ejemplo que he subido es acelerar "el crash" y aun que no he conseguido que crashee el programa es super obvio que algo le pasa al write de miCadena2 justo después de que se provoque el colapso y borrado de strings.

Respecto a lo que comentas de reusar el mismo array, en este caso cada proceso tiene su propia string, donde le pasan por referencia un valor pero para asegurar el tiro lo guardo en una string propia del proceso e independiente al resto de procesos, en su seccion private... Imagina que en vez de enviar a ese proceso "hola caracola" le envio frases con sentido y una posición X e Y.


Quote from: SplinterGU on May 29, 2018, 01:22:06 PM
el problema es que estas moviendo un texto que muestra micadena a la posicion de micadena2...
Los límites de pintado del proceso "creaTexto()" son entre 10 y 500 en el eje Y, y main pinta en 550. Así que eso no debería suceder nunca, xDD (sino vaya porquería de prueba que os estaría enviando)


Quote from: SplinterGU on May 29, 2018, 01:22:06 PM"0" (cero) tampoco es un valor correcto para id de texto... quizas tengas algo ahi, ya que tu codigo considera 0 como valido...

"0" (cero) se retorna cuando hay demasiados textos en pantalla, cuando se llega a 512.
Returns INT : TextID
-1 - Error.
>=0 - The TextID of the text.
Eso es lo que pone en la wiki. De hecho tenía todo mi código con un >0 y lo cambié a >=0 para ajustarme a la wiki. E incluso te abrí una sugerencia para no aceptar el código 0 como valido ¿recuerdas? Dijiste que te lo pensarías ;)

SplinterGU, has dicho una cosa que me hace que pensar si te pudiera dar alguna pista: dices que el límite está en 512, pero el problema aparece siempre a los 33.000 (aprox) y no a los 512. Diría que siempre es el mismo número. ¿Splinter, te dice algo el número 33.000 o ¡32.768!? Tiene una pinta muy sospechosa...


Conclusión, estoy de acuerdo con splinterGU de que no es la string, pero algo le pasa al write y puede ser seria la cosa.

5Leaps, el primer juego comercial desarrollado para BennuGD. http://www.5leaps.com

SplinterGU

mira que yo soy testarudo a veces... pero me ganas... :D

esto esta mal


OnExit:
    if (txtid >= 0) delete_text(txtid); END
    //say(cadena);
END


debe ser


OnExit:
    if (txtid > 0) delete_text(txtid); END
    //say(cadena);
END


y ademas necesitas esto, reemplazar esto


    txtid = write_string(0, x, y, 0, &miCadena);


por esto


    txtid = write_string(0, x, y, 0, &miCadena);
    if ( !txtid )
        say("no mas textos en pantalla!");
        return;
    end


para que entiendas que pasa


import "mod_key"
import "mod_video"
import "mod_text"
import "mod_map"
import "mod_string"
import "mod_rand"
import "mod_say"
import "mod_proc"

PROCESS creaTexto(int miX, int miY, string cadena)
PRIVATE
    int txtid;
    int temporizador;
    string miCadena;
END
BEGIN
    txtid = -1;
    x = miX;
    y = miY;
    miCadena = cadena;
    temporizador = 360;
    txtid = write_string(0, x, y, 0, &miCadena);
    if ( !txtid )
        say("no mas textos en pantalla!");
// descomentame        exit();
        return;
    end

    say(txtid);

    while(temporizador > 0)
        temporizador--;
        move_text(txtid, x, y);
        FRAME;
    END
OnExit:
    if (txtid > 0) delete_text(txtid); END
    //say(cadena);
END

PROCESS main()
PRIVATE
    int indice, contador, contador2, txtid;
    string miCadena, micadena2;

END
BEGIN
    set_fps(0,0);
    set_mode(800,600,32, mode_window);
    x = 30;
    y = 550;
    txtid = write_string(0, 30,550, 0, &micadena2);
    while(!(key(_ESC)))
            contador2++;
            for (indice = 0; indice < 100; indice++)
                contador++;
                miCadena = "Hola caracola: " + contador;
                miCadena2 = "Total: " + contador;
                creaTexto(rand(10,200), rand(10,500), miCadena);
            END
            move_text(txtid, x, y);
            say (miCadena2);
        FRAME;
    END
    let_me_alone();
END


asi el codigo es como funciona, hasta 500 mil en segundos

hay una linea que puse que dice // descomentame, descomentala y veras el ultimo id asignado... solo 512 textos, en realidad 511... luego retorna 0, pero si tu pones


    if (txtid >= 0) delete_text(txtid); END


cuando el write_string falla, txtid vale 0, y cuando el temporizador termina, se ejecuta el onexit y borra delete_text(0) *** TODOS LOS TEXTOS EN PANTALLA ***!!!! pero tu main sigue pensando que miCadena2 sigue vivo con el id obtenido cuando creaste su propio write_string... entonces, tu logica vuelve a crear todos los textos de nuevo... y cuando crea un write_string con miCadena con id igual al del main, entonces lo mueve al lugar de miCadena2...

te voy a poner otro codigo mas, para que veas lo que te digo... bug autoexplicado...


import "mod_key"
import "mod_video"
import "mod_text"
import "mod_map"
import "mod_string"
import "mod_rand"
import "mod_say"
import "mod_proc"


global
    int main_txtid;
    int no_mas_textos_flag = 0;
    int voy_a_borrar_todos = 0;
    int bug_flag = 0;
end


PROCESS creaTexto(int miX, int miY, string cadena)
PRIVATE
    int txtid;
    int temporizador;
    string miCadena;
END
BEGIN
    txtid = -1;
    x = miX;
    y = miY;
    miCadena = cadena;
    temporizador = 360;
    txtid = write_string(0, x, y, 0, &miCadena);
    if ( !txtid )
        if ( !no_mas_textos_flag )
            say("no mas textos en pantalla! (texto aparece solo primera vez, luego sucedera el error)");
            no_mas_textos_flag = 1;
        end
    end

    if ( txtid == main_txtid && !bug_flag)
        say( "aca genere un miCadena con id de miCadena2... bye bye... (muestro solo 1 vez)");
        bug_flag = 1;
    end

    while(temporizador > 0)
        temporizador--;
        move_text(txtid, x, y);
        FRAME;
    END
OnExit:
    if (!txtid && !voy_a_borrar_todos) say( "voy a borrar todos los textos, incluyendo main_txtid (solo muestro primera vez)"); voy_a_borrar_todos = 1; end
    if (txtid >= 0) delete_text(txtid); END
    //say(cadena);
END

PROCESS main()
PRIVATE
    int indice, contador, contador2, txtid;
    string miCadena, micadena2;

END
BEGIN
    set_fps(0,0);
    set_mode(800,600,32, mode_window);
    x = 30;
    y = 550;
    main_txtid = write_string(0, 30,550, 0, &micadena2);
    while(!(key(_ESC)))
            contador2++;
            for (indice = 0; indice < 100; indice++)
                contador++;
                miCadena = "Hola caracola: " + contador;
                miCadena2 = "Total: " + contador;
                creaTexto(rand(10,200), rand(10,500), miCadena);
            END
            move_text(main_txtid, x, y);
//            say (miCadena2);
        FRAME;
    END
    let_me_alone();
END

Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

NOTA: 32000 no tiene nada que ver con 32768, simplemente es casualidad que se reuse el mismo id cerca de ese numero... de hecho a mi me falla a los 36500 exactos, creados...

ya esta explicado el problema, debes controlar los ids de textos que creas, si haces un delete_text(0) se borra todo, write* retorna 0 ante error... (la wiki esta mal), luego de borrar todo los ids se reusan, si tu logica no limpia el id, en algun momento va a asignar ese id a otro texto, y tu logica hara cosas pensando que es otro texto que ya no existe...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2

SplinterGU

Quote from: Arcontus on May 29, 2018, 11:27:11 PM
Eso es lo que pone en la wiki. De hecho tenía todo mi código con un >0 y lo cambié a >=0 para ajustarme a la wiki. E incluso te abrí una sugerencia para no aceptar el código 0 como valido ¿recuerdas? Dijiste que te lo pensarías ;)

y en esa ocasion evidentemente te respondi sin pensar mucho, o te dije que lo veria, ya que nunca puede retornar un id = 0 en un texto valido, ya que delete_text(0) borra todos los textos...
Download Lastest BennuGD Release: http://www.bennugd.org/node/2