title: Choppy online python game
body: <p>I have programmed a simple clone of <a href="https://slither.io" rel="nofollow noreferrer">Slither.io</a> in python using pygame and sockets and I have three problems:</p>
  <ol>
  <li>When I play the game alone on my laptop the game is choppy. Every ten seconds my game get stuck for a while (one milisecond) and then continue. It's not a big problem but it's annoying.</li>
  <li>When I play on two computers in my local network, I see the other player (the other snake) is also choppy.</li>
  <li>The strangest problem is when I run my server on my main laptop and then run the game on my second laptop the game starts and after few seconds crash. Debugger on client says that pickle data was truncated while receiving data from the server. But when I run the server program on my second laptop and the game on my main laptop everything is OK. Why?</li>
  </ol>
  <hr />
  <p>I tried:<br>
  Problem 1. change FPS on the client and time.sleep on the server<br>
  Problem 2. change time.sleep on the server<br>
  Problem 3. change the input value of recv() method</p>
  <hr />
  <p>Server code:</p>
  <pre><code>import socket
  import threading
  import pickle
  import random
  import time
  import math
  
  ip = socket.gethostbyname(socket.gethostname())
  port = 5555
  
  address = (ip, port)
  server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  snakes = []
  colors = [(80, 0, 0), (0, 80, 0), (0, 0, 140), (80, 80, 0), (80, 0, 80), (0, 80, 80)]
  food = []
  
  for i in range(0, 300, 1):
      food.append([random.randint(10, 3290), random.randint(10, 2090), (0, 0, 100), 10, 1])
  
  number_of_players = 0
  
  def start(server, address):
      server.bind(address)
  
  def new_clients(server):
      global number_of_players
      server.listen()
      while True:
          conn, addr = server.accept()
          thread = threading.Thread(target=client, args=(conn, addr, number_of_players))
          thread.start()
          number_of_players += 1
          print(f&quot;Active connections: {threading.activeCount() - 1}&quot;)
  
  def client(conn, addr, player):
      print(f&quot;New connection: {addr}\n&quot;)
      global snakes
      global colors
      global food
      try:
          snakes.append([])
          name = pickle.loads(conn.recv(1024))
          while True:
              verity = True
              x = random.randint(100, 3200)
              y = random.randint(100, 2000)
              for i in range(0, len(snakes), 1):
                  if snakes[i] != []:
                      if math.sqrt((snakes[i][0][0] - x)**2 + (snakes[i][0][1] - y)**2) &gt; snakes[i][0][4]*2:
                          for a in range(0, len(snakes[i][1])):
                              if math.sqrt((snakes[i][1][a][0] - x)**2 + (snakes[i][1][a][1] - y)**2) &lt; snakes[i][0][4]*2:
                                  verity = False
                              if verity == False:
                                  break
                      else:
                          verity = False
                      if verity == False:
                          break
              if verity == True:
                  break
  
          snakes[player] = [[x, y, random.choice(colors), 4, 30, name, 0, 3], []]
          conn.send(pickle.dumps(snakes[player]))
      except (ConnectionAbortedError, ConnectionResetError, EOFError):
          print(&quot;Connection lost&quot;)
          print(f&quot;Active connections: {threading.activeCount() - 2}&quot;)
          conn.close()
          return
      while True:
          try:
              snakes_to_send = []
              for i in range(0, len(snakes), 1):
                  if (i != player) and (len(snakes[i]) != 0):
                      snakes_to_send.append(snakes[i])
              conn.send(pickle.dumps([snakes_to_send, food]))
  
              received = pickle.loads(conn.recv(8192*4))
              snakes[player] = received[0]
  
              eaten = received[1]
              for i in range(0, len(eaten), 1):
                  try:
                      food.remove(eaten[i])
                      if eaten[i][4] != 5:
                          food.append([random.randint(10, 3290), random.randint(10, 2090), (0, 0, 100), 10, 1])
                  except ValueError:
                      continue
  
              dead_snake = received[2]
              if dead_snake != []:
                  for i in range(0, len(dead_snake), 1):
                      food.append(dead_snake[i])
  
              time.sleep(1/1000)
          except (ConnectionAbortedError, ConnectionResetError, EOFError):
              snakes[player] = []
              print(&quot;Connection lost&quot;)
              print(f&quot;Active connections: {threading.activeCount() - 2}&quot;)
              conn.close()
              break
  
  print(f&quot;Server is starting on IP: {ip} and PORT: {port}&quot;)
  start(server, address)
  new_clients(server)
  </code></pre>
  <hr />
  <p>The game:</p>
  <pre><code>#import
  import pygame
  import sys
  import math
  import random
  import pygame_textinput
  from client import Client
  
  #pygame initialize
  pygame.init()
  win = pygame.display.set_mode((1100, 700))
  pygame.display.set_caption(&quot;Slither.io&quot;)
  
  #connect to the server
  ip = &quot;10.0.0.3&quot;
  port = 5555
  c = Client(ip, port)
  c.server()
  
  #fonts
  font = pygame.font.SysFont(&quot;comicsansms&quot;, 20)
  big_font = pygame.font.SysFont(&quot;comicsansms&quot;, 35)
  
  #time
  clock = pygame.time.Clock()
  
  class Snake:
      def __init__(self, x, y, color, length, diameter, name, score, step):
          self.x = x
          self.y = y
          self.color = color
          self.length = length
          self.diameter = diameter
          self.name = name
          self.score = score
          self.step = step
          self.pos_list = []
  
      #move the snake
      def move(self):
          self.mousex, self.mousey = pygame.mouse.get_pos()
          self.difx = self.mousex - 550
          self.dify = self.mousey - 350
          try:
              self.xstep = math.sqrt((self.step**2)/((self.dify/self.difx)**2 + 1))
              self.ystep = (self.dify/self.difx)*self.xstep
          except ZeroDivisionError:
              self.xstep = 0
              self.ystep = self.step
          if math.sqrt(self.difx**2 + self.dify**2) &gt; self.diameter/3:
              if(self.difx &gt; 0):
                  self.x += self.xstep
                  self.y += self.ystep
              elif(self.difx &lt; 0):
                  self.x -= self.xstep
                  self.y -= self.ystep
              else:
                  if self.dify &gt; 0:
                      self.y += self.ystep
                  else:
                      self.y -= self.ystep
          self.pos_list.append([self.x, self.y])
          if len(self.pos_list) == 100:
              del self.pos_list[0]
  
      #check if the snake isn't out of the area or not crashed
      def collision(self):
          global run
          if (self.x + self.diameter/3 &gt;= 3299) or (self.x - self.diameter/3 &lt;= 1) or (self.y + self.diameter/3 &gt;= 2099) or (self.y - self.diameter/3 &lt;= 1):
              run = False
          for i in range(0, len(other_snakes), 1):
              if math.sqrt((other_snakes[i].x - snake.x)**2 + (other_snakes[i].y - snake.y)**2) &lt; other_snakes[i].diameter*(2/3):
                  run = False
          for i in range(0, len(other_segments), 1):
              if math.sqrt((other_segments[i].x - snake.x)**2 + (other_segments[i].y - snake.y)**2) &lt; other_segments[i].diameter*(2/3):
                  run = False
  
      #draw all snakes
      def draw(self):
          pygame.draw.rect(win, self.color, [self.x - snake.x + 550 - self.diameter/2, self.y - snake.y + 350 - self.diameter/2, self.diameter, self.diameter], border_radius=int(self.diameter/2))
  
  class Segment(Snake):
      def __init__(self, x, y, diameter, step, circle):
          self.x = x
          self.y = y
          self.color = (80, 80, 80)
          self.diameter = diameter
          self.step = step
          self.circle = circle
          self.pos_list = []
  
      def move(self):
          self.diameter = snake.diameter
          try:
              self.x = self.circle.pos_list[-int(self.diameter/self.step)][0]
              self.y = self.circle.pos_list[-int(self.diameter/self.step)][1]
          except IndexError:
              self.x = self.circle.pos_list[0][0]
              self.y = self.circle.pos_list[0][1]
          self.pos_list.append([self.x, self.y])
          if len(self.pos_list) == 100:
              del self.pos_list[0]
  
  class Food(Snake):
      def __init__(self, x, y, color, diameter, points):
          self.x = x
          self.y = y
          self.color = color
          self.diameter = diameter
          self.points = points
  
  other_snakes = []
  segments = []
  other_segments = []
  food = []
  eaten = []
  dead_snake = []
  
  #get other snakes
  def return_snakes():
      global other_snakes
      global other_segments
      global food
      other_snakes = []
      other_segments = []
      food = []
      message = c.receive_message()
      received = message[0]
      for i in range(0, len(received), 1):
          s = Snake(received[i][0][0], received[i][0][1], received[i][0][2], received[i][0][3], received[i][0][4], received[i][0][5], received[i][0][6], snake.step)
          other_snakes.append(s)
          for a in range(0, len(received[i][1]), 1):
              sg = Segment(received[i][1][a][0], received[i][1][a][1], s.diameter, snake.step, 0)
              other_segments.append(sg)
      rec_fd = message[1]
      for i in range(0, len(rec_fd), 1):
          f = Food(rec_fd[i][0], rec_fd[i][1], rec_fd[i][2], rec_fd[i][3], rec_fd[i][4])
          food.append(f)
  
  #send the snake to server
  def send_snake():
      global snake
      global segments
      global food
      to_send = [[[snake.x, snake.y, snake.color, snake.length, snake.diameter, snake.name, snake.score], []]]
      for i in range(0, len(segments), 1):
          to_send[0][1].append([segments[i].x, segments[i].y])
      to_send.append(eaten)
      to_send.append(dead_snake)
      c.send_message(to_send)
  
  #function for ordering snakes by score
  def order(o):
      return o[&quot;score&quot;]
  
  #draw screen
  def screen():
      for i in range(0, 3301, 100):
          pygame.draw.line(win, (60, 60, 60), [i - snake.x + 550, 0 - snake.y + 350], [i - snake.x + 550, 2100 - snake.y + 350], 1)
      for i in range(0, 2101, 100):
          pygame.draw.line(win, (60, 60, 60), [0 - snake.x + 550, i - snake.y + 350], [3300 - snake.x + 550, i - snake.y + 350], 1)
  
  #enter your nickname
  textinput = pygame_textinput.TextInput(font_family=&quot;comicsansms&quot;, max_string_length=12)
  while True:
      win.fill((100, 100, 100))
      events = pygame.event.get()
      for event in events:
          keys = pygame.key.get_pressed()
          if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() &amp; pygame.KMOD_ALT:
              sys.exit()
      if keys[pygame.K_RETURN]:
          break
      name = big_font.render(&quot;Nickname:&quot;, True, (0, 0, 0))
      center = name.get_rect(center=(400, 350))
      win.blit(name, center)
      textinput.update(events)
      win.blit(textinput.get_surface(), (500, 325))
      pygame.display.update()
  
  c.send_message(textinput.get_text())
  received = c.receive_message()
  snake = Snake(received[0][0], received[0][1], received[0][2], received[0][3], received[0][4], received[0][5], received[0][6], received[0][7])
  
  segment = Segment(snake.x, snake.y, snake.diameter, snake.step, snake)
  segments.append(segment)
  for i in range(0, snake.length - 2, 1):
      segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
      segments.append(segment)
  
  while True:
      run = True
      while run:
          eaten = []
          #quit game
          for event in pygame.event.get():
              keys = pygame.key.get_pressed()
              if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() &amp; pygame.KMOD_ALT:
                  sys.exit()
  
          return_snakes()
  
          snakes = [{&quot;name&quot;: snake.name, &quot;score&quot;: snake.score}]
          for i in range(0, len(other_snakes), 1):
              snakes.append({&quot;name&quot;: other_snakes[i].name, &quot;score&quot;: other_snakes[i].score})
  
          win.fill((100, 100, 100))
          screen()
  
          #draw all food
          for i in range(0, len(food), 1):
              food[i].draw()
  
          #draw the other snakes and describe
          for i in range(0, len(other_segments), 1):
              other_segments[i].draw()
  
          for i in range(0, len(other_snakes), 1):
              other_snakes[i].draw()
  
              name = font.render(other_snakes[i].name, True, (0, 0, 0))
              center = name.get_rect(center=(other_snakes[i].x - snake.x + 550, other_snakes[i].y - snake.y + 350))
              win.blit(name, center)
  
          #move, draw snake and check if the snake is alive
          snake.move()
          for i in range(0, len(segments), 1):
              segments[i].move()
          for i in range(0, len(segments), 1):
              segments[i].draw()
          snake.draw()
          snake.collision()
  
          #check if the snake ate food and add score
          for i in range(len(food) - 1, -1, -1):
              if math.sqrt((food[i].x - snake.x)**2 + (food[i].y - snake.y)**2) &lt; (snake.diameter/2 + food[i].diameter/2):
                  snake.score += food[i].points
                  snake.length = int(snake.score/10) + 4
                  if snake.length - 1 &gt; len(segments):
                      segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
                      segments.append(segment)
                  snake.diameter = int(snake.score/50)*0.5 + 30
                  eaten.append([food[i].x, food[i].y, food[i].color, food[i].diameter, food[i].points])
  
          #describe the snake
          name = font.render(snake.name, True, (0, 0, 0))
          center = name.get_rect(center=(550, 350))
          win.blit(name, center)
  
          send_snake()
  
          #sort the snakes by score
          snakes.sort(key=order, reverse=True)
  
          #create the table
          for i in range(0, len(snakes), 1):
              table = font.render(str(snakes[i][&quot;name&quot;]), True, (0, 0, 0))
              win.blit(table, (900, 50 + i*30))
          for i in range(0, len(snakes), 1):
              table = font.render(str(snakes[i][&quot;score&quot;]), True, (0, 0, 0))
              win.blit(table, (1050, 50 + i*30))
          for i in range(0, len(snakes), 1):
              table = font.render(str(i + 1) + &quot;.&quot;, True, (0, 0, 0))
              win.blit(table, (870, 50 + i*30))
  
          pygame.display.update()
          clock.tick(140)
  
      return_snakes()
  
      #create food from head of dead snake
      ds = [snake.x, snake.y, snake.color, 15, 5]
      dead_snake.append(ds)
  
      #create food from segments of dead snake
      for i in range(0, len(segments), 1):
          ds = [segments[i].x, segments[i].y, snake.color, 15, 5]
          dead_snake.append(ds)
  
      send_snake()
      dead_snake = []
  
      clock.tick(140)
  
      c.client.close()
  
      while True:
          #quit game
          for event in pygame.event.get():
              keys = pygame.key.get_pressed()
              if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() &amp; pygame.KMOD_ALT:
                  sys.exit()
          if keys[pygame.K_SPACE]:
              c = Client(ip, port)
              c.server()
              c.send_message(snake.name)
              received = c.receive_message()
              snake = Snake(received[0][0], received[0][1], received[0][2], received[0][3], received[0][4], received[0][5], received[0][6], received[0][7])
              segments = []
              segment = Segment(snake.x, snake.y, snake.diameter, snake.step, snake)
              segments.append(segment)
              for i in range(0, snake.length - 2, 1):
                  segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
                  segments.append(segment)
              break
  
          win.fill((100, 100, 100))
          screen()
  
          #press space bar to continue
          cont = big_font.render(&quot;Press space bar to continue&quot;, True, (0, 0, 0))
          center = cont.get_rect(center=(550, 350))
          win.blit(cont, center)
  
          pygame.display.update()
          clock.tick(140)
  </code></pre>
  <hr />
  <p>Client class file:</p>
  <pre><code>import socket
  import pickle
  
  ip = &quot;10.0.0.3&quot;
  port = 5555
  
  class Client:
      def __init__(self, ip, port):
          self.ip = ip
          self.port = port
          self.address = (self.ip, self.port)
          self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
      def server(self):
          try:
              self.client.connect(self.address)
              print(f&quot;Successfully connected to {self.address}&quot;)
          except:
              pass
  
      def send_message(self, message):
          self.client.sendall(pickle.dumps(message))
  
      def receive_message(self):
          return pickle.loads(self.client.recv(8192*4))
  </code></pre>
  <hr />
  <p>And this is <a href="https://github.com/Nearoo/pygame-text-input" rel="nofollow noreferrer">pygame_textinput</a> library I'm using here.</p>