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