Cache last-sent values on Player and skip send_msdp() when nothing changed. Idle players no longer get a packet every second.
133 lines
3.3 KiB
Python
133 lines
3.3 KiB
Python
"""GMCP and MSDP package support for rich MUD clients."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from mudlib.player import Player
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def send_char_vitals(player: Player) -> None:
|
|
"""Send Char.Vitals — pl, max_pl, stamina, max_stamina."""
|
|
if not player.gmcp_enabled:
|
|
return
|
|
player.send_gmcp(
|
|
"Char.Vitals",
|
|
{
|
|
"pl": round(player.pl, 1),
|
|
"max_pl": round(player.max_pl, 1),
|
|
"stamina": round(player.stamina, 1),
|
|
"max_stamina": round(player.max_stamina, 1),
|
|
},
|
|
)
|
|
|
|
|
|
def send_char_status(player: Player) -> None:
|
|
"""Send Char.Status — flying, resting, mode, in_combat."""
|
|
if not player.gmcp_enabled:
|
|
return
|
|
player.send_gmcp(
|
|
"Char.Status",
|
|
{
|
|
"flying": player.flying,
|
|
"resting": player.resting,
|
|
"mode": player.mode,
|
|
"in_combat": player.mode == "combat",
|
|
},
|
|
)
|
|
|
|
|
|
def send_room_info(player: Player) -> None:
|
|
"""Send Room.Info — zone, coordinates, terrain, exits."""
|
|
if not player.gmcp_enabled:
|
|
return
|
|
from mudlib.zone import Zone
|
|
|
|
zone = player.location
|
|
if not isinstance(zone, Zone):
|
|
return
|
|
|
|
terrain = zone.get_tile(player.x, player.y)
|
|
|
|
# Build exits list from passable adjacent tiles
|
|
exits = []
|
|
directions = [
|
|
("north", 0, -1),
|
|
("south", 0, 1),
|
|
("east", 1, 0),
|
|
("west", -1, 0),
|
|
("northeast", 1, -1),
|
|
("northwest", -1, -1),
|
|
("southeast", 1, 1),
|
|
("southwest", -1, 1),
|
|
]
|
|
for name, dx, dy in directions:
|
|
nx, ny = player.x + dx, player.y + dy
|
|
if zone.is_passable(nx, ny):
|
|
exits.append(name)
|
|
|
|
player.send_gmcp(
|
|
"Room.Info",
|
|
{
|
|
"zone": zone.name,
|
|
"x": player.x,
|
|
"y": player.y,
|
|
"terrain": terrain,
|
|
"exits": exits,
|
|
},
|
|
)
|
|
|
|
|
|
def send_map_data(player: Player) -> None:
|
|
"""Send Room.Map — terrain viewport around player for client rendering."""
|
|
if not player.gmcp_enabled:
|
|
return
|
|
from mudlib.zone import Zone
|
|
|
|
zone = player.location
|
|
if not isinstance(zone, Zone):
|
|
return
|
|
|
|
radius = 10
|
|
rows = []
|
|
for dy in range(-radius, radius + 1):
|
|
row = []
|
|
for dx in range(-radius, radius + 1):
|
|
tx = player.x + dx
|
|
ty = player.y + dy
|
|
wx, wy = zone.wrap(tx, ty)
|
|
row.append(zone.terrain[wy][wx])
|
|
rows.append(row)
|
|
|
|
player.send_gmcp(
|
|
"Room.Map",
|
|
{
|
|
"x": player.x,
|
|
"y": player.y,
|
|
"radius": radius,
|
|
"terrain": rows,
|
|
},
|
|
)
|
|
|
|
|
|
def send_msdp_vitals(player: Player) -> None:
|
|
"""Send MSDP variable updates for real-time gauges.
|
|
|
|
Skips sending if values haven't changed since last send.
|
|
"""
|
|
if not player.msdp_enabled:
|
|
return
|
|
data = {
|
|
"HEALTH": str(round(player.pl, 1)),
|
|
"HEALTH_MAX": str(round(player.max_pl, 1)),
|
|
"STAMINA": str(round(player.stamina, 1)),
|
|
"STAMINA_MAX": str(round(player.max_stamina, 1)),
|
|
}
|
|
if data == player._last_msdp:
|
|
return
|
|
player._last_msdp = data
|
|
player.send_msdp(data)
|