import socket
import threading
import json
import random
import time

# Server configuration
HOST = '0.0.0.0'
PORT = 5555

# Global variables
rooms = {}
room_id_counter = 1
lock = threading.Lock()

class Player:
    def __init__(self, conn, addr, name):
        self.conn = conn
        self.addr = addr
        self.name = name
        self.snake = []
        self.direction = 'UP'
        self.alive = True
        self.current_room = None  # Track the room the player is currently in

class Room:
    def __init__(self, room_id, host_player):
        self.room_id = room_id
        self.players = [host_player]
        self.game_state = {
            'snakes': {},
            'food': (random.randint(0, 39), random.randint(0, 39))  # Adjusted for larger game size
        }
        self.running = False
        self.lock = threading.Lock()
        self.host = host_player  # The host player

    def start_game(self):
        self.running = True
        threading.Thread(target=self.game_loop).start()

    def game_loop(self):
        while self.running:
            self.update_game_state()
            self.send_game_state()
            alive_players = [player for player in self.players if player.alive]
            if len(alive_players) <= 1:
                self.running = False
                self.send_game_over()
                break
            time.sleep(0.05)  # Reduced sleep time for smoother updates
        # Notify players that the game has ended and reset their states
        self.reset_players()
        # Remove room after game over
        with lock:
            if self.room_id in rooms:
                del rooms[self.room_id]

    def update_game_state(self):
        with self.lock:
            for player in self.players:
                if not player.alive:
                    continue
                head_x, head_y = player.snake[0]
                direction = player.direction
                if direction == 'UP':
                    head_y -= 1
                elif direction == 'DOWN':
                    head_y += 1
                elif direction == 'LEFT':
                    head_x -= 1
                elif direction == 'RIGHT':
                    head_x += 1
                # Adjusted for larger game size
                head_x %= 40
                head_y %= 40
                new_head = (head_x, head_y)
                if new_head in player.snake:
                    player.alive = False
                    continue
                for other_player in self.players:
                    if other_player != player and new_head in other_player.snake:
                        player.alive = False
                        break
                if not player.alive:
                    continue
                player.snake.insert(0, new_head)
                if new_head == self.game_state['food']:
                    self.game_state['food'] = (random.randint(0, 39), random.randint(0, 39))
                else:
                    player.snake.pop()
            self.game_state['snakes'] = {player.name: player.snake for player in self.players if player.alive}

    def send_game_state(self):
        with self.lock:
            game_state_message = {
                'type': 'GAME_UPDATE',
                'data': self.game_state
            }
            message = json.dumps(game_state_message)
            for player in self.players:
                try:
                    player.conn.sendall((message + '\0').encode())
                except:
                    pass

    def send_game_over(self):
        game_over_message = {
            'type': 'GAME_OVER'
        }
        message = json.dumps(game_over_message)
        for player in self.players:
            try:
                player.conn.sendall((message + '\0').encode())
            except:
                pass

    def reset_players(self):
        for player in self.players:
            player.alive = True
            player.direction = 'UP'
            player.snake = []
            player.current_room = None  # Reset current room
            # Notify player to return to menu
            reset_message = {
                'type': 'RETURN_TO_MENU'
            }
            try:
                player.conn.sendall((json.dumps(reset_message) + '\0').encode())
            except:
                pass

def handle_client(conn, addr):
    global room_id_counter
    player = None
    try:
        buffer = ''
        while True:
            data = conn.recv(4096).decode()
            if not data:
                break
            buffer += data
            while '\0' in buffer:
                message_data, buffer = buffer.split('\0', 1)
                if not message_data:
                    continue
                message = json.loads(message_data)
                if message['type'] == 'SET_NICKNAME':
                    nickname = message['nickname']
                    player = Player(conn, addr, nickname)
                elif message['type'] == 'CREATE_ROOM':
                    if player is None:
                        # Must set nickname first
                        response = {'type': 'ERROR', 'message': 'Set nickname first'}
                        conn.sendall((json.dumps(response) + '\0').encode())
                        continue
                    with lock:
                        room_id = str(room_id_counter)
                        room_id_counter += 1
                        room = Room(room_id, player)
                        rooms[room_id] = room
                    player.current_room = room
                    player.snake = [(20, 20)]  # Starting position adjusted for larger game size
                    response = {
                        'type': 'ROOM_CREATED',
                        'room_id': room_id
                    }
                    conn.sendall((json.dumps(response) + '\0').encode())
                    # Wait for host to start the game
                elif message['type'] == 'JOIN_ROOM':
                    if player is None:
                        response = {'type': 'ERROR', 'message': 'Set nickname first'}
                        conn.sendall((json.dumps(response) + '\0').encode())
                        continue
                    room_id = message['room_id']
                    with lock:
                        if room_id in rooms:
                            room = rooms[room_id]
                            if len(room.players) < 5 and not room.running:
                                room.players.append(player)
                                player.current_room = room
                                player.snake = [(20 + len(room.players), 20)]
                                response = {
                                    'type': 'JOINED_ROOM',
                                    'room_id': room_id
                                }
                                conn.sendall((json.dumps(response) + '\0').encode())
                                # Notify host that a new player has joined
                                notify_message = {
                                    'type': 'PLAYER_JOINED',
                                    'player_name': player.name
                                }
                                room.host.conn.sendall((json.dumps(notify_message) + '\0').encode())
                            else:
                                response = {
                                    'type': 'ERROR',
                                    'message': 'Room is full or game already started'
                                }
                                conn.sendall((json.dumps(response) + '\0').encode())
                        else:
                            response = {
                                'type': 'ERROR',
                                'message': 'Room not found'
                            }
                            conn.sendall((json.dumps(response) + '\0').encode())
                elif message['type'] == 'LIST_ROOMS':
                    with lock:
                        room_list = []
                        for room_id, room in rooms.items():
                            room_info = {
                                'room_id': room_id,
                                'players': len(room.players),
                                'running': room.running
                            }
                            room_list.append(room_info)
                    response = {
                        'type': 'ROOM_LIST',
                        'rooms': room_list
                    }
                    conn.sendall((json.dumps(response) + '\0').encode())
                elif message['type'] == 'START_GAME':
                    if player.current_room and player == player.current_room.host and not player.current_room.running:
                        player.current_room.start_game()
                elif message['type'] == 'INPUT':
                    direction = message['direction']
                    if direction in ['UP', 'DOWN', 'LEFT', 'RIGHT']:
                        player.direction = direction
                    else:
                        pass
                elif message['type'] == 'EXIT_ROOM':
                    if player.current_room:
                        with lock:
                            if player in player.current_room.players:
                                player.current_room.players.remove(player)
                                if len(player.current_room.players) == 0:
                                    # Remove room if empty
                                    del rooms[player.current_room.room_id]
                        player.current_room = None
                else:
                    response = {
                        'type': 'ERROR',
                        'message': 'Invalid message type'
                    }
                    conn.sendall((json.dumps(response) + '\0').encode())
    except Exception as e:
        print(f"Exception in handle_client: {e}")
    finally:
        if player and player.current_room and player in player.current_room.players:
            with lock:
                player.current_room.players.remove(player)
                if len(player.current_room.players) == 0:
                    # Remove room if empty
                    del rooms[player.current_room.room_id]
        conn.close()

def main():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen()
        print(f"Server listening on {HOST}:{PORT}")
        while True:
            conn, addr = s.accept()
            print(f"Connected by {addr}")
            threading.Thread(target=handle_client, args=(conn, addr)).start()

if __name__ == '__main__':
    main()

Check the code and says if it's work correctly