Return always returns to wherever it was called from, yes.
The server *can* have everything happen in it, but this rarely happens in real multiplayer games. It is inefficient for the server to run the game, with all of the collision code and everything, especially since a server might actually host the data for several games at once! But it comes down to design decisions with your game. I don't know how your game is structured, but I'll make up a simple example, and then describe one way it could be done. I hope that this won't be too vague...
We have a game consisting of 3 objects: The client-side controller object, the playable player object, and the "dumb" player object. The "dumb" player object is an object that draws a player, but does not have any events set up for responding to player inputs like keyboard presses and stuff. The playable player object does have controls.
Suppose that at the start of the game, 5 players appear in a room together. Let's say we see them from the top-down.
Each player is at a separate computer. When the game begins, an object is created in the room, and the object represents that one specific player.
The player's object responds to keyboard inputs as usual.
But then, there's a second object--the client-side controller object. This object establishes a connection to the server. It receives a buffer containing specific pieces of information. Maybe something like this:
(number of players) (ID of receiving player) (x-position of first player) (y-position of first player) (direction of first player) (x-position of second player) (y-position of second player) (direction of second player) ......and so on.
The controller object checks the first piece of data in the buffer. It's a number. This is the number of connected players. The controller object counts how many "dumb" player type objects exist in the room. There
should be 1 less "dumb" player object than the number of players. In other words, if there's 5 players in the game, then there should be 4 "dumb" player objects in the room. And one object that can be controlled by the player.
Of course, when the game first gets going, there's not
any "dumb" player objects yet. Just the one controllable one.
Soooo, you do a loop. This would be inside of an async event, when a network message is recieved. This is not complete code, but an example of one portion.
Code:
var numPlayers = buffer_read( myBuffer, buffer_u8);
var numDumb = instance_count(obj_dumbPlayer);
var numToAdd = (numPlayers-1) - numDumb;
for(var i=0; i<numToAdd; i+=1){
newObject = instance_create(0,0, obj_dumbPlayer);
}
Does the code above make sense? I hope so? It's not code that you can use as-is.... It assumes the existence of something called myBuffer, which is a buffer that would come from the server. It also assumes that there's a 1-byte value at the start of the buffer, which says how many players are in the game. But more importantly, do you see how one object could create many "dumb" player objects?
Networking is almost beside the point--the variable numPlayers
could have been set in the Create event, if this was a single player game with NPC players. Instead, it's set by the value from a server. Either way, same result: The controller object makes a set of objects equal to the number needed to make a full set of players.
So, we know that the player can control their own object...but how do the other objects move around?
Well, suppose that the server is constantly sending buffers like the example I mentioned. Every time a new one is sent, it contains the current number of players, followed by the x,y,and direction for each object. The controller just needs to use that info. Remember the examples where I was using an object's ID number to set a value for that object? We'll need to do that now...
And that's where data structures are super handy again. Instead of storing the
return from instance_create as a single variable, newObject, which will be overwritten every time a new object is made, you could add the value of newObject to a ds_list.
Once you have a list of object ID numbers, it's easy:
Loop through the list, dealing with 1 object at a time
For the first object in the list, apply the first x coordinate, y coordinate, and direction.
Move on to the next item in the list and next trio of data pieces read from the buffer.
Repeat until you're through the list.
Easy, right? The controller object has a list of "dumb" objects and a buffer full of data groups that need to be applied.
The one slight hiccup is that the numbering might have to adjust:
Suppose I'm player number 3, right? In programming, we start counting at zero, so I'm the 4th player--but number 3. Here's the buffer from the example above, using abbreviations:
[N][P][x0][y0][d0][x1][y1][d1][x2][y2][d2]
[x3][y3][d3][x4][y4][d4]
I've hilighted in red the portion of the buffer that would correspond to our player character. The rest of the sets of data would control "dumb" players.
Now, suppose that the controller object made 4 "dumb" player objects in a list. That looks like this:
[obj0]
[obj1]
[obj2]
[obj3]
So, we can just apply x0, y0, d0 from the buffer to obj0 from the list.
And we can apply x1, y1, d1 from the buffer to obj1 from the list.
And x2, y2, d2 can apply to obj2.
But you can't apply x3, y3, d3 to obj3 in the list. Remember, since I'm player #3, that data set in the buffer does not apply anyone in the list.
I assume that the logic is easy to work around though, right?
As you look through the list, you just check whether the player number (second piece of data in the buffer) is equal to the current set of data you're reading from the buffer. If so, skip it and move to the next one.
It's a lot of words to say it all out, but the concept is really simple. You have a list of "data sets" (x, y coordinates and other info). You have a list of "dumb" player objects you've created. Apply the data to the objects.
As for the
while()... Do what you understand best. If, later on, it becomes more clear why small changes in style might be more time-saving or whatever, it's easy to change.