GMS 2 Attempting to write outside buffer, line -1?

solarcrispz

Member
Hey guys
I've been putting together a multiplayer game with pretty good success. I've encountered "attempting to read from outside buffer, returning 0" errors before, but they're never as ambiguous as the one I'm consistently running into recently.
Here it is:
GML:
FATAL ERROR in
action number 1
of Async Event: Networking
for object controller_object:

Attempting to read from outside the buffer, returning 0
at gml_Object_controller_object_Other_68
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Object_controller_object_Other_68 (line -1)
Notice the (line -1) note. Very strange and unusual.
I've rewatched recordings during my playtesting sessions where this bug occurs, and the only in-game factor that seems to *relatively* coincide is 7 or more players at once. But even then, the bug can trigger even with just a few players connected to my server!
(Note: This bug only occurs to the player hosting the server)

Is this potentially related to packet loss? The game runs on a TCP infrastructure. The "line -1" aspect of this really makes it untraceable in a conventional sense to me, as any other 'normal' async crash will atleast note a specific line. What could it be? How could I prevent this from happening? Should I just be writing my servers in something like node.js?
I'm currently using GMS 2.2.5.481 (because 2.3 seems to not work with the socket.io extension).

I'm generally clueless on this sort of stuff, so any insight is much appreciated.
 

Yal

🐧 *penguin noises*
GMC Elder
"Line -1" means that there's no line information, so it could be an error in an external function. How is the event set up?

Do all your buffer reads check the buffer size so you're 100% sure they won't read outside the buffer? If this happens with lots of players specifically, it could be both an overflow (buffer's not big enough to deal with them all) or an underrun (you read faster than players send data because of some flawed assumption).
 

solarcrispz

Member
Thanks for your response. I've seen things indicating manual buffer-size checks are the way to go, but as I've mentioned I'm pretty clueless on this stuff so I can't necessarily think of a way to implement some check like that.
What would an elegant implementation of checking buffer size look like? Does it involve buffer_tell or buffer_get_size or something? What would I compare it to?

As for the other guesses, I think I'll look at the first scenario first as I think that's the most likely. As I've said, the problem still occurs even with relatively low player counts.
 

solarcrispz

Member
Bump. Does anyone know what this might be about?
What would the best way to implement a buffer size checker? Should I not be using buffer_grow (is it bugged?)? Any ideas?
 

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
Line -1 usually means that this is an error in compiled executable and you did not use YYC to preserve line information.

buffer_tell(b) + number_of_bytes_you_want_to_read <= buffer_get_size(b) would be your condition.
 

solarcrispz

Member
Line -1 usually means that this is an error in compiled executable and you did not use YYC to preserve line information.

buffer_tell(b) + number_of_bytes_you_want_to_read <= buffer_get_size(b) would be your condition.
I didn't necessarily disable YYC. Why does it seem to not work in some scenarios?

As for the line you've shared, would number_of_bytes_you_to_read just be set up for every individual case setting?
Say for instance, sending x/y coord data requires 16 bytes, and sending a player made bullet requires 32 bytes, how would I dynamically reset number_of_bytes_to_read etc for the distinct cases?
I send an interger with every piece of data like above to tell the async event how to read it (0 being player movement, 2 being bullets for example) in a case switch. How would I set something like above up?
Also, I assume the line you shared is set to confirm an order, ie 'if line_you_shared() = true { read the whole buffer like normal'? Or are you doing that for every subsequent buffer_read, ie:

obj = buffer_read(buffer, buffer_u32);
line_you_shared();
obj.speed = buffer_read(buffer, buffer_s16);
line_you_shared();
obj.damage = buffer_read(buffer, buffer_u16);
line_you_shared();

I assume isn't what you would say to do, right?
Thanks alot for your response.
 

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
I didn't necessarily disable YYC. Why does it seem to not work in some scenarios?

As for the line you've shared, would number_of_bytes_you_to_read just be set up for every individual case setting?
Say for instance, sending x/y coord data requires 16 bytes, and sending a player made bullet requires 32 bytes, how would I dynamically reset number_of_bytes_to_read etc for the distinct cases?
I send an interger with every piece of data like above to tell the async event how to read it (0 being player movement, 2 being bullets for example) in a case switch. How would I set something like above up?
Also, I assume the line you shared is set to confirm an order, ie 'if line_you_shared() = true { read the whole buffer like normal'? Or are you doing that for every subsequent buffer_read, ie:

obj = buffer_read(buffer, buffer_u32);
line_you_shared();
obj.speed = buffer_read(buffer, buffer_s16);
line_you_shared();
obj.damage = buffer_read(buffer, buffer_u16);
line_you_shared();

I assume isn't what you would say to do, right?
Thanks alot for your response.
The snippet is a general concept, so you might
Code:
if (buffer_tell(buffer) + 8 <= buffer_get_size(buffer)) { // 4 for u32 + 2 for s16 + 2 for u16
    obj = buffer_read(buffer, buffer_u32);
    obj.speed = buffer_read(buffer, buffer_s16);
    obj.damage = buffer_read(buffer, buffer_u16);
} else {
    // show a warning
}
 
Top