XML talk:BINA/OBJC/TRGV

From OniGalore
Jump to navigation Jump to search

Failing string comparison

There is also a bug with DOOR textures. XML:BINA/OBJC/DOOR#tags

We could try to capitalize everything, maybe that helps. --paradox-01 (talk) 12:12, 14 May 2020 (CEST)

Can someone describe the bug in more detail so I can take a look? --geyser (talk) 14:39, 14 May 2020 (CEST)

Oh, OK, silly me, I should have read the TRGV page to learn about the string comparison bug. However, I don't understand how the TRGV string comparison is related to DOOR textures (DOOR textures are a permanent level setting, nothing to do with BSL - right?) --geyser (talk) 16:20, 14 May 2020 (CEST)


This is what s10k wrote: https://wiki.oni2.net/w/index.php?title=XML%3ABINA%2FOBJC%2FTRGV&type=revision&diff=26530&oldid=26524

Basically "String" eq "String" returns false when one expression is delivered by TV and one expression delivered from a 'native' BSL variable.

Maybe the comparison fails because at some side (left or right) the string is made toUpper. We have seen a similar bug when the engine reads texture names.

So the idea is that "STRING" eq "STRING" might work, just maybe. --paradox-01 (talk) 16:25, 14 May 2020 (CEST)

The analogy to Doors would be that "String(fromCJBO)" eq "String(fromTextureFileName)" fails because of the bugged comparison. (The code for comparison might be used at both locations - BSL and Door.) --paradox-01 (talk) 16:26, 14 May 2020 (CEST)

To check the toUpper hypothesis, you don't need to change the game data, just test against "CHAR_0". Another thing to do (which I did) is set testVar = ai_name (similar to what is done with my_save_point = save_point in many of Bungie's scripts). I then print testVar (it displays as char_0, both with dmsg and at the dev console, i.e., with no uppercase) and finally check testVar eq "char_0" instead of ai_name eq "char_0" (it fails). I also quick-changed char_0 to KONOKO in Konoko's CHAR. And it still fails the same way: both strings display the same value KONOKO, but testVar eq "KONOKO" (or testVar eq KONOKO) evaluates to false (same as for "char_0" before the hack). However, testVar eq testVar, ai_name eq ai_name and testVar eq ai_name all evaluate to true. So I really think it's a non-printable char in the character name as sent from the runtime event (end-of-line most likely). -- geyser (talk) 19:41, 14 May 2020 (CEST)

Is there a way / "geyser hack" to count the signs a la length(string)? :D --'Dox

You mean something to count the symbols/characters/bytes? Well, none that I know of, at least not from within Oni's game/console and scripts. Neo or the Daodan folks would be able to help, but I have never had the patience to look at Oni's ASM myself. I am pretty sure that the problem is a trailing end-of-line, though. --geyser (talk) 22:37, 14 May 2020 (CEST)

One way to check would be to deliberately initialize a string variable with an end-of-line, and use it to check against the character name received from the TRGV event. Daodan DLL's sprintf would be the simplest way to create a string variable that includes an end-of-line. Or, if sprintf doesn't support \n for some reason, it should be possible to hex-edit a custom name for an AISA character, and then retrieve its name into a string variable using d_name. Then again, I guess it would be easy enough to implement strlen. --geyser (talk) 00:38, 15 May 2020 (CEST)


Hi guys, I saw this page and got interested in the topic.
I tried to run some tests and did some runtime memory probing.
See BSL:String comparison for results.
--Loser (talk) 20:54, 17 May 2020 (CEST)

It doesn't resolve the current situation but it is a help nonetheless and kills some headache about bsl scripting.... If someone really needed to he can use your work as foundation. Thanks you Loser for looking into this. --paradox-01 (talk) 08:59, 18 May 2020 (CEST)

Thanks for checking the runtime ai_name and how it doesn't have any extra characters after all. I am not yet convinced that the comparison is by pointer and not by content, but if it is so, then it would mean that string arguments passed from runtime events (TRGV etc) are completely unusable - because they have their own unique pointer, which will never match anything you compare it with (unless you do something like BOB_string = ai_name, but then the two strings are in fact one and the same, and there's nothing left to compare). --geyser (talk) 11:05, 18 May 2020 (CEST)

I have confirmed in my own way that the strings coming from runtime events are (apparently) compared by pointer rather than by content. Suppose you define two global string vars, dead_name and dead_name_too. Then you kill A_t48 twice (after force-spawning him), with t48_dead(string ai_name) performing the assignment dead_name = ai_name. Between the two deaths, do dead_name_too = dead_name from the console (this is now a redirect to the same string data, and dead_name_too eq dead_name evaluates to true). After the second A_t48 death, try dead_name_too eq dead_name again, and see that it evaluates to false now (there used to be one string, and now it's two identical strings at different addresses). --geyser (talk) 13:10, 18 May 2020 (CEST)

It should be noted, however, that "ordinary" string literals (the ones that are included in BSL scripts and allocated at level start) seem to be always compared by content even if they're changed to completely new literals at runtime (as long as it's from the console and not from OBJC events). If you have two global string vars testVar and testVar2, you can set them both to "bla" from the console, and testVar eq testVar2 will evaluate as true - so apparently the comparison becomes lazy (by pointer) if at least one of the strings (pointers) is in the dynamic range used by runtime events, but for new strings from the console (also dynamically allocated) it still uses the non-lazy comparison. --geyser (talk) 13:10, 18 May 2020 (CEST)

I sort of found a workaround, where I copy the value of ai_name to testVar not directly, but via Daodan's sprintf (basically you do testVar = sprintf("%s",ai_name) instead of testVar = ai_name). This fills testVar with some garbage (in my test it was "hӌ"), but it's some consistent garbage that is somehow related to ai_name - so you can use this at spawn time to initialize the "reference name" of a character, and later check against it with names that you receive from death functions, trigger volumes, etc. I didn't check this thoroughly, but I will make an illustration (probably in TCTF HQ, where many Vanilla CHARs have spawn functions). Also, I have no idea why sprintf garbles the literal like that when it comes from runtime, so that's another thing that will need to be checked. --geyser (talk) 13:56, 18 May 2020 (CEST)

OK, good news and bad news. Bad: string management is fundamentally messy, including sprintf. Like, I was expecting sprintf-created strings to stay protected, but they're easily overwritten by other data, depending on the context - it's like the "string" is just a raw char* and there is nothing to keep track of the allocated memory at all. Good: the following works:

func void t48_dead(string ai_name)
{
    var int index = d_getindex(ai_name);
    var int index0 = d_getindex(A_t48);
    if(index eq index0)
        dmsg("A_t48 detected!")
    else
        dmsg("No index match!")
}

or this, in TCTF HQ:

func void striker_spawn(string ai_name)
{
	var int index = d_getindex(ai_name);
	var int index1 = d_getindex(garage_striker_01);
	var int index2 = d_getindex(garage_striker_02);
	if(index eq index1)
		dmsg("Striker_01 has been spawned!")
	if(index eq index2)
		dmsg("Striker_02 has been spawned!")
}

func void striker_die(string ai_name)
{
	var int index = d_getindex(ai_name);
	var int index1 = d_getindex(garage_striker_01);
	var int index2 = d_getindex(garage_striker_02);
	if(index eq index1)
		dmsg("Striker_01 has died!")
	if(index eq index2)
		dmsg("Striker_02 has died!")
}

func void spawnStriker1(void)
{
	chr_wait_animtype 0 punch
	ai2_spawn garage_striker_01 force
	sleep 60
	fork spawnStriker1
}

func void spawnStriker2(void)
{
	chr_wait_animtype 0 kick
	ai2_spawn garage_striker_02 force
	sleep 60
	fork spawnStriker2
}

func void main(void)
{
	fork spawnStriker1
	fork spawnStriker2
}

The same thing works just fine for a trigger volume, e.g., at the Airport:

func void fire_damage(string ai_name)
{
	var int index = d_getindex(ai_name);
	var int index0 = d_getindex(char_0);
	var int index1 = d_getindex(LoadingBay_Thug_1);
	var int index2 = d_getindex(LoadingBay_Thug_2);
	if(index eq index0)
		dmsg "Konoko is burning!"
	if(index eq index1)
		dmsg "Thug 1 is burning!"
	if(index eq index2)
		dmsg "Thug 2 is burning!"
}

func void main(void)
{
	ai2_spawn LoadingBay_Thug_1
	ai2_spawn LoadingBay_Thug_2
	chr_location 0 134 -105 1029
}

OK, then, problem solved, moving on. --geyser (talk) 12:39, 19 May 2020 (CEST)