How do I create a multiplayer "text game" ?

Just started using Construct 2? Post your questions here

Post » Sat Dec 10, 2016 4:27 pm

Upon connexion, every player gets the next value in the smart random list.

Oooooooohh...........
OOOHHH...

Now I see where you are going. That's actually a waaaay more intelligent solution than mine. Wow.
As long as you give that task to the host and the host only, it's fine.
I didn't understand it at first, I thought you want to give that task to every peer on connection.

(If you have problems with understanding and you didn't read it yet, take a look at this: Multiplayer tutorial: concepts)
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Sat Dec 10, 2016 11:43 pm

Ah ah thanks. It's actually combining several of your ideas that made me think about it.

I see one problem with using that solution though : in case anybody is disconnected and has to connect back, he will get a new number. So a single disconnection would ruin an entire gaming session. Well, disconnections are a problem anyway, it would be better if that does not happen. But that is something I should think about maybe (but later :) ).

Concerning the multiplayer, I read that tutorial, but it is still unclear to me how events work in multiplayer. What is hard to understand, in particular, is what is common to all players and what is specific to each. For example, when you have a global variable, it's neither in the host or peer related event so it applies to both, but when you put an event to change that variable, each player can get a specific value (like in our example above). Another example, when I create an object such as a dictionnary on any layout, how does this work in a multiplayer ? Does every player gets its own dictionnary or do they all write in the same one ?

And when you say to give that task to the host, do you mean the host is the one generating the smart random and then send their numbers to each peer ? Or even the peer has access to the smart random once it is create and so the host just has to create it ?
B
7
Posts: 18
Reputation: 311

Post » Sun Dec 11, 2016 2:44 am

in case anybody is disconnected and has to connect back, he will get a new number.

To prevent this, you could add an array or a dictionary that store each player's group number together with their Multiplayer Alias.
When a player connects, he will be assigned to his group and the host will save the player's alias to the dictionary.

In case one player disconnects, he simply has to login again with the same alias. The host will then assign the player the group number that is stored under his name in the dictionary.

_________________________________

what is common to all players and what is specific to each

Basically, there are three groups:
peer, host and common.

You can't be peer and host at the same time.
The first player that joins a multiplayer room, automatically becomes the host for that room.
The main difference between peers and the host is:
  • the host can broadcast messages to all peers
  • peers can only send messages to the host or to a specific peer
In the event sheet, you differentiate between these two to not include actions that can't be executed anyways (like trying to broadcast messages as a peer). You can also give host-specific tasks (like in your project) since the host is the only person that can broadcast messages.
(You can test whether you are host or not by checking the condition "is host")

__________________________________

when I create an object such as a dictionnary on any layout, how does this work in a multiplayer ?

You need to keep one thing in mind:
In Multiplayer, nothing apart from positions or angles can be directly shared between peers.

You need to distribute information via messages and by using the host's broadcasting ability.
([object].AsJSON is very helpful there).
Let's take the example of a dictionary:
  • The host would have the "ultimate" version of the dictionary
  • when something happens to a peer that needs to change the dictionary, the peer will send those information with an appropriate tag to the host
  • The host will read that message, edit his dictionary and broadcast his dictionary as JSON to all peers
  • The peers, on receiving the host's message, will load their dictionary from the JSON in the message
  • Now everybody has the same dictionary

Also remember that, most of the time, you don't need to create separate objects for the host and the peers. Like in the dictionary example, you could add one dictionary to your project, that is used differently by peer and host.

______________________________________________

And when you say to give that task to the host, do you mean the host is the one generating the smart random and then send their numbers to each peer ?

Exactly. It would be a mess if every user generated their own number.
We need a central user who stores the group numbers of every player for later use.
If every peer generated their own number and stored it locally, we would need to send that information one by one to each peer when it is needed. If it is stored at the host, he can simply broadcast what is needed for everyone and send messages to peers that require special information.
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Mon Dec 12, 2016 9:41 pm

