Collision issue on Samsung Galaxy S20 bundled as release

This is a strange one.

When a kinematic object collides with a static object the contact_point_response message returns the message.other_id:

hash: [12693854167970456507 (unknown)]

Rather than the expected:

hash: [/terrain]

When using this code to turn it into a full url:

local url = msg.url( nil, message.other_id, nil )

This is returned:

[main:<unknown>] 

Rather than the expected:

url: [main:/terrain]

On the S20 this means that collisions can’t be referenced, which is a game stopper because message.other_id is used to identify what the player bumped into.

This behaviour only seems to happen on a Galaxy S20 when bundling as release. Bundling as debug works.

Minimal example: CollisionIssueS20.zip (3.1 MB)

As you might have guessed, this obscure bug was discovered late in development, in fact after soft launch, so I’d very much appreciate any assistance trying to fix it!

1 Like

The strings (the reverse hashes) are just there for debugging purposes. They are not available in release mode.

Their actual value (the hashes) are still the same.

2 Likes

Hey @Mathias_Westerdahl that’s good to know. Any idea how I can fix the issue? It seems to be isolated to the Galaxy S20 for some reason, and happens only on release builds.

To clarify, this string is taken from an .apk that was bundled as release (typed in by hand, because debugging doesn’t exist on release!):

hash: [12693854167970456507 (unknown)]

Not sure what the issue is?

It seems you expect debug data in the release build ?
Note that the “<unknown>” is just telling you it cannot reverse hash that value into a debug string.

You can printout the individual hashes if you want to:

io.stderr:write(string.format(“URL: %s (%s, %s, %s) is_debug: %s\n”, u, u.socket, u.path, u.fragment, sys.get_engine_info().is_debug))

Here’s an example where I printed out from “build and run” which is in debug mode, and from a release bundle:

Debug:

