The Client

Finally, the coolest part of sockets! Also the most frustrating, but that's beside the point. (You'll be writing most of your code here)

I'd recommend following along the starter code. This time, we will be looking at dots_client.py.

NOTE: I'll be highlighting only the most important lines here

Basics (dots_client.py)

On a high level, each client runs their own individual model of the game, communicating to other clients through the server only when a change has happened. When they trigger an event, they tell the server what happened, and the server tells everyone else what happened. Then, the other clients take the message and update their individual model.

Some Logistics

import socket
import threading
from queue import Queue

HOST = ""
PORT = 50003

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

server.connect((HOST,PORT))

Obviously you need to import these modules and connect the sockets. Also make sure HOST and PORT values match the ones in your server file.

At the Bottom

serverMsg = Queue(100)
threading.Thread(target = handleServerMsg, args = (server, serverMsg)).start()

run(200, 200, serverMsg, server)

This creates a queue of messages received from the server (think bulletin board or to-do list) and makes a thread for the client to handle these incoming messages.

Notice how run is different?

It now takes in two more arguments, serverMsg and server (we created both already!). These two lines

data.server = server
data.serverMsg = serverMsg

are embedded in the function to integrate the server and serverMsg threads into your program.


Important Things

There are two things a client must do:

1. Send Instructions

Whenever any event happens that requires the model to change, you must communicate it to the server. Otherwise, only your model will change and other clients will be out of sync. Imagine if Abhi looked at memes himself and didn't share them with his relatives. How sad they (and he) would be! :(

For example, for moving a dot, we do the following:

data.me.move(dx, dy)
msg = "playerMoved %d %d\n" % (dx, dy)
...
data.server.send(msg.encode())

Two vital things:

Adding "\n" to the end of the message

This is crucial. It signifies that the message has ended. If you do not include this, the server will never know when your message is complete so they will stack up and never actually be sent out.

Encoding it

This is equally important. msg.encode() makes it possible to send the message by translating the message to unicode. Without .encode(), it would be like Abhi trying to send his meme in an 8 x 11 instead of an envelope. It just doesn't work until he encodes it by folding it up into a neat little envelope.

2. Receive Instructions

Since all the dot clients are run from the same file, all of them need to be able to both send and receive messages. Clients typically process instructions in timerFired (can you think of why?). This is how instructions get received.

while (serverMsg.qsize() > 0):
  msg = serverMsg.get(False)
  try:
    print("received: ", msg, "\n")
    msg = msg.split()
    command = msg[0]

    if (command == "myIDis"):
      myPID = msg[1]
      data.me.changePID(myPID)

  ...

    elif (command == "playerMoved"):
      PID = msg[1]
      dx = int(msg[2])
      dy = int(msg[3])
      data.otherStrangers[PID].move(dx, dy)

NOTE: the original code had an if instead of a while in the first line. This should be in a while loop so that commands will be executed faster. Please check to see that your version of the code has the while there. Your application will run faster :)

The first two lines get the instructions from serverMsg (remember, this is where messages from the server come in). Then for each command, we retrieve the command, and do whatever we need to do based on that command. We use a while loop here so we can execute all pending commands in one timerFired.

For example, the "myIDis" command only affects me, so I don't need to update anyone else in my model.

However, the "playerMoved" command requires that I update someone else, so I update the person named PID's position in my model.

Overall this concept is a bit confusing, so don't be discouraged! Just think about it for a bit or try testing it out and I'm sure you'll get it.


And That's About It!

That's pretty much what you need to know to code in sockets. Just send the right command when you do an action, and add a corresponding if statement in timerFired for the other clients to sync with your move.

results matching ""

    No results matching ""