Message: Zakeru can only post plain text URLS until they have 500 rep. 1 URLS modified. Why?
In case one player disconnects, he simply has to login again with the same alias. The host will then assign the player the group number that is stored under his name in the dictionary.


Well, actually I don't want them to have an alias since I don't want them to recognize each other in the actual room and try to communicate not using the chat. I deleted that option and made everybody's alias "player" so they will just be called "playerX". But actually, if anybody gets disconnected, then that player just has to connect back several times until he falls back in the right group (they will be informed of which group they are in). With the smart random set like this, it will take at most 3 tries to get back to the right group, so it's not a major problem.

Concerning the multiplayer, I think some things are a little more clear now, I will try a few things and send another capx soon so you can give me your opinion about it (well, if you want, of course).

Edit : - Just a naive question on the way smart random works. I see no way to ask for the "first" value in the meaning of "the first of the randomly generated string of value" but only in the meaning "lowest possible value (the opposite of end value)". So I suppose asking for the "next" value for the first time will actually give the first value in the string (and then the second for second time you ask for the "next" and so on). I don't know if I am being clear, but basically, I just want to be sure not to skip the first value everytime I use smart random just because I overlooked something.

- I know I haven't put every creature sprites in a family yet, but for now I put another smart random on the "spawn" value. So it changes from 1 to 30 (using all values only once before going to game over) as I wanted, but I still have no idea why my ugly big black numbers that I use as sprite for now dont want to appear. Any idea ?
I simply wanted to create something similar to what is shown here, but I can't figure out what I'm doing wrong : https://www. scirra.com/forum/create-random-object-every-x-seconds_t63037

==> Problem solved. I did a long list of 30 conditions to create the 30 sprite at the same time XD. I actually had to just separate them in different events. Yeaaaaah one fewer problem!

- Actually I have trouble now I am using families (I could get the license, yay !) :

Try this:

Add a family containing all the sprites, e.g."Sprites"
Add a family instance variable, e.g. "index"
Edit that variable for every Sprite and set it to the Sprite's number (don't set it in the event sheet, set it in the object properties)
Delete that long chain of conditions you referred to (Spawn=X, Spawn=Y,...) and replace it by this:

Image

____________________________________________________________


This does not seem to work, or maybe I am doing something wrong...
B
7
Posts: 18
Reputation: 311

Post » Thu Dec 15, 2016 5:22 pm

Hey ! Sorry for the double post, but I have done a lot of changes to the game so I wanted to post the new .capx : https://www. dropbox.com/s/tqpq5zedfi9iw1f/ChatProject.capx?dl=0

Thanks to your explanations and some testing, this whole multiplayer thing is getting quite clear. I now have a license and two computers connected in LAN to do some testing.

There are two points with which I am struggling :
- I can't find a way to synchronize the "timer" global value. Everybody should have the host "timer", but I see no way to broadcast a number value.
- I can't find a way to send to each peer, upon connection, the next value in the smart random to set there "Group" value.

And a less important one :
- Is there a way to simplify the list of 30 events for spawning creatures using family ?

I feel like dictionnaries and arrays will come in handy here, but I am not yet familiar with those. Anyway, next step will be implementing the scoring system, so I will have to learn about it quite soon.
B
7
Posts: 18
Reputation: 311

Post » Sun Dec 18, 2016 4:02 pm

Sorry that I didn't reply earlier.
I don't get a notification when you edit your post without mentioning my username.

Let's go through your questions one by one:
From the first post:
I see no way to ask for the "first" value in the meaning of "the first of the randomly generated string of value" but only in the meaning "lowest possible value (the opposite of end value)"

asking for the "next" value for the first time will actually give the first value in the string

Since Smart Random's Expression "next" starts at the minimum of the second value and only "start" returns the first value, you won't be able to simply always use "next".

I suppose you use a function.
You will call the function with the parameter of the random number index you want.
(If you want the first random number, call the function with parameter 1, for the second, choose parameter 2 and so on)
The function itself will check what the parameter ( Function.Param(0) ) is.
If it is 1, it will set the function's return value to "SmartRandom.Start".
If it is bigger than 1, it will set the function's return value to "SmartRandom.Next".

