transform this pygame game into js game use pixijs and nodejs
client.py
import time
import pygame
import socket
import pickle
import threading
import struct
import sys

# Initialize Pygame
pygame.init()

# Set up the screen
screen = pygame.display.set_mode((1280, 720))
pygame.display.set_caption('2dFight Game')

# Constants
tile_size = 72  # Tile size after scaling
GRAVITY = 1
JUMP_STRENGTH = 15
SPEED = 11
FRICTION = 0.0001
FONT = pygame.font.Font(None, 48)
SMALL_FONT = pygame.font.Font(None, 36)
WHITE = (255, 255, 255)
BLACK = (46, 46, 46)
BLUE = (135, 206, 235)

# Input boxes
player_name_input_box = pygame.Rect(400, 200, 400, 50)
server_ip_input_box = pygame.Rect(400, 300, 400, 50)

# Text variables
player_name = 'Player'
server_ip = 'localhost'
input_active = None

# Connecting flag
connecting = False

# Function to display text
def draw_text(text, font, color, surface, x, y):
    textobj = font.render(text, True, color)
    textrect = textobj.get_rect(center=(x, y))
    surface.blit(textobj, textrect)

# Connecting screen
def show_connecting_screen():
    screen.fill(BLUE)
    draw_text("Connecting...", FONT, BLACK, screen, 640, 360)
    pygame.display.flip()

# Function to handle user input for name and server IP in Pygame
def get_user_input_pygame():
    global player_name, server_ip, input_active, connecting
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if player_name_input_box.collidepoint(event.pos):
                    input_active = 'name'
                elif server_ip_input_box.collidepoint(event.pos):
                    input_active = 'server_ip'
                else:
                    input_active = None
            if event.type == pygame.KEYDOWN:
                if input_active == 'name':
                    if event.key == pygame.K_RETURN:
                        input_active = 'server_ip'
                    elif event.key == pygame.K_BACKSPACE:
                        player_name = player_name[:-1]
                    else:
                        player_name += event.unicode
                elif input_active == 'server_ip':
                    if event.key == pygame.K_RETURN:
                        connecting = True
                        show_connecting_screen()
                        return player_name, server_ip
                    elif event.key == pygame.K_BACKSPACE:
                        server_ip = server_ip[:-1]
                    else:
                        server_ip += event.unicode

        screen.fill(BLUE)
        draw_text("Enter Player Name:", FONT, BLACK, screen, 640, 160)
        draw_text("Enter Server IP:", FONT, BLACK, screen, 640, 260)
        
        pygame.draw.rect(screen, WHITE, player_name_input_box, 2)
        pygame.draw.rect(screen, WHITE, server_ip_input_box, 2)
        
        draw_text(player_name, SMALL_FONT, BLACK, screen, 640, 225)
        draw_text(server_ip, SMALL_FONT, BLACK, screen, 640, 325)

        pygame.display.flip()

# Function to send data
def send_data(data):
    serialized_data = pickle.dumps(data)
    length = len(serialized_data)
    client.sendall(struct.pack('>I', length) + serialized_data)

# Function to receive messages
def receive_message(conn):
    length_data = conn.recv(4)
    if len(length_data) < 4:
        raise Exception("Received incomplete length data")
    
    data_length = struct.unpack('>I', length_data)[0]
    data = b''
    while len(data) < data_length:
        packet = conn.recv(data_length - len(data))
        if not packet:
            raise Exception("Received incomplete data")
        data += packet
    return pickle.loads(data)

# Set up the character
character = pygame.Rect(375, 375, 50, 50)  # Initial position
character_velocity_x = 0
character_velocity_y = 0
is_jumping = False

# Initialize player data and map layout
players = {}
map_layout = []
# Load tile images
tile_images = {
    0: pygame.Surface((tile_size, tile_size)),  # Empty space
    1: pygame.image.load('./assets/grass.png').convert_alpha(),  # Grass tile (solid)
    2: pygame.image.load('./assets/grass.png').convert_alpha(),  # Grass tile (jump-through)
}

# Scale the grass tile image
tile_images[1] = pygame.transform.scale(tile_images[1], (tile_size, tile_size))
tile_images[2] = pygame.transform.scale(tile_images[2], (tile_size, tile_size))

# Set color for transparent tile (matching the screen background)
tile_images[0].fill(BLUE)  # Light blue to blend with background

