Added some utils
This commit is contained in:
parent
3aed5da246
commit
ea58b8e46f
179
client.py
179
client.py
@ -4,10 +4,16 @@ import pickle
|
||||
import pygame
|
||||
import sys
|
||||
import time
|
||||
import math
|
||||
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 65432
|
||||
MOVE_SPEED = 200 # pixels per second
|
||||
GUN_LENGTH = 30
|
||||
PLAYER_RADIUS = 20
|
||||
BULLET_RADIUS = 4
|
||||
FLASH_EFFECT_RADIUS = 200
|
||||
FLASH_DURATION = 1.0 # must match server value
|
||||
|
||||
class Event:
|
||||
def __init__(self, name, payload):
|
||||
@ -36,9 +42,11 @@ def recv_event(sock):
|
||||
data = recvall(sock, msg_len)
|
||||
return pickle.loads(data)
|
||||
|
||||
# Global state
|
||||
players = {} # Server-authoritative state: client_id -> {"username", "x", "y", "mouse_x", "mouse_y"}
|
||||
my_client_id = None # Our unique client id from the server
|
||||
# Global state.
|
||||
players = {} # client_id -> {"username", "x", "y", "mouse_x", "mouse_y", "health"}
|
||||
bullets = [] # List of bullet dicts from server.
|
||||
grenades = [] # List of grenade dicts.
|
||||
my_client_id = None # Our unique client id from the server.
|
||||
predicted_state = {
|
||||
"x": 100, "y": 100,
|
||||
"keys": set(),
|
||||
@ -47,10 +55,11 @@ predicted_state = {
|
||||
"mouse_y": 0
|
||||
}
|
||||
ping = 0 # measured ping in seconds
|
||||
server_details = {} # Updated from server_info and state_update events
|
||||
server_details = {} # Updated from server_info and state_update events.
|
||||
map_obstacles = [] # Map obstacles from server info.
|
||||
|
||||
def listen_to_server(sock):
|
||||
global my_client_id, players, predicted_state, ping, server_details
|
||||
global my_client_id, players, predicted_state, ping, server_details, bullets, grenades, map_obstacles
|
||||
while True:
|
||||
try:
|
||||
event = recv_event(sock)
|
||||
@ -62,6 +71,7 @@ def listen_to_server(sock):
|
||||
print(f"[INFO] My client ID: {my_client_id}")
|
||||
elif event.name == "server_info":
|
||||
server_details = event.payload
|
||||
map_obstacles = server_details.get("map", [])
|
||||
print(f"[INFO] Received server info: {server_details}")
|
||||
elif event.name == "client_connect":
|
||||
print(f"[INFO] Client connected: {event.payload.get('client_id')}")
|
||||
@ -72,6 +82,8 @@ def listen_to_server(sock):
|
||||
del players[cid]
|
||||
elif event.name == "state_update":
|
||||
players = event.payload.get("players", {})
|
||||
bullets = event.payload.get("bullets", [])
|
||||
grenades = event.payload.get("grenades", [])
|
||||
# Update dynamic server details.
|
||||
if "total_users" in event.payload:
|
||||
server_details["total_users"] = event.payload["total_users"]
|
||||
@ -127,17 +139,14 @@ def main():
|
||||
|
||||
pygame.init()
|
||||
screen = pygame.display.set_mode((800, 600))
|
||||
pygame.display.set_caption("Pygame Client - Multiplayer (Real vs Fake)")
|
||||
pygame.display.set_caption("Top Down Shooter Multiplayer")
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
ghost_color = (0, 255, 0) # Ghost: server state (outlined)
|
||||
client_color = (0, 0, 255) # Client: predicted state (filled)
|
||||
other_color = (255, 0, 0) # Remote players
|
||||
remote_mouse_color = (255, 255, 0) # Color for remote mouse pointer (yellow)
|
||||
local_keys = set()
|
||||
|
||||
while True:
|
||||
dt = clock.tick(60) / 1000.0
|
||||
|
||||
for ev in pygame.event.get():
|
||||
if ev.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
@ -152,6 +161,13 @@ def main():
|
||||
key_name = "up"
|
||||
elif ev.key == pygame.K_DOWN:
|
||||
key_name = "down"
|
||||
# Grenade throw keys.
|
||||
elif ev.key == pygame.K_q:
|
||||
send_message(sock, Event("throw_smoke", {}))
|
||||
print("[ACTION] Smoke grenade thrown")
|
||||
elif ev.key == pygame.K_e:
|
||||
send_message(sock, Event("throw_flash", {}))
|
||||
print("[ACTION] Flash grenade thrown")
|
||||
if key_name and key_name not in local_keys:
|
||||
local_keys.add(key_name)
|
||||
predicted_state["keys"].add(key_name)
|
||||
@ -176,9 +192,18 @@ def main():
|
||||
predicted_state["mouse_x"] = mouse_x
|
||||
predicted_state["mouse_y"] = mouse_y
|
||||
send_message(sock, Event("mouse_move", {"x": mouse_x, "y": mouse_y}))
|
||||
|
||||
elif ev.type == pygame.MOUSEBUTTONDOWN:
|
||||
if ev.button == 1: # Left click: shoot
|
||||
mouse_x, mouse_y = ev.pos
|
||||
dx = mouse_x - predicted_state["x"]
|
||||
dy = mouse_y - predicted_state["y"]
|
||||
angle = math.atan2(dy, dx)
|
||||
send_message(sock, Event("shoot", {"angle": angle}))
|
||||
print("[ACTION] Shoot event sent")
|
||||
|
||||
# Update predicted state locally.
|
||||
dx, dy = 0, 0
|
||||
dx = 0
|
||||
dy = 0
|
||||
if "left" in predicted_state["keys"]:
|
||||
dx -= MOVE_SPEED * dt
|
||||
if "right" in predicted_state["keys"]:
|
||||
@ -190,50 +215,106 @@ def main():
|
||||
predicted_state["x"] += dx
|
||||
predicted_state["y"] += dy
|
||||
|
||||
# For our own player, the ghost (server) position is used as the "real" state.
|
||||
if my_client_id and my_client_id in players:
|
||||
ghost_x = players[my_client_id].get("x", 100)
|
||||
ghost_y = players[my_client_id].get("y", 100)
|
||||
else:
|
||||
ghost_x, ghost_y = predicted_state["x"], predicted_state["y"]
|
||||
# Camera follows local player.
|
||||
local_x = predicted_state["x"]
|
||||
local_y = predicted_state["y"]
|
||||
screen_width, screen_height = 800, 600
|
||||
camera_offset = (local_x - screen_width // 2, local_y - screen_height // 2)
|
||||
|
||||
screen.fill((0, 0, 0))
|
||||
# Draw remote players (only real state).
|
||||
# Drawing.
|
||||
screen.fill((30, 30, 30)) # Background.
|
||||
|
||||
# Draw map obstacles.
|
||||
for obs in map_obstacles:
|
||||
rect = pygame.Rect(obs["x"] - camera_offset[0], obs["y"] - camera_offset[1], obs["width"], obs["height"])
|
||||
pygame.draw.rect(screen, (100, 100, 100), rect)
|
||||
|
||||
# Draw bullets.
|
||||
for bullet in bullets:
|
||||
bx = int(bullet.get("x", 0) - camera_offset[0])
|
||||
by = int(bullet.get("y", 0) - camera_offset[1])
|
||||
pygame.draw.circle(screen, (255, 255, 255), (bx, by), BULLET_RADIUS)
|
||||
|
||||
# Draw grenades.
|
||||
for gren in grenades:
|
||||
gx = int(gren.get("x", 0) - camera_offset[0])
|
||||
gy = int(gren.get("y", 0) - camera_offset[1])
|
||||
gtype = gren.get("type", "")
|
||||
if gren.get("fuse", 0) > 0:
|
||||
pygame.draw.circle(screen, (200, 200, 200), (gx, gy), 6)
|
||||
else:
|
||||
if gtype == "smoke":
|
||||
# Draw semi-transparent smoke flood fill.
|
||||
smoke_radius = 80
|
||||
smoke_surface = pygame.Surface((smoke_radius*2, smoke_radius*2), pygame.SRCALPHA)
|
||||
pygame.draw.circle(smoke_surface, (100, 100, 100, 150), (smoke_radius, smoke_radius), smoke_radius)
|
||||
screen.blit(smoke_surface, (gx - smoke_radius, gy - smoke_radius))
|
||||
elif gtype == "flash":
|
||||
pygame.draw.circle(screen, (255, 255, 0), (gx, gy), 30)
|
||||
|
||||
# Draw players.
|
||||
for cid, data in players.items():
|
||||
if cid == my_client_id:
|
||||
continue
|
||||
x = int(data.get("x", 100))
|
||||
y = int(data.get("y", 100))
|
||||
# Get player position and health.
|
||||
px = data.get("x", 100)
|
||||
py = data.get("y", 100)
|
||||
health = data.get("health", 100)
|
||||
uname = data.get("username", "Guest")
|
||||
pygame.draw.circle(screen, other_color, (x, y), 20)
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
text_surface = font.render(uname, True, (255, 255, 255))
|
||||
text_rect = text_surface.get_rect(center=(x, y - 30))
|
||||
screen.blit(text_surface, text_rect)
|
||||
# Draw remote mouse pointer if available.
|
||||
if "mouse_x" in data and "mouse_y" in data:
|
||||
mx = int(data.get("mouse_x", 0))
|
||||
my = int(data.get("mouse_y", 0))
|
||||
pygame.draw.circle(screen, remote_mouse_color, (mx, my), 5)
|
||||
# Draw our ghost (server-authoritative) as an outlined circle.
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
ghost_text = font.render("Ghost", True, (255, 255, 255))
|
||||
ghost_rect = ghost_text.get_rect(center=(int(ghost_x), int(ghost_y) - 30))
|
||||
pygame.draw.circle(screen, ghost_color, (int(ghost_x), int(ghost_y)), 20, 2)
|
||||
screen.blit(ghost_text, ghost_rect)
|
||||
# Draw our client-predicted player as a filled circle.
|
||||
pred_x = int(predicted_state.get("x", 100))
|
||||
pred_y = int(predicted_state.get("y", 100))
|
||||
font = pygame.font.SysFont(None, 24)
|
||||
client_text = font.render(predicted_state.get("username", "Guest"), True, (255, 255, 255))
|
||||
client_rect = client_text.get_rect(center=(pred_x, pred_y - 30))
|
||||
pygame.draw.circle(screen, client_color, (pred_x, pred_y), 20)
|
||||
screen.blit(client_text, client_rect)
|
||||
# Determine aiming direction.
|
||||
if cid == my_client_id:
|
||||
aim_dx = predicted_state["mouse_x"] - local_x
|
||||
aim_dy = predicted_state["mouse_y"] - local_y
|
||||
else:
|
||||
aim_dx = data.get("mouse_x", 0) - px
|
||||
aim_dy = data.get("mouse_y", 0) - py
|
||||
aim_angle = math.atan2(aim_dy, aim_dx)
|
||||
# Adjust drawing positions by camera offset.
|
||||
draw_x = int(px - camera_offset[0])
|
||||
draw_y = int(py - camera_offset[1])
|
||||
# Draw player body.
|
||||
if cid == my_client_id:
|
||||
color = (0, 0, 255)
|
||||
else:
|
||||
color = (255, 0, 0)
|
||||
pygame.draw.circle(screen, color, (draw_x, draw_y), PLAYER_RADIUS)
|
||||
# Draw face (an eye) indicating aim.
|
||||
eye_offset = 10
|
||||
eye_x = draw_x + int(eye_offset * math.cos(aim_angle))
|
||||
eye_y = draw_y + int(eye_offset * math.sin(aim_angle))
|
||||
pygame.draw.circle(screen, (255, 255, 255), (eye_x, eye_y), 4)
|
||||
# Draw health bar above player.
|
||||
bar_width = 40
|
||||
bar_height = 5
|
||||
health_ratio = health / 100
|
||||
health_bar_back = pygame.Rect(draw_x - bar_width//2, draw_y - PLAYER_RADIUS - 15, bar_width, bar_height)
|
||||
health_bar_front = pygame.Rect(draw_x - bar_width//2, draw_y - PLAYER_RADIUS - 15, int(bar_width * health_ratio), bar_height)
|
||||
pygame.draw.rect(screen, (100, 100, 100), health_bar_back)
|
||||
pygame.draw.rect(screen, (0, 255, 0), health_bar_front)
|
||||
# Draw username.
|
||||
font = pygame.font.SysFont(None, 20)
|
||||
name_surface = font.render(uname, True, (255, 255, 255))
|
||||
screen.blit(name_surface, (draw_x - name_surface.get_width()//2, draw_y - PLAYER_RADIUS - 30))
|
||||
|
||||
# Flash effect: if a flash grenade is active and the local player is nearby, overlay white.
|
||||
flash_alpha = 0
|
||||
for gren in grenades:
|
||||
if gren.get("type") == "flash" and gren.get("fuse", 0) <= 0:
|
||||
gx = gren.get("x", 0)
|
||||
gy = gren.get("y", 0)
|
||||
dist = math.hypot(local_x - gx, local_y - gy)
|
||||
if dist < FLASH_EFFECT_RADIUS:
|
||||
intensity = gren.get("duration", 0) / FLASH_DURATION
|
||||
flash_alpha = max(flash_alpha, int(255 * intensity))
|
||||
if flash_alpha > 0:
|
||||
flash_overlay = pygame.Surface((screen_width, screen_height))
|
||||
flash_overlay.fill((255, 255, 255))
|
||||
flash_overlay.set_alpha(flash_alpha)
|
||||
screen.blit(flash_overlay, (0, 0))
|
||||
|
||||
# Display ping.
|
||||
ping_font = pygame.font.SysFont(None, 24)
|
||||
ping_surface = ping_font.render(f"Ping: {int(ping*1000)} ms", True, (255, 255, 0))
|
||||
screen.blit(ping_surface, (10, 10))
|
||||
# Display HUD info: total users, client ID, and tickrate.
|
||||
# Display HUD info.
|
||||
hud_font = pygame.font.SysFont(None, 24)
|
||||
total_users = server_details.get("total_users", len(players))
|
||||
max_users = server_details.get("max_users", "?")
|
||||
|
185
server.py
185
server.py
@ -3,13 +3,44 @@ import threading
|
||||
import pickle
|
||||
import time
|
||||
import uuid
|
||||
import math
|
||||
|
||||
HOST = '127.0.0.1'
|
||||
PORT = 65432
|
||||
MOVE_SPEED = 200 # pixels per second
|
||||
TICKRATE = 64 # ticks per second
|
||||
MOVE_SPEED = 200 # pixels per second for players
|
||||
TICKRATE = 128 # ticks per second (sub-tick simulation)
|
||||
MAX_USERS = 100
|
||||
|
||||
BULLET_SPEED = 400 # pixels per second
|
||||
BULLET_LIFETIME = 2.0 # seconds
|
||||
BULLET_DAMAGE = 25 # damage per bullet
|
||||
PLAYER_RADIUS = 20
|
||||
BULLET_RADIUS = 4
|
||||
|
||||
SMOKE_GRENADE_FUSE = 0.5 # seconds before smoke activates
|
||||
SMOKE_DURATION = 5.0 # active smoke duration (seconds)
|
||||
FLASH_GRENADE_FUSE = 0.3 # seconds before flash activates
|
||||
FLASH_DURATION = 1.0 # active flash duration (seconds)
|
||||
|
||||
# Define map obstacles.
|
||||
map_obstacles = [
|
||||
{"x": 200, "y": 150, "width": 100, "height": 300},
|
||||
{"x": 500, "y": 100, "width": 50, "height": 400},
|
||||
{"x": 100, "y": 500, "width": 600, "height": 50},
|
||||
]
|
||||
|
||||
def circle_rect_collision(cx, cy, radius, rect):
|
||||
closest_x = max(rect["x"], min(cx, rect["x"] + rect["width"]))
|
||||
closest_y = max(rect["y"], min(cy, rect["y"] + rect["height"]))
|
||||
dx = cx - closest_x
|
||||
dy = cy - closest_y
|
||||
return (dx*dx + dy*dy) < (radius*radius)
|
||||
|
||||
def circle_circle_collision(x1, y1, r1, x2, y2, r2):
|
||||
dx = x1 - x2
|
||||
dy = y1 - y2
|
||||
return (dx*dx + dy*dy) < ((r1 + r2) ** 2)
|
||||
|
||||
class Event:
|
||||
def __init__(self, name, payload):
|
||||
self.name = name
|
||||
@ -37,17 +68,19 @@ def recv_event(sock):
|
||||
data = recvall(sock, msg_len)
|
||||
return pickle.loads(data)
|
||||
|
||||
# Global dictionaries
|
||||
# Global dictionaries.
|
||||
event_subscriptions = {} # event name -> list of client sockets
|
||||
client_ids = {} # conn -> client_id
|
||||
client_states = {} # conn -> {"client_id", "username", "x", "y", "keys", "mouse_x", "mouse_y"}
|
||||
client_states = {} # conn -> {"client_id", "username", "x", "y", "keys", "mouse_x", "mouse_y", "health"}
|
||||
lock = threading.Lock()
|
||||
|
||||
bullets = [] # List of dicts: {"x", "y", "dx", "dy", "life", "shooter"}
|
||||
grenades = [] # List of dicts: {"type": "smoke" or "flash", "x", "y", "fuse", "duration"}
|
||||
|
||||
def handle_client(conn, addr):
|
||||
client_id = str(uuid.uuid4())
|
||||
with lock:
|
||||
client_ids[conn] = client_id
|
||||
# Initialize state for new client.
|
||||
client_states[conn] = {
|
||||
"client_id": client_id,
|
||||
"username": "Guest",
|
||||
@ -55,27 +88,26 @@ def handle_client(conn, addr):
|
||||
"y": 100,
|
||||
"keys": set(),
|
||||
"mouse_x": 0,
|
||||
"mouse_y": 0
|
||||
"mouse_y": 0,
|
||||
"health": 100
|
||||
}
|
||||
print(f"[NEW CONNECTION] {addr} connected as {client_id}")
|
||||
|
||||
try:
|
||||
# Send self_id so the client knows its unique id.
|
||||
send_event(conn, Event("self_id", {"client_id": client_id}))
|
||||
# Send server info.
|
||||
server_info = {
|
||||
"tickrate": TICKRATE,
|
||||
"total_users": len(client_states),
|
||||
"max_users": MAX_USERS,
|
||||
"server_ip": HOST,
|
||||
"server_port": PORT,
|
||||
"server_name": "My Multiplayer Server"
|
||||
"server_name": "My Multiplayer Shooter Server",
|
||||
"map": map_obstacles
|
||||
}
|
||||
send_event(conn, Event("server_info", server_info))
|
||||
except Exception as e:
|
||||
print(f"[ERROR] sending self_id/server_info: {e}")
|
||||
|
||||
# Broadcast that a new client connected.
|
||||
broadcast_event("client_connect", {"client_id": client_id})
|
||||
|
||||
try:
|
||||
@ -113,14 +145,56 @@ def handle_client(conn, addr):
|
||||
if conn in client_states:
|
||||
client_states[conn]['mouse_x'] = x
|
||||
client_states[conn]['mouse_y'] = y
|
||||
# Optionally log mouse moves.
|
||||
# print(f"[MOUSE_MOVE] {client_id} moved mouse to ({x}, {y})")
|
||||
elif event.name == "shoot":
|
||||
angle = event.payload.get("angle")
|
||||
with lock:
|
||||
if conn in client_states:
|
||||
state = client_states[conn]
|
||||
bx = state["x"]
|
||||
by = state["y"]
|
||||
dx = math.cos(angle) * BULLET_SPEED
|
||||
dy = math.sin(angle) * BULLET_SPEED
|
||||
bullet = {
|
||||
"x": bx,
|
||||
"y": by,
|
||||
"dx": dx,
|
||||
"dy": dy,
|
||||
"life": BULLET_LIFETIME,
|
||||
"shooter": client_ids[conn]
|
||||
}
|
||||
bullets.append(bullet)
|
||||
print(f"[SHOOT] {client_ids[conn]} fired a bullet")
|
||||
elif event.name == "throw_smoke":
|
||||
with lock:
|
||||
if conn in client_states:
|
||||
state = client_states[conn]
|
||||
grenade = {
|
||||
"type": "smoke",
|
||||
"x": state["x"],
|
||||
"y": state["y"],
|
||||
"fuse": SMOKE_GRENADE_FUSE,
|
||||
"duration": SMOKE_DURATION
|
||||
}
|
||||
grenades.append(grenade)
|
||||
print(f"[GRENADE] {client_ids[conn]} threw a smoke grenade")
|
||||
elif event.name == "throw_flash":
|
||||
with lock:
|
||||
if conn in client_states:
|
||||
state = client_states[conn]
|
||||
grenade = {
|
||||
"type": "flash",
|
||||
"x": state["x"],
|
||||
"y": state["y"],
|
||||
"fuse": FLASH_GRENADE_FUSE,
|
||||
"duration": FLASH_DURATION
|
||||
}
|
||||
grenades.append(grenade)
|
||||
print(f"[GRENADE] {client_ids[conn]} threw a flash grenade")
|
||||
elif event.name == "ping":
|
||||
timestamp = event.payload.get('timestamp')
|
||||
send_event(conn, Event("pong", {"timestamp": timestamp}))
|
||||
# You can handle additional events here.
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Exception with {client_id}: {e}")
|
||||
print(f"[ERROR] Exception with {client_ids.get(conn, 'unknown')}: {e}")
|
||||
finally:
|
||||
with lock:
|
||||
for subs in event_subscriptions.values():
|
||||
@ -144,24 +218,90 @@ def broadcast_event(event_name, payload):
|
||||
receivers.remove(client)
|
||||
|
||||
def game_loop():
|
||||
dt = 1 / TICKRATE # Tick interval
|
||||
global bullets, grenades
|
||||
dt = 1 / TICKRATE
|
||||
while True:
|
||||
time.sleep(dt)
|
||||
with lock:
|
||||
# Update each client's position based on pressed keys.
|
||||
# Update player positions with axis separation and collision.
|
||||
for state in client_states.values():
|
||||
dx, dy = 0, 0
|
||||
# Horizontal movement.
|
||||
dx = 0
|
||||
if 'left' in state['keys']:
|
||||
dx -= MOVE_SPEED * dt
|
||||
if 'right' in state['keys']:
|
||||
dx += MOVE_SPEED * dt
|
||||
new_x = state['x'] + dx
|
||||
colliding = False
|
||||
for obs in map_obstacles:
|
||||
if circle_rect_collision(new_x, state['y'], PLAYER_RADIUS, obs):
|
||||
colliding = True
|
||||
break
|
||||
if not colliding:
|
||||
state['x'] = new_x
|
||||
# Vertical movement.
|
||||
dy = 0
|
||||
if 'up' in state['keys']:
|
||||
dy -= MOVE_SPEED * dt
|
||||
if 'down' in state['keys']:
|
||||
dy += MOVE_SPEED * dt
|
||||
state['x'] += dx
|
||||
state['y'] += dy
|
||||
# Build payload for state_update.
|
||||
new_y = state['y'] + dy
|
||||
colliding = False
|
||||
for obs in map_obstacles:
|
||||
if circle_rect_collision(state['x'], new_y, PLAYER_RADIUS, obs):
|
||||
colliding = True
|
||||
break
|
||||
if not colliding:
|
||||
state['y'] = new_y
|
||||
|
||||
# Update bullets.
|
||||
new_bullets = []
|
||||
for bullet in bullets:
|
||||
bullet["x"] += bullet["dx"] * dt
|
||||
bullet["y"] += bullet["dy"] * dt
|
||||
bullet["life"] -= dt
|
||||
if bullet["life"] <= 0:
|
||||
continue
|
||||
# Check collision with obstacles.
|
||||
hit_obstacle = False
|
||||
for obs in map_obstacles:
|
||||
if circle_rect_collision(bullet["x"], bullet["y"], BULLET_RADIUS, obs):
|
||||
hit_obstacle = True
|
||||
break
|
||||
if hit_obstacle:
|
||||
continue
|
||||
# Check collision with players.
|
||||
hit_player = False
|
||||
for state in client_states.values():
|
||||
if state["client_id"] == bullet["shooter"]:
|
||||
continue
|
||||
if circle_circle_collision(bullet["x"], bullet["y"], BULLET_RADIUS, state["x"], state["y"], PLAYER_RADIUS):
|
||||
state["health"] -= BULLET_DAMAGE
|
||||
if state["health"] <= 0:
|
||||
# Respawn the player.
|
||||
state["health"] = 100
|
||||
state["x"] = 100
|
||||
state["y"] = 100
|
||||
hit_player = True
|
||||
break
|
||||
if hit_player:
|
||||
continue
|
||||
new_bullets.append(bullet)
|
||||
bullets = new_bullets
|
||||
|
||||
# Update grenades.
|
||||
new_grenades = []
|
||||
for gren in grenades:
|
||||
if gren["fuse"] > 0:
|
||||
gren["fuse"] -= dt
|
||||
new_grenades.append(gren)
|
||||
else:
|
||||
gren["duration"] -= dt
|
||||
if gren["duration"] > 0:
|
||||
new_grenades.append(gren)
|
||||
grenades = new_grenades
|
||||
|
||||
# Build payload.
|
||||
players_payload = {}
|
||||
for state in client_states.values():
|
||||
players_payload[state['client_id']] = {
|
||||
@ -169,11 +309,14 @@ def game_loop():
|
||||
"x": state['x'],
|
||||
"y": state['y'],
|
||||
"mouse_x": state.get('mouse_x', 0),
|
||||
"mouse_y": state.get('mouse_y', 0)
|
||||
"mouse_y": state.get('mouse_y', 0),
|
||||
"health": state.get('health', 100)
|
||||
}
|
||||
total_users = len(client_states)
|
||||
payload = {
|
||||
"players": players_payload,
|
||||
"bullets": bullets,
|
||||
"grenades": grenades,
|
||||
"total_users": total_users,
|
||||
"max_users": MAX_USERS
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user