Like this, you can simply call this function with the appropriate parameter when you need a new random number.

_______________________________________

Actually I have trouble now I am using families

Try this .capx I created:
Capx Download

_________________________________________

From the second post:

I can't find a way to synchronize the "timer" global value. Everybody should have the host "timer", but I see no way to broadcast a number value.


When the "timer" value is changed by the host, add an action "Multiplayer: Broadcast message".
  • From ID: Leave this like it is, since "" indicates that the message comes from the host, which is what we want
  • Tag: Change to what you like, for example "timer"
  • Message: Obviously, you put your global variable in this.
  • Mode: Leave this like it is too. This is only really important for real-time games (e.g. Multiplayer Shooter)

Also, make sure that you add an event for the peers:
"Multiplayer: On message "timer": Set timer to: 'Multiplayer.Message'".
And make sure, no other action apart from this changes the global variable when you aren't the host.

__________________________________

I can't find a way to send to each peer, upon connection

There are multiple ways to do this.
First of all,
add this event:
"Multiplayer: On peer connected".
This triggers when somebody joins the same room.
Then, under that event, you can save the ID of the player that just connected or any other information you want by using "Multiplayer.From____" (e.g. FromID, FromAlias). This will return the information of the peer that just connected.

Instead, you could also use "Multiplayer: PeerAliasAt(PeerCount-1)/PeerIDAt(PeerCount-1).
This will return the same values since the player that just connected has to be the most recent player in the room and therefore, this will return his information. ("-1" since PeerAliasAt needs an index which starts at 0 and the PeerCount starts at 1)


When you saved those values (to a dictionary, for example), you can send the needed information, e.g. the group number, to this peer using the aforementioned values.
You need to add an action "Multiplayer: Send message" under the "On peer connected" event.
As the peer ID, use the values mentioned above.
The rest is the same as above.