# Function to check if the character is on the ground (on a grass tile)
def is_on_ground(rect):
    rect.bottom += 1  # Temporarily move the rect down by 1 pixel
    for y in range(max(0, rect.bottom // tile_size), min(len(map_layout), (rect.bottom // tile_size) + 1)):
        for x in range(max(0, rect.left // tile_size), min(len(map_layout[y]), (rect.right // tile_size) + 1)):
            if map_layout[y][x] == 1 or map_layout[y][x] == 2:
                tile_rect = pygame.Rect(x * tile_size, y * tile_size, tile_size, tile_size)
                if rect.colliderect(tile_rect):
                    rect.bottom -= 1  # Move the rect back up
                    return True
    rect.bottom -= 1  # Move the rect back up
    return False

# Function to check and handle general collision with tiles
# Function to check and handle general collision with tiles
def handle_tile_collisions(rect):
    global character_velocity_y, character_velocity_x, is_jumping

    # Check vertical collisions first (falling or jumping)
    for y in range(max(0, rect.top // tile_size), min(len(map_layout), (rect.bottom // tile_size) + 1)):
        for x in range(max(0, rect.left // tile_size), min(len(map_layout[y]), (rect.right // tile_size) + 1)):
            tile_id = map_layout[y][x]
            tile_rect = pygame.Rect(x * tile_size, y * tile_size, tile_size, tile_size)

            if rect.colliderect(tile_rect):
                if tile_id == 1:  # Solid tile
                    # Handle vertical collisions
                    if character_velocity_y > 0:  # Falling down
                        if rect.bottom > tile_rect.top and rect.bottom <= tile_rect.bottom:
                            rect.bottom = tile_rect.top
                            character_velocity_y = 0
                            is_jumping = False
                    elif character_velocity_y < 0:  # Jumping up
                        if rect.top < tile_rect.bottom and rect.top >= tile_rect.top:
                            rect.top = tile_rect.bottom
                            character_velocity_y = 0

                elif tile_id == 2:  # Jump-through tile
                    # Handle collision if falling onto the tile
                    if character_velocity_y > 0 and rect.bottom > tile_rect.top and rect.bottom <= tile_rect.bottom:
                        rect.bottom = tile_rect.top
                        character_velocity_y = 0
                        is_jumping = False

    # Check horizontal collisions after vertical collisions are resolved
    for y in range(max(0, rect.top // tile_size), min(len(map_layout), (rect.bottom // tile_size) + 1)):
        for x in range(max(0, rect.left // tile_size), min(len(map_layout[y]), (rect.right // tile_size) + 1)):
            tile_id = map_layout[y][x]
            tile_rect = pygame.Rect(x * tile_size, y * tile_size, tile_size, tile_size)

            if rect.colliderect(tile_rect):
                if tile_id == 1:  # Solid tile
                    # Handle horizontal collisions
                    if character_velocity_x > 0:  # Moving right
                        if rect.right > tile_rect.left and rect.right <= tile_rect.right:
                            rect.right = tile_rect.left
                            character_velocity_x = 0
                    elif character_velocity_x < 0:  # Moving left
                        if rect.left < tile_rect.right and rect.left >= tile_rect.left:
                            rect.left = tile_rect.right

# Main game loop (unchanged)

# Networking setup
player_name, server_ip = get_user_input_pygame()

try:
    HOST, PORT = server_ip.split(':')
    PORT = int(PORT) if PORT else 25566
except ValueError:
    HOST = server_ip
    PORT = 25566
except TypeError:
    HOST = 'localhost'
    PORT = 25566
except Exception as e:
    print(f"An error occurred: {e}")
    HOST = 'localhost'
    PORT = 25566

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    client.connect((HOST, PORT))
    # Send initial player data to the server
    send_data({'name': player_name, 'position': (character.x, character.y)})
except socket.error as e:
    print(f"Unable to connect to the server: {e}")
    sys.exit()

def receive_data():
    global players, map_layout, running
    while running:
        try:
            data = receive_message(client)
            players = data.get('players', {})
            map_layout = data.get('map_layout', [])
        except Exception as e:
            print(f"Disconnected from server: {e}")
            running = False
            break

# Start a thread to receive data from the server
running = True
threading.Thread(target=receive_data, daemon=True).start()

# Main game loop
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Get keys pressed
    keys = pygame.key.get_pressed()

    # Handle horizontal movement
    if keys[pygame.K_LEFT]:
        character_velocity_x = -SPEED
    elif keys[pygame.K_RIGHT]:
        character_velocity_x = SPEED
    else:
        character_velocity_x *= FRICTION  # Apply friction when no key is pressed

    # Handle jumping
    if (keys[pygame.K_SPACE] or keys[pygame.K_UP] or keys[pygame.K_w]) and not is_jumping and is_on_ground(character):
        character_velocity_y = -JUMP_STRENGTH
        is_jumping = True

    # Apply gravity
    character_velocity_y += GRAVITY
    character.x += character_velocity_x
    character.y += character_velocity_y

    # Check and handle collisions with tiles
    handle_tile_collisions(character)

    # Ensure character stays within screen bounds
    if character.left < 0:
        character.left = 0
    if character.right > 1280:
        character.right = 1280
    if character.top < 0:
        character.top = 0
    if character.bottom > 720:
        character.bottom = 720

    # Check for collisions with other players
    for addr, info in players.items():
        if addr != (client.getsockname()[0], client.getsockname()[1]):
            other_rect = pygame.Rect(info['position'][0], info['position'][1], 50, 50)
            if character.colliderect(other_rect):
                # Simple collision response
                if keys[pygame.K_LEFT]:
                    character.x += SPEED
                if keys[pygame.K_RIGHT]:
                    character.x -= SPEED
                if keys[pygame.K_UP]:
                    character.y += SPEED
                if keys[pygame.K_DOWN]:
                    character.y -= SPEED

    # Send character position to server
    try:
        send_data({'name': player_name, 'position': (character.x, character.y)})
    except socket.error as e:
        print(f"Failed to send data to server: {e}")
        running = False

    # Light blue background
    screen.fill(BLUE)

    # Draw the map layout
    for y, row in enumerate(map_layout):
        for x, tile_id in enumerate(row):
            tile_image = tile_images.get(tile_id)
            if tile_image:
                screen.blit(tile_image, (x * tile_size, y * tile_size))

    # Draw all players
    for addr, info in players.items():
        pos = info['position']
        color = info['color']
        pygame.draw.rect(screen, color, (pos[0], pos[1], 50, 50))
        font = pygame.font.Font(None, 24)
        text_surface = font.render(info['name'], True, BLACK)
        screen.blit(text_surface, (pos[0], pos[1] - 20))  # Display name above player

    # Update the display
    pygame.display.flip()

    # Cap the frame rate
    pygame.time.Clock().tick(60)

pygame.quit()


server.py
import random
import socket
import threading
import pickle
import struct
import time

# Server settings
HOST = 'localhost'
PORT = 25566

# Store player positions and colors
players = {}

# Tile size
tile_size = 18

# Define the map layouts
map_layouts = [
    [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 1, 1, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]  # Ground layer
    ],
    # Add additional predefined map layouts here
    [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 1, 1, 0, 1, 0, 0, 0, 0, 2, 0, 2, 0, 1, 1, 1, 0, 0, 1, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]  # Ground layer
    ]
]

def send_data(conn, data):
    try:
        serialized_data = pickle.dumps(data)
        length = len(serialized_data)
        conn.sendall(struct.pack('>I', length) + serialized_data)
    except Exception as e:
        print(f"Error sending data: {e}")
        if conn.fileno() != -1:
            conn.close()

def receive_message(conn):
    try:
        length_data = conn.recv(4)
        if len(length_data) < 4:
            raise Exception("Received incomplete length data")
        
        data_length = struct.unpack('>I', length_data)[0]
        data = b''
        while len(data) < data_length:
            packet = conn.recv(data_length - len(data))
            if not packet:
                raise Exception("Received incomplete data")
            data += packet
        return pickle.loads(data)
    except Exception as e:
        print(f"Error receiving message: {e}")
        if conn.fileno() != -1:
            conn.close()
        raise

def handle_client(conn, addr):
    global connections
    print(f"Connected by {addr}")
    try:
        initial_data = receive_message(conn)
        player_name = initial_data['name']
        
        # Generate a unique spawn position
        spawn_position = get_random_spawn_position()

        # Initialize new player with a unique color and position
        players[addr] = {'name': player_name, 'position': spawn_position, 'color': generate_color()}

        # Send initial positions and map layout to the new client
        send_data(conn, {'players': players, 'map_layout': current_map_layout})

        while True:
            try:
                data = receive_message(conn)
                if not data:
                    break

                players[addr]['position'] = data['position']

                # Send updated positions and map layout to all clients
                for player_conn in connections:
                    if player_conn != conn:
                        send_data(player_conn, {'players': players, 'map_layout': current_map_layout})

            except Exception as e:
                print(f"Exception in handle_client loop: {e}")
                break

    except Exception as e:
        print(f"Exception in handle_client: {e}")

    print(f"Disconnected by {addr}")
    if conn in connections:
        del players[addr]
        connections.remove(conn)
        if conn.fileno() != -1:
            conn.close()

        # Inform remaining clients about the disconnected player
        for player_conn in connections:
            try:
                send_data(player_conn, {'players': players, 'map_layout': current_map_layout})
            except Exception as e:
                print(f"Exception sending data to remaining clients: {e}")
                connections.remove(player_conn)
                if player_conn.fileno() != -1:
                    player_conn.close()

def get_random_spawn_position():
    x = random.randint(0, len(current_map_layout[0]) - 1)
    y = random.randint(0, len(current_map_layout) - 1)
    return x * tile_size, y * tile_size

def generate_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

def change_map_layout_periodically():
    global current_map_layout
    map_index = 0
    while True:
        time.sleep(60)  # Change map layout every 20 seconds
        current_map_layout = map_layouts[map_index]
        map_index = (map_index + 1) % len(map_layouts)
        
        print("Map layout changed.")

        # Send the new map layout to all connected clients
        for player_conn in connections:
            try:
                send_data(player_conn, {'players': players, 'map_layout': current_map_layout})
            except Exception as e:
                print(f"Error sending new map layout: {e}")
                connections.remove(player_conn)
                if player_conn.fileno() != -1:
                    player_conn.close()

# Set up the server socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen()
print("Server started, waiting for connections...")

connections = []
current_map_layout = map_layouts[0]  # Start with the first map layout

# Start the thread that changes the map layout periodically
threading.Thread(target=change_map_layout_periodically, daemon=True).start()

while True:
    conn, addr = server.accept()
    connections.append(conn)
    threading.Thread(target=handle_client, args=(conn, addr)).start()