URL: url: [main:/go#main] (hash: [main], hash: [/go], hash: [main]) is_debug: true

Release bundle:

URL: url: [main:<unknown>#<unknown>] (hash: [17296653182486628446 (unknown)], hash: [2403139543084994936 (unknown)], hash: [17296653182486628446 (unknown)]) is_debug: false

As you can see, the url still contains hashes in the release mode. Which is the actual value, not the string.

I think it’s better to see some code to better grasp what you are trying to do, and potentially see why it doesn’t work.

1 Like

The issue is that I expect this code:

local url = msg.url( nil, message.other_id, nil )

To produce a unique url, rather than:

[main: <unknown>]

I’ve only seen this issue on a S20 device and only in release mode. All other devices return a unique url.

If the above is expected, is there an alternative way to get a unique id from message.other_id?

A sample project was included in the first post.

Correct, that is the url of the other game object.
Just because you cannot debug print it the way you used to, doesn’t change the actual url.

Here’s an example, where I renamed my gameobject to “terrain”:

Debug:

URL: url: [main:/terrain#main]
(hash: [main], hash: [/terrain], hash: [main]) == (f00a0e6d893ec85e, b029a11c607da3bb, f00a0e6d893ec85e)
hash: main  hash: [main] == f00a0e6d893ec85e
hash: /terrain  hash: [/terrain] == b029a11c607da3bb

Release bundle:

URL: url: [main:<unknown>#<unknown>]
(hash: [17296653182486628446 (unknown)], hash: [12693854167970456507 (unknown)], hash: [17296653182486628446 (unknown)]) == (f00a0e6d893ec85e, b029a11c607da3bb, f00a0e6d893ec85e)
hash: main  hash: [17296653182486628446 (unknown)] == f00a0e6d893ec85e
hash: /terrain  hash: [12693854167970456507 (unknown)] == b029a11c607da3bb

As you can see by the values from “hash_to_hex”, they’re the same.

EDIT: Using this code to print:

io.stderr:write(string.format("URL: %s\n(%s, %s, %s) == (%s, %s, %s)\n", u, u.socket, u.path, u.fragment,
	hash_to_hex(u.socket), hash_to_hex(u.path), hash_to_hex(u.fragment) ))

	io.stderr:write(string.format("hash: %s  %s == %s\n", "main", hash("main"), hash_to_hex(hash("main"))))
	io.stderr:write(string.format("hash: %s  %s == %s\n", "/terrain", hash("/terrain"), hash_to_hex(hash("/terrain"))))

The sample project was a bundled .apk?
I referred to you actual Lua code, which seems to have some errors?

In release mode the reverse hash functionality does not work, but URLs should still work. The individual parts of the URL are hashes and I’m sure these return the values for the hashed strings. Try this:

local url = msg.url( nil, message.other_id, nil )
print(url.socket, url.path, url.fragment)

What do you mean by this?

2 Likes

Yes, the long numbers and (unknown) only appear when the .apk is bundled as release.

I didn’t know you used be able to! Save me! :slight_smile:

That’s possible, but to me it seems fine?

Here is a screenshot of what comes back when printing socket, path and fragment individually (release build .apk on Galaxy S20):

Judging by the confusion on all sides here, I think it’s likely I’m missing something basic. It’s just troublesome it works on all devices apart from the S20. And that it’s so HOT TODAY. :hot_face:

In an effort to get to the crux of the matter, here is what I’m trying to do, step by step:

  1. Turn message.other_id (a collision object) into a game object url.
  2. Use tostring(url) as an id, ie. game_objects[tostring(url)], to cross reference it.

I get the feeling #2 is a no-no, so maybe the questions are:

  • What is the correct way of referencing a game object in an associative array?
  • Why does it only fail on the S20?

I’ll try to explain it in a bit different way.
Strings are available only for debug reason, there are no strings in release build.
It doesn’t matter you can’t see this string. It is not a bug, but if you mention this as a bug, probably you try to use hash in a wrong way.
Could you please explain why it’s bothering you and what exactly doesn’t work in your project because of that?

Thanks Agulev. Yeah, see above. I’m using tostring(url), which is likely why it fails (but only on one device, mind!).

I meant as you used to in debug mode.

  1. As you see from your screenshot (?), the values:
    17296653182486628446 and 12693854167970456507 are the same as in my example. Thus the original strings are “main”, and “/terrain”. So, your url seems fine.

  2. Using the Url as a string, is a no-no. As mentioned, the string representation is for debugging purposes. You should be able to use the url as a key in a table. If all else fails:

	local s = "" .. hash_to_hex(u.socket) .. ":/" .. hash_to_hex(u.path) .. "#" .. hash_to_hex(u.fragment)
	io.stderr:write(string.format("string_id: %s\n", s))

which outputs:

string_id: f00a0e6d893ec85e:/b029a11c607da3bb#f00a0e6d893ec85e
1 Like

Better yet:

	local t = {}
	t[url] = "Url A"
	pprint(t)

which yields:

DEBUG:SCRIPT: 
{ --[[0x10db78610]]
  url: [main:/terrain#main] = "Url A"
}

Oh, nice, I’m trying that right away! I have soaked a long sleeve t-shirt to keep the heat at bay. Thanks for your patience with this.

Hmm, there seems to be a caveat though. It seems it’s using the object id though. so unless you have the original url object, you won’t get the value.

I consider this a bug though. I’m guessing it has been requestedfor a fix previously.

A workaround is to iterate over the table, and do the compare yourself:

		
	for k, v in pairs(self.t) do
		if k == sender then
			print(k, v)
			break
		end
	end
2 Likes

Maybe that’s why I used strings to begin with. So going back to square one, how can I use the url as a key in a table in my specific case?

player.script:

local url = msg.url( nil, message.other_id, nil )
msg.post("/main#main", "hit_box", {url=url})

main.script:

local boxes 

function init(self)

	boxes = {}
	
	for i=1, 2 do
		local url = msg.url( nil, "box"..i, nil )
		boxes[url] = "box_index"..i
	end
	
end

function on_message(self, message_id, message, sender)

	if message_id == hash("hit_box") then

		pprint( boxes )
			--[[
			-- Returns:
			{
				url: [main:/box2] = "box_index2",
				url: [main:/box1] = "box_index1"
			}
			--]]
	
		print( message.url ) -- Returns url: [main:/box1]
		print( boxes[message.url] ) -- Returns nil
		
	end
	
end

Test project: UrlAsKey.zip (20 KB)

Yes, #2 is a very big NO-NO. It will break down completely in a release build. The tostring() will result in the non-unique [main:<unknown>] string and it will be useless as a table key.

If it is a hash then you can use it as a key in a table. The hashes will be unique even in a debug build. If it is a URL you need to convert it to a unique string from its components before using it as a key:

local EMPTY = hash("")

local function to_key(url)
	return hash_to_hex(url.socket or EMPTY) .. hash_to_hex(url.path or EMPTY) .. hash_to_hex(url.fragment or EMPTY)
end

That’s the million dollar question. Which other devices have you tested on?

3 Likes

And now that I think of it maybe we should handle this under-the-hood somehow. We should provide a better __tostring meta-method for URLs. It’s not the first time someone has stumbled on to this issue.

2 Likes

This is what I’ve been missing. I modified the test project to create a key in this way and now it works! :metal: :metal: :metal:

Thanks for helping me find the missing piece of the puzzle.

The above fix might be obvious to some, but it’s black magic to me. Would love to have an easier way to do this.

Literally hundreds of devices on Android and iOS: The game is in soft launch. As far as I know, only the S20 is kicking up a fuss.

Given the solution described, it would apply to all platforms in release mode. If you still only have that particular issue on the S20, then you have another issue in your code.