(Note: you don't have to save the ID/Alias, but it might be useful later)

__________________________________________

Is there a way to simplify the list of 30 events for spawning creatures using family ?

See the .capx I posted above.

_________________________________________________________-

I feel like dictionnaries and arrays will come in handy here, but I am not yet familiar with those. Anyway, next step will be implementing the scoring system, so I will have to learn about it quite soon.

Just tell me where you need help if the C2 manual didn't help you.
Dictionary manual entry
Array manual entry
________________________________________________________
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Mon Dec 19, 2016 1:35 pm

Hey ! No problem, it's very cool that you keep answering and helping me :D

So, concerning the synchronization of global values, this is pretty much the same idea I had (found in this thread : https://www. scirra.com/forum/how-do-i-sync-global-variables-over-multiplayer_t101878). The problem is that the peer always gets a NaN value for the timer. Apparently, there is a problem with sending a variable as a message. The guy in the thread had the same problem and found an alternative solution which is not clearly explained, so I am stuck here.

I will try the other stuff too and come back. (Just mentioning "randomly" in my edit will give you a notification ?)

Edit @randomly : Concerning the use of families. I looked at your .capx and I understand quite clearly how this work, but still, I can't think of a way to spawn my creatures according to my "Spawn" variable. As you can see in my .capx, I already have a "Creatures" family with all the sprites in it, and I have several events using this like : on start of layout > delete "Creatures" (and that's convenient). I also have the "index" instance variable set accordingly for each sprite, as you explained me. But I don't know how to say to the game "you have to spawn the creature who's "Index" value is egal to the current "Spawn" value". I tried your idea of "picking "Creature" by evaluating "Index = Spawn" but that did not work. That's the only detail that I am stuck with.

- Concerning the smart random "next" expression. After some testing, it appears to be more simple than what you explained. "Start" expression always give you the lowest possible value in the range (useless in my case), but "next" actually starts from the first value of the sequence. Proof is in this .capx : https://www. dropbox.com/s/5l7ztd8kndlsov8/Next.expression-test.capx?dl=0

Notice that the two first value you get are always different, which proves they belong to the same loop, thus the first time you click on "next" you really get the first value in the sequence. If you started at the second value, skipping the first one, then you would start at the edge of a loop and have a 50% chance that the first value of the next loop is the same.

Illustration : with a sequence like 2-1 / 1-2 / 1-2 / 2-1 / ...

The two first value you get using only "next" is this : 2-1 / 1-2 / 1-2 / 2-1 / ...
And never : 2-1 / 1-2 / 1-2 / 2-1 / ...

Which is what I want anyway. So that's cool. Now I have to think of the way to always send the "next" value to every peer upon connection. Of course, I tried "upon peer connected" > send message, tag "Group", message "Groupgenerator.Next". But that wouldn't work since "Groupgenerator.Next" is a number and not a string (damn, that limitation is troublesome).

Here is another idea I just tried, but it failed (too bad! I was confident this time ah ah) : I created a "GlobalValues" dictionnary. The host task is to add a "Timer" key every second with the value "Timer" (global variable's current value), and add a key "Group" on peer connected, with the value "GroupGenerator.Next". Then, broadcast a message every second, tag "Timer", message "GlobalValues.Get(Timer)" and send a message on peer connected, ID "Multiplayer.FromID", tag "Group", message "GlobalValues.Get(Group)". For the peers : on peer message, tag "Timer"/"Group", set "Timer/Group" (Global variable) to "Multiplayer.message".

And of course, it doesn't work for both : "Timer" gets a NaN value and "Group" doesn't change at all.

- Concerning dictionnaries, I think I have a hard time understanding how the informations can be transmitted from host to peer. It seems the "AsJSON" expression is the key, but I don't really get it. For example, I suppose I could send the "GlobalVariables" Dictionnary as JSON and make the peer load it somehow and then set their global values accordingly. But I have troubles with this whole "host transforms into JSON > send to peers > Peers transform back to dictionnary objet" process. I think I would need a concrete example on that one.
B
7
Posts: 18
Reputation: 311

Post » Mon Dec 19, 2016 7:14 pm

Apparently, there is a problem with sending a variable as a message.

Try wrapping a "str()" tag around the global variable.
So that the message is 'str(variable)'.

_____________________________________________-

As you can see in my .capx, I already have a "Creatures" family with all the sprites in it

Tried to look at your .capx, your file doesn't exist anymore under the link you posted.
Please update the link.

____________________________________________

I tried your idea of "picking "Creature" by evaluating "Index = Spawn" but that did not work

This is exactly how it should work.
Put the whole family in the picking event and set the condition as "Self.Index = [your_variable]".
If that doesn't work, then it's the Spawn variable that is the problem.

_____________________________________________

But that wouldn't work since "Groupgenerator.Next" is a number and not a string (damn, that limitation is troublesome).

I see, you are not familiar with "str()" and "int()" yet.
You can remove number or string limitations by putting "str()" or "int()" around your variable.
So when the expression needs to be a number, use "int(variable)".
This will automatically take all the numbers that are in the string and make a number out of that.

When you need a string, use "str(variable)".
This will convert numbers or anything else to a string so you can e.g. send it via Multiplayer.
Just remember to convert it back when receiving a message if needed.

_________________________________________

Here is another idea I just tried, but it failed (too bad! I was confident this time ah ah) : I created a "GlobalValues" dictionnary. The host task is to add a "Timer" key every second with the value "Timer" (global variable's current value), and add a key "Group" on peer connected, with the value "GroupGenerator.Next". Then, broadcast a message every second, tag "Timer", message "GlobalValues.Get(Timer)" and send a message on peer connected, ID "Multiplayer.FromID", tag "Group", message "GlobalValues.Get(Group)". For the peers : on peer message, tag "Timer"/"Group", set "Timer/Group" (Global variable) to "Multiplayer.message".


May I ask what the "timer" variable is for?
And what the exact purpose of the dictionary and the whole process around it is?
I don't seem to grasp it yet.

Broadcasting and using dictionary values shouldn't be too hard, so if you tell me what the purpose of it is, I can help you with it more easily.

Then, broadcast a message every second, tag "Timer", message "GlobalValues.Get(Timer)"

Also, why don't you just broadcast the variable directly?
Why does it have to be in the dictionary first?

________________________________________________________

But I have troubles with this whole "host transforms into JSON > send to peers > Peers transform back to dictionnary objet" process. I think I would need a concrete example on that one.


As simple as this:
Image
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Tue Dec 20, 2016 10:07 am

Thank you again for your answer.

Also, why don't you just broadcast the variable directly?
Why does it have to be in the dictionary first?


It was just an alternative solution I wanted to try because I already tried and failed with sending the variable directly. I also already tried with "int(variable)" but the peers always get 0 (the same problem happened in the thread I sent on my previous message). I will try with "str(variable)" this time, hopefully that will work.

The "timer" is here so that everybody as X seconds to answer (probably 90 seconds for each creature). I want players to be synchronized on that and the timer to be displayed for them. The first round will be empty of creature, just so players have time to connect and synch with host and exchange a few messages with other players in their group. Then, every 90 seconds the content of "AnswerText" is sent to host and then comes the scoring system (not yet implemented).

Concerning the dictionnaries, thank you for this simple example, this is what I thought but I wasn't sure. I did not have time to test a lot of stuff and everything failed so far, so I supposed I was doing something wrong.

And here is the latest .capx : https://www. dropbox.com/s/i9d67roqu2jtql6/ChatProject.capx?dl=0

On the debug, I can see the "Spawn" variable changine exactly as I want to, so I don't think that is the problem. But I may be wrong ^^

Edit @randomly : YEEAAAAAAHHHH !!! The str(variable) worked ! I can't believe I spent so much time on such a trivial thing ! But I guess that is what we call "learning" lol. Thank you ! Next : the scoring system. I will try something and send a new .capx.

- Arf, actually it doesn't work with the "Group" value. I think I am doing something wrong with the "send message" action. I tried :
For host : On peer connected >> send message, ID : Multiplayer.FromID, tag : "Group", message "str(GroupGenerator.Next)
For peer : On peer message, tag "Group", set "Group" to "Multiplayer.Message"

And since that did not work, I tried to first save "Multiplayer.FromID" in a global variable or in a dictionary and sending to that value instead, but it doesn't work either.

"Timer" works fine because I use the "broadcast message" action instead of "send message".
B
7
Posts: 18
Reputation: 311

Post » Tue Dec 20, 2016 3:33 pm

I want players to be synchronized on that and the timer to be displayed for them.

I would just have the host broadcast one single message that will start a timer for the peers.
Just use the "timer" behavior and start that when receiving the host's message.

Of course, this will be client-dependant but less tedious than regularly sending a variable.

You could also start a "timer" behavior for the host and use the "duration" expression minus the "currentTime" expression to broadcast the remaining time.

That would make the dictionary obsolete.

_________________________________

And here is the latest .capx : https://www. dropbox.com/s/i9d67roqu2jtql6/ChatProject.capx?dl=0

Link doesn't work for me. 404.
(Yes, I removed the blank space)

______________________________________

Arf, actually it doesn't work with the "Group" value. I think I am doing something wrong with the "send message" action.


Actually, I think that this
Code: Select all
For peer : On peer message, tag "Group", set "Group" to "Multiplayer.Message"

is the issue.
Try changing the "group" value to int(Multiplayer.Message).
See my previous post:
randomly wrote:This will convert numbers or anything else to a string so you can e.g. send it via Multiplayer.
Just remember to convert it back when receiving a message if needed.


Btw, as a future tip:
try to test whether messages are actually received with a simple debugging text object before changing the actions/methods.
(Just add a text object and change it to "Multiplayer.Message" on Peer message to check whether the host or peer is the issue)

___________________________________

And since that did not work, I tried to first save "Multiplayer.FromID" in a global variable or in a dictionary and sending to that value instead, but it doesn't work either.

Chuck that. Isn't really necessary since the expression already works as a global variable of some sort.
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

PreviousNext

Return to Beginner's Questions

Who is online

Users browsing this forum: No registered users and 1 guest