Rand_weighted() not working on web?

Godot Version

4.3.stable

Question

I am creating a flashcards app, and I am using rand_weighted() to pick a card from a list of strings. It works perfectly fine on desktop, but on web, rand_weighted() returns null.

Thanks for the help,
ValleyDragon

Update: Array.pick_random() works perfectly fine. Can anyone explain why this is?

Did you read the rand_weighted() documentation ? and if so, is there any mention related to the disparity between OS (desktop) and web ?

I can’t see anything here

1 Like

Are you using version control ? and if so can you share a link to the repository ?

If you are not using version control, could you share some code ? … there is not much for me to work with here.

Here is the link: [GitHub repo link]

I was also thinking of submitting an issue to the Godot repo.

Thanks for the help!

Have you tried testing what happens if you hardcode the string list and the argument?

var result = ["string one", "string two", "whatever"][rng.rand_weighted([0.5, 1, 1])

print(result)

This way you can check if it is an async issue.

This also returns null. Just for extra information, my project does not have cross-origin isolation

Shameless self plug but I made an EZ-RNG asset lib entry and under the hood of it this implementation works:

var ezrng: RandomNumberGenerator = RandomNumberGenerator.new()

func gen_weighted(count: int, weights: PackedFloat32Array) -> Array[int]:
	var arr_int: Array[int] = []
	
	for c in count:
		arr_int.append(ezrng.rand_weighted(weights))
	
	return arr_int

As an example:

## Specify an array of things you want to be picked with favorability
var favorite_snacks: Array[String] = ["apple", "cookie", "ice cream"]

## Specify the weights, or, favorability of these things in the same order
## e.g. I like cookies twice as much as apples, and ice cream twice as much as cookies
var snack_weights: PackedFloat32Array = [0.5, 1.0, 2.0]

## Let's say I want to buy snacks for the next two weeks, eating one per day
## This means we want 14 random values to be generated to then align with the snack indices
var weight_results: Array[int] = EZRNG.gen_weighted(14, snack_weights)

## The weight_results array will favor ice cream but still pick apples and cookies
## Now we combine the weight_results and favorite_snacks to see what to buy
var snack_purchase: Array[String] = []
for result in weight_results:
    snack_purchase.append(favorite_snacks[result])

In short, rand_weighted() doesn’t work as directly as you may be trying to use it. If you happen to have a fresh HTML5 export, do you have a link to the latest version of your project files? In my forums profile I have an article of server-less HTML5 export hosting I could try to replicate.

Also for your print(result) test above, you’re not statically typing it, can you also print the typeof() of result? When you say print(result) returns null, is nothing being printed to the F12 console when running the Debug version of your export? Or is it printing the word null? What is your RandomNumberGenerator.new() setup and are you calling randomize() anywhere?

Sources:
AssetLib Entry
GitHub Repo

In terms of printing null, it actually prints <null>.

Here is a link to the project files and builds:
GitHub Repo
GitHub Pages Site

I do not call randomise(), but other random functions still seem to work. [code]

Thanks for the help!

I just exported after importing your project, I turned on:

Extensions Support
Thread Support
Progressive Web App Enabled

And it runs fine in my browsers (Brave, Chrome Canary, Firefox Nightly) on Windows 10, where in your project can I test the rand_weighted()?

Edit 1

When booting up the game I click a random lesson then click Start Lesson and get this in my F12 console:

index.js:462 3
index.js:462 3
index.js:462 [1, 1, 1, 1, 1, 1, 1]
index.js:462 string two
index.js:462 e
index.js:462 (before the direct object)
index.js:462 [1, 1, 1, 1, 1, 1, 1.5]
index.js:462 string one
index.js:462 pali
index.js:462 do, take action on, work on; build, make, prepare

Checking your code now, but does this look familiar? Is this the right repro steps? If so, it looks like rand_weighted() is working on HTML5 export and it’s likely something else on your end.

I believe these Debug statements align with this part of your flashcards.gd script but want to make sure:

func next():
	# there is a rand_weighted() function
	#var new_word = words[rng.rand_weighted(weights)]
	var result = ["string one", "string two", "whatever"][rng.rand_weighted([0.5, 1, 1])]

	print(result)

	var new_word = words.pick_random()
	print(new_word)
	var type = [FlashcardType.LATIN, FlashcardType.SITELEN, FlashcardType.BOTH].pick_random()
	card.set_flashcard_type(type)
	card.set_word(new_word)
	card.refresh_display()

Edit 2

Yea it looks like this is the right code, more F12 Debug output:

index.js:462 ear; to hear, listen; pay attention to, obey
index.js:462 [1, 1.39999997615814, 1, 1.5, 1, 1, 1]
index.js:462 whatever
index.js:462 e
index.js:462 (before the direct object)
index.js:462 [1, 1.39999997615814, 1, 1.5, 1, 1, 1]
index.js:462 string two
index.js:462 pali
index.js:462 do, take action on, work on; build, make, prepare
index.js:462 [1, 1.39999997615814, 1, 2, 1, 1, 1]
index.js:462 string two

This also appears to be right in terms of order/count of print() statements. Is there perhaps another thing you were testing which made it seem like rand_weighted() wasn’t working? I recommend hosting a Debug version of the export and stepping through your browser’s F12 console because this isn’t something reproducible with the same project files. I’d also try with different browsers on your end to see if there’s any differences in behavior on that front.

1 Like

These printouts look fine. I think the difference is in the different settings you used - I will try those and see if it works.

EDIT: It seems to work fine locally, but null is still printed on github pages. Could this be anything to do with a lack of cross-origin isolation on that platform?

Just for information, if you see “string one”, “string two” ect in the console after pressing the smiley face, it is working. If you see any instances of null, the rand_weighted() function is not working.

I use CloudFront/S3 which is also server-less and also did include the Isolation export option since it’s a default. I won’t clog your thread with my setup but my post on it has the CORS, CSP, HSTS, etc. settings I use on my hosting method. With that said though, these should really only impact your browser’s ability to fetch the content in the first place and anything at runtime is either host-side if dynamic content gets populated and fetched mid-runtime or client-side for anything else.

The source code for rand_weighted() itself is:

int64_t RandomPCG::rand_weighted(const Vector<float> &p_weights) {
	ERR_FAIL_COND_V_MSG(p_weights.is_empty(), -1, "Weights array is empty.");
	int64_t weights_size = p_weights.size();
	const float *weights = p_weights.ptr();
	float weights_sum = 0.0;
	for (int64_t i = 0; i < weights_size; ++i) {
		weights_sum += weights[i];
	}

	float remaining_distance = randf() * weights_sum;
	for (int64_t i = 0; i < weights_size; ++i) {
		remaining_distance -= weights[i];
		if (remaining_distance < 0) {
			return i;
		}
	}

	for (int64_t i = weights_size - 1; i >= 0; --i) {
		if (weights[i] > 0) {
			return i;
		}
	}
	return -1;
}

And in random_number_generator.h:

_FORCE_INLINE_ int64_t rand_weighted(const Vector<float> &p_weights) { return randbase.rand_weighted(p_weights); }

And null isn’t really even a possible engine-level response here. It’s not even possible to test with null or incorrect parameters in GDScript further indicating it’s not the engine sending a null response. I’m not able to force this behavior across any browser and even spun up an Apache VM with similar CORS/CSP/HSTS/etc. settings and it ran without issues.

1 Like

This might sound obvious, but sometimes bugs hide behind simple things. There is a null value causing this issue, have you investigated where it is coming from ?

rng for some reason might be causing it.

Have you tried the following ?

func next():
	var rng = RandomNumberGenerator.new()
	var result = ["string one", "string two", "whatever"][rng.rand_weighted([0.5, 1, 1])]

	print(result)

If that doesn’t work, then I’m pretty much useless from now on. I wouldn’t really be able to help much more.

1 Like

This example code works locally (local web builds), but not an github pages.

null is not an engine-level response.

This is very worrying. (The example code provided by @Gass is currently the piece of code that is outputting null to the terminal, as the rand_weighted() code that is part of my app is currently disabled)

If it runs as expected in other environments it might be something related to Github pages then.

Do you think it’s anything to do with cross-origin isolation? Can anyone explain what that even is?

It’s one of many CORS settings:

Overall, CORS exists to allow webhosts control over third party sites accessing your content. i.e. if you own your game yourself and only want it hosted on your own website and approved third-parties, without CORS anyone could just add an iframe HTML element or similar to embed your game in their own site while incurring hosting fees on your end. With CORS, you can specify which cross-origins (other websites) can access your content and in which manners.

This is also why I mentioned earlier any changes in CORS/CSP/HSTS/etc. wouldn’t be gameplay-affecting since the lack of alignment on these would cause a 403 if anything for the initial GET of your game’s content in the first place, or in other words, they wouldn’t even be able to download the index.html/etc. in the first place to even run it.

Because of that, this issue gets isolated to the hosting side or the client side since CORS/CSP/HSTS/etc. are applied during the initial GETs for content in your browser’s Network tab but not after. I went to your GitHub Pages site and reproduced the null issue, but also noted that you’re using v4.1.2.stable.official.399c9dc39, can you try using v4.3.stable.official.77dcf97d8, exporting again, and overwriting your GitHub Pages files? This is the last thing we can rule out Godot-side.

If issues persist after, then that means GitHub Pages handle WASM apps via static content in an unexpected manner and you need to check further on that front.

1 Like

Switching the github action from using 4.1 to 4.3 solves the issue! Thanks to everyone for all of the help.

1 Like