Strange bug

Monkey Forums/Monkey Programming/Strange bug

Farflame(Posted 2012) [#1]
I've come across a bit of a weird bug. I have a method, which completes correctly and returns back to the code, but nothing else in the method it was called from is being processed. I've put in a 'Print 1' to demonstrate, but in my code there's other code following the call to 'DrawString', which is all being completely ignored. I don't see what can be causing this, either the method I'm calling goes wrong or it doesn't. How can it affect the next line? Here's the code, I've cut it down to the bare basics. The variables are correctly declared in the main code, I've just removed them to save space.

Note that 'greenmedium' is the name of my Fontmachine font which is working fine throughout the rest of the program.

Method CallingMethod
DrawString(Box1X+5,Box1Y+5,Box1Width10,Text1,greenmedium)
Print 1
End Method

Method DrawString:Void(X:Int,Y:Int,Width:Int,TheText:String,TheFont:BitmapFont)
' Draw a string, splitting it into sentences which wrap onto the next line.
	Local WordWidth:Int=0
	Local Word:String=""
	Local CurrentX:Int=0
	Local CurrentY:Int=Y
	For Local F:Int=0 to TheText.Length
		Local A:Int=TheText[F] ' Get the character code of the next letter in the text.
		if A=32 or A=46 Then
		' If we've hit a space or a full-stop, it's time to print the word.
			Word+=String.FromChar(A)
			TheFont.DrawText Word,CurrentX+X,CurrentY
			CurrentX+=WordWidth+8
			Word=""
			WordWidth=0
		Else
			Local B:BitMapCharMetrics=TheFont.GetFaceInfo(A)
			WordWidth+=B.drawingWidth ' Add the width of this letter to the width of the existing word we're building.
			Word+=String.FromChar(A) ' Add the letter to the word, we keep doing this until we reach a space.
			if CurrentX+WordWidth>=Width Then
				' This word will trail off the edge of the specified box, so move onto the next line.
				CurrentX=0 ' X back to the start.
				CurrentY+=16 ' Y to the next line.
			EndIf
		EndIf
	Next
End Method


Just to clarify, it's not the 'DrawString' method which is failing here. It works exactly as intended and returns back to the calling code. But any lines after the call are just ignored until the previous method is completed. The game still continues fine - i.e after the calling method has finished, everything else still works as normal.


muddy_shoes(Posted 2012) [#2]
Does this happen in all targets? What does CallingMethod compile to in one where you see the issue?


Farflame(Posted 2012) [#3]
Ahhh, that's helped. I tried compiling in HTML5 (was originally in Flash) and it's crashing, with the error, t_B is null. I tried adding a 'If B<>Null' (btw, could that just be 'If B'?) inside the 'Else' section, after B is declared. It now runs in HTML5 properly, but is still doing the same thing in Flash, i.e not crashing but just continuing to ignore the lines in the calling method.

Maybe this is something to do with Fontmachine and the way the font is interacting with Flash? It's definitely acting differently in HTML5. Not quite sure how to get around this.


muddy_shoes(Posted 2012) [#4]
Both HTML5 and Flash printed "1" when I eventually wrapped your code in enough scaffolding to get it to run (with the null check).


Farflame(Posted 2012) [#5]
Sorry about the shortened code, seems better to me to try to condense it to the relevant parts rather than post reams of unrelated code. Did you use a Fontmachine font for your test? I'm finding that it works perfectly in HTML5, but in Flash it still skips to the end of the initial calling method and skips the remaining lines. I know different targets can react differently but this is the first time I've ever noticed it (tbh, I only test in Flash anyway). Good tip for me for the future anyway, if something odd happens I'll try to compile to different targets to see what errors I get.

Still not got to the bottom of this one but I'll keep trying.


muddy_shoes(Posted 2012) [#6]
Yes, I used a fontmachine font. It works fine. Cutting down the code to what's relevant is great as long as it includes everything that is relevant. The only way to know that's the case is to provide a code example that runs and shows the error.

Right now I've got an example that works based on what you posted and you've got code that doesn't. That suggests that the problem isn't in the code you posted.


Farflame(Posted 2012) [#7]
That's true, if yours works, something in mine is causing problems. I'll look into it and post up a bit more if I can't get to grips with this. It's one of those situations where I feel something is wrong with the language rather than my code, but probably not now :)


Farflame(Posted 2012) [#8]
Ok, still stumped by this. There's some way to look at the compiled code right? It's strange that it's working in HTML5 but not in Flash. I'll show my complete code to see if anyone can spot any programming errors on my part, but since it works in HTML5, I don't see how that can be possible. Still, someone might see where I'm going wrong.

These are the two methods in question. They're both part of my main game class. It's not finished, but the idea is basically to over-lay some black rectangles (usually 1, but sometimes two)with text inside, as a tutorial for my game. The rectangles and the text are fed into the first method, which then calls the 2nd.

In this case, I've put a 'PlayASound(Click)' after the call. In HTML5, it's clicking incessantly (i.e once per frame) as I'd expect. In Flash, there's nothing, and the other lines below it do nothing either.

Method TutorialBox:Void(Box1X:Int,Box1Y:Int,Box1Width:Int,Box1Height:Int,Box2X:Int,Box2Y:Int,Box2Width:Int,Box2Height:Int,Text1:String,Text2:String)
	SetColor 255,255,255
	DrawRect(Box1X,Box1Y,Box1Width,Box1Height)
	if Box2X>0 Then ' Only draw box 2 if we have a 2nd box.
		DrawRect(Box2X,Box2Y,Box2Width,Box2Height)
	EndIf
	SetColor 0,0,0
	DrawRect(Box1X+1,Box1Y+1,Box1Width-2,Box1Height-2)
	if Box2X>0 Then ' Only draw box 2 if we have a 2nd box.
		DrawRect(Box2X+1,Box2Y+1,Box2Width-2,Box2Height-2)
	EndIf
	SetColor 255,255,255
	DrawString(Box1X+5,Box1Y+5,Box1Width-10,Text1,greenmedium)
	PlayASound(Click)
	HotSpots.AddLast New cHotSpot(Box1X,Box1Y,Box1Width,Box1Height,200,0)
	if Box2X>0 Then ' Only do the 2nd box text if there is any.
		DrawString(Box2X+5,Box2Y+5,Box2Width-10,Text2,greenmedium)
	EndIf
End Method
	
Method DrawString:Void(X:Int,Y:Int,Width:Int,TheText:String,TheFont:BitmapFont)
	' Draw a string, splitting it into sentences which wrap onto the next line.
	Local WordWidth:Int=0
	Local Word:String=""
	Local CurrentX:Int=0
	Local CurrentY:Int=Y
	For Local F:Int=0 to TheText.Length
		Local A:Int=TheText[F] ' Get the character code of the next letter in the text.
		if A=32 or A=46 Then
			' If we've hit a space or a full-stop, it's time to print the word.
			Word+=String.FromChar(A)
			TheFont.DrawText Word,CurrentX+X,CurrentY
			CurrentX+=WordWidth+8
			Word=""
			WordWidth=0
		Else
			Local B:BitMapCharMetrics=TheFont.GetFaceInfo(A)
			if B<>Null Then
				WordWidth+=B.drawingWidth ' Add the width of this letter to the width of the existing word we're building.
				Word+=String.FromChar(A) ' Add the letter to the word, we keep doing this until we reach a space.
				if CurrentX+WordWidth>=Width Then
					' This word will trail off the edge of the specified box, so move onto the next line.
					CurrentX=0 ' X back to the start.
					CurrentY+=16 ' Y to the next line.
				EndIf
			EndIf
		EndIf
	Next
End Method
	



muddy_shoes(Posted 2012) [#9]
The translated code will be in the build directory.

Your example is still impossible to use to track the problem as it's full of references to external values. Can you alter this to recreate what you're seeing and repost it?




Farflame(Posted 2012) [#10]
Ok, thanks so far for your help. I understand that I need to be more precise and provide a working (or not working) example so you can recreate it at your end, and then delve into why it doesn't work... especially since it does at your side.

Ok, this isn't much, but I've taken your code and gone back to the Monkey ide and pasted it in and made some changes. I haven't used the Monkey ide for months since I now use Jungle, so it's possible (read probable) that I've just done something very dumb here and this is nothing. However, I've saved the code and made the data folder and dragged my original Fontmachine font folder into the data folder. So now it's using the same font and data structure as my Jungle version.

What I am noticing is, if I run it in HTML5, it does work, and my font shows correctly. In Flash it gives a null object error. If I comment out the call to 'Drawstring', it then works. So what I'm thinking is, the call to Drawstring is somehow behaving differently between HTML5 and Flash, and somehow just passing 'greenmedium' in Flash isn't passing the font correctly.

I should point out I know nothing of other languages, I barely understand my own programs xD

Here's the code, similar to yours but a bit updated... I'm fairly sure you'll need to create a Fontmachine font and put it in the data folder to get this to work/crash.

Import fontmachine
Import mojo

Class TestApp Extends App
    Field greenmedium:BitmapFont
    
    Method OnCreate()
        greenmedium=New BitmapFont("greenmedium/greenmedium.txt",True)
        SetUpdateRate(30)
    End

    Method OnRender()
        TutorialBox(50,50,100,100,0,0,0,0,"Test line 1","") 
    End
    
	Method TutorialBox:Void(Box1X:Int,Box1Y:Int,Box1Width:Int,Box1Height:Int,Box2X:Int,Box2Y:Int,Box2Width:Int,Box2Height:Int,Text1:String,Text2:String)
		SetColor 255,255,255
		DrawRect(Box1X,Box1Y,Box1Width,Box1Height)
		If Box2X>0 Then ' Only draw box 2 if we have a 2nd box.
			DrawRect(Box2X,Box2Y,Box2Width,Box2Height)
		Endif
		SetColor 0,0,0
		DrawRect(Box1X+1,Box1Y+1,Box1Width-2,Box1Height-2)
		If Box2X>0 Then ' Only draw box 2 if we have a 2nd box.
			DrawRect(Box2X+1,Box2Y+1,Box2Width-2,Box2Height-2)
		Endif
		SetColor 255,255,255
		DrawString(Box1X+5,Box1Y+5,Box1Width-10,Text1,greenmedium)
		Print "It works"
		If Box2X>0 Then ' Only do the 2nd box text if there is any.
			DrawString(Box2X+5,Box2Y+5,Box2Width-10,Text2,greenmedium)
		Endif
	End Method
	

    Method DrawString:Void(X:Int,Y:Int,Width:Int,TheText:String,TheFont:BitmapFont)
    ' Draw a string, splitting it into sentences which wrap onto the next line.
    	Local WordWidth:Int=0
    	Local Word:String=""
    	Local CurrentX:Int=0
    	Local CurrentY:Int=Y
    	For Local F:Int=0 To TheText.Length
    		Local A:Int=TheText[F] ' Get the character code of the next letter in the text.
    		If A=32 Or A=46 Then
    		' If we've hit a space or a full-stop, it's time to print the word.
    			Word+=String.FromChar(A)
    			TheFont.DrawText( Word,CurrentX+X,CurrentY)
    			CurrentX+=WordWidth+8
    			Word=""
    			WordWidth=0
    		Else
    			Local B:BitMapCharMetrics=TheFont.GetFaceInfo(A)
    			If B = Null
                    Continue
                End
    		    WordWidth+=B.drawingWidth ' Add the width of this letter to the width of the existing word we're building.
                	Word+=String.FromChar(A) ' Add the letter to the word, we keep doing this until we reach a space.
    			If CurrentX+WordWidth>=Width Then
    				' This word will trail off the edge of the specified box, so move onto the next line.
    				CurrentX=0 ' X back to the start.
    				CurrentY+=16 ' Y to the next line.
    			Endif
    		Endif
    	Next
    End Method
End     

Function Main()
    Local a:TestApp = New TestApp
End




Farflame(Posted 2012) [#11]
Bit of an addition because I thought I'd solved it, but at least I'm closing in on it. If you remove 'thefont' from the DrawString method and just directly access 'greenmedium' from there, thus removing the need to pass the font, it still doesn't work in Flash. My thinking was that passing a font this way was the problem.

The issue I now suspect is the line 'Local B:BitMapCharMetrics=TheFont.GetFaceInfo(A)'. Am I simply using BitMapCharMetrics wrongly? It's the first time I've used it so I suspect this is the problem. Even if you change it to Local B:BitMapCharMetrics=greenmedium.GetFaceInfo(A) (i.e directly access the font instead of the passed pointer) it fails in the exact same way. Not quite sure why it fails in the way that it does (i.e not generate an error and then go on to ignore the next few lines of code).


muddy_shoes(Posted 2012) [#12]
I am using a fontmachine font and I'm also using Jungle (you don't need to create a project you know, you can just open a new file wherever you want).

That code still works for me in Flash. What's the full error message you're seeing in debug mode?


Farflame(Posted 2012) [#13]
Thanks for the tip, didn't realise I could do that in Jungle. Would've saved me a bit of time... and will save me plenty in the future, so thanks for that :)

The error I'm getting in debug mode (Flash only) is

Monkey runtime error: Null object access

Again, no error at all in HTML5 and it continues with the code correctly. I know this isn't much use, Null object errors are usually fairly obvious but I can't work this one out.

Still studying it, will post up more information if I can get any.


muddy_shoes(Posted 2012) [#14]
So, no stack trace with line numbers?

Anyway. I can reproduce your issue by nulling the font reference in DrawString. So there seem to be two problems:

1. The flash target seems to be doing something odd when hitting a null object reference and not throwing an error.

2. You've got a null reference.

As it works for me perhaps it's your font that's the issue. Are you sure that your font images are in the data directory? If I rename one of mine I get the skipping issue too.


Farflame(Posted 2012) [#15]
The best I can come up with is this. If I comment out the line 'Local B:BitMapCharMetrics=TheFont.GetFaceInfo(A)' (and the couple after which reference the B variable) then I don't get the error and the program runs normally. My routine doesn't work of course because I can't work out the width of my characters, but the program runs again and the error goes away.

So I'm doing something wrong in that line and it's causing a null access. Since I've never used 'BitMapCharMetrics' or 'GetFaceInfo' before, is there anything I need to know to make them work?


muddy_shoes(Posted 2012) [#16]
B being null can't cause an issue if you've got the null check in there. If you mean that you get the problem with the null check removed then either your font data is broken or you've not included a character in your fontmachine font that is in the text. If the character doesn't exist then GetFaceInfo returns null. In debug mode I get:

Monkey runtime error: Null object access
C:/Data/Dev/Projects/monkey/temp/Untitled_47.monkey<58>
C:/Data/Dev/Projects/monkey/temp/Untitled_47.monkey<29>
C:/Data/Dev/Projects/monkey/temp/Untitled_47.monkey<14>
C:/Data/Dev/MonkeyPro/modules/mojo/app.monkey<70>


Farflame(Posted 2012) [#17]
I was thinking along those lines too. But if you leave the top reference to the font and just remove the B:Bitmapcharmetrics line (and the lines with 'B' in just after), then it all works fine. Meaning it does still reference the font above and infact it prints the text in the correct font (albeit all printing over the top of each other because the routine doesn't work now).

I actually thought the font was broken or something because I was getting other font errors but then I realised it's because I'd named your program 'test' and put it in the same location as my game but hadn't made a 'test.data' folder... for some reason I was assuming it would access my game's data folder but obviously it needs it's own. So now it works fine, in both targets.

I was replying to your previous reply here, your point about missing characters may have hit the nail on the head, but it's odd because I included every letter of the alphabet and all numbers... I'll check that.


Farflame(Posted 2012) [#18]
The phrase 'FML' springs to mind.

I just noticed that I'd changed my test text to 'Test line 1' and only just noticed I'm only getting 'Test line' and then the errors. Presumably meaning I don't have '1' in my font. Changed the text to 'aaaaa' and the error goes away. I have no idea how that's happened, I was sure it was a full font.

GRRRRRR!!!

Thanks very much for your time. To be honest, I'd NEVER have worked this out on my own. The odd thing is... my game does use numbers in that font :s


Farflame(Posted 2012) [#19]
Ugh, what I think the problem was, is I'm doing 'For Local F:Int=0 To TheText.Length', and I think it should be .Length-1. So presumably the sentence 'a' would give an 'a' and then some kind of end-of-line character, and since my font doesn't have that character, that's the null object.

Thanks very much for your help, it's fixed (fingers-crossed) and I've learnt a few things. Time for bed xD


ziggy(Posted 2012) [#20]
@FarFlame, on Monkey, for this zero-based arrays you can also use
For Local F:Int = 0 Until TheText.Length
and that is equivalent to:
For Local F:Int = 0 To TheText.Length - 1



Farflame(Posted 2012) [#21]
Thanks Ziggy, this bug has taught me a few things :)