LetsMakeGames2 / app.py
awacke1's picture
Update app.py
f9e5e02 verified
raw
history blame
10.7 kB
import streamlit as st
import os
import random
import time
from PIL import Image
import json
from datetime import datetime
from pathlib import Path
import base64
from io import BytesIO
import numpy as np
from py_mp import CommandServer, CommandClient, ClientCommand, ServerSideServerCommand
from py_mp.commands import NetworkFlag
import threading
import queue
# ๐ŸŽฎ Game Constants
GRID_WIDTH = 16
GRID_HEIGHT = 9
REFRESH_RATE = 5
INTERACTION_RADIUS = 2
POINTS_PER_INTERACTION = 1
# ๐ŸŒ Network Settings
SERVER_HOST = "localhost"
SERVER_PORT = 5000
message_queue = queue.Queue()
# โฐ Time Sync
def get_server_time():
return time.time()
# ๐ŸŽจ Resource Management
@st.cache_resource(show_spinner="Loading game resources...")
def get_game_images():
return {
'tile': Image.new('RGB', (50, 50), color='green'),
'player': Image.new('RGB', (50, 50), color='blue'),
'other_player': Image.new('RGB', (50, 50), color='red')
}
# ๐Ÿ“ Name Generation
@st.cache_data
def get_name_components():
return {
'prefixes': ['Aer', 'Bal', 'Cal', 'Dor', 'El', 'Fae', 'Gor', 'Hel', 'Il', 'Jor',
'Kal', 'Lyr', 'Mel', 'Nym', 'Oro', 'Pyr', 'Qar', 'Ryn', 'Syl', 'Tyr'],
'suffixes': ['ian', 'or', 'ion', 'us', 'ix', 'ar', 'en', 'yr', 'el', 'an',
'is', 'ax', 'on', 'ir', 'ex', 'az', 'er', 'eth', 'ys', 'ix']
}
# ๐Ÿ”„ Player Synchronization
@st.cache_data(ttl=2)
def load_all_players(timestamp):
players = {}
if os.path.exists('players'):
for filename in os.listdir('players'):
if filename.endswith('.json'):
with open(f"players/{filename}", 'r') as f:
try:
player_data = json.load(f)
if time.time() - player_data['last_update'] < 60:
players[player_data['name']] = player_data
except json.JSONDecodeError:
continue
return {'players': players, 'last_update': timestamp}
# ๐Ÿ”„ Auto-Refresh Implementation
def create_autorefresh():
refresh_html = """
<script>
let lastUpdate = Date.now();
let serverTimeOffset = 0;
async function syncTime() {
const response = await fetch('/server_time');
const serverTime = await response.json();
serverTimeOffset = serverTime - Date.now();
}
function getServerTime() {
return Date.now() + serverTimeOffset;
}
function checkForUpdates() {
const now = getServerTime();
if (now - lastUpdate >= 5000) {
lastUpdate = now;
window.parent.document.querySelector('.stApp').classList.add('refreshing');
setTimeout(() => window.location.reload(), 100);
}
}
function startAutoRefresh() {
syncTime();
setInterval(syncTime, 30000);
setInterval(checkForUpdates, 1000);
document.addEventListener('visibilitychange', function() {
if (!document.hidden) {
syncTime();
lastUpdate = getServerTime() - 4900;
}
});
}
if (document.readyState === 'complete') {
startAutoRefresh();
} else {
window.addEventListener('load', startAutoRefresh);
}
</script>
"""
st.components.v1.html(refresh_html, height=0)
# ๐Ÿ’พ State Management
def save_game_state():
if st.session_state.player_name and st.session_state.character_stats:
params = {
'player_name': st.session_state.player_name,
'x': str(st.session_state.position['x']),
'y': str(st.session_state.position['y']),
**{k: str(v) for k, v in st.session_state.character_stats.items()},
'last_sync': str(get_server_time())
}
st.query_params.from_dict(params)
# ๐ŸŽฒ Game State Initialization
if 'initialized' not in st.session_state:
st.session_state.initialized = False
st.session_state.game_state = {
'players': {},
'chat_messages': [],
'last_sync': get_server_time()
}
st.session_state.player_name = None
st.session_state.character_stats = None
st.session_state.position = {
'x': random.randint(0, GRID_WIDTH - 1),
'y': random.randint(0, GRID_HEIGHT - 1)
}
st.session_state.last_move = get_server_time()
st.session_state.nearby_players = []
# ๐ŸŽฎ Player State Management
def save_player_state():
if st.session_state.player_name:
player_data = {
'name': st.session_state.player_name,
'position': st.session_state.position,
'stats': st.session_state.character_stats,
'last_update': get_server_time()
}
os.makedirs('players', exist_ok=True)
with open(f"players/{st.session_state.player_name}.json", 'w') as f:
json.dump(player_data, f)
save_game_state()
# ๐Ÿ“ Distance Calculation
def calculate_distance(pos1, pos2):
dx = min(abs(pos1['x'] - pos2['x']), GRID_WIDTH - abs(pos1['x'] - pos2['x']))
dy = min(abs(pos1['y'] - pos2['y']), GRID_HEIGHT - abs(pos1['y'] - pos2['y']))
return dx + dy
# ๐Ÿƒโ€โ™‚๏ธ Movement System
def update_position(direction):
if direction == "up":
st.session_state.position['y'] = (st.session_state.position['y'] - 1) % GRID_HEIGHT
elif direction == "down":
st.session_state.position['y'] = (st.session_state.position['y'] + 1) % GRID_HEIGHT
elif direction == "left":
st.session_state.position['x'] = (st.session_state.position['x'] - 1) % GRID_WIDTH
elif direction == "right":
st.session_state.position['x'] = (st.session_state.position['x'] + 1) % GRID_WIDTH
st.session_state.last_move = get_server_time()
save_player_state()
# ๐Ÿ‘ฅ Nearby Players Update
def update_nearby_players():
current_time = get_server_time()
all_players = load_all_players(current_time)['players']
nearby = []
score_gain = 0
for player_name, player_data in all_players.items():
if player_name != st.session_state.player_name:
distance = calculate_distance(st.session_state.position, player_data['position'])
if distance <= INTERACTION_RADIUS:
nearby.append({
'name': player_name,
'distance': distance,
'score': player_data['stats']['score'],
'last_seen': current_time - player_data['last_update']
})
score_gain += POINTS_PER_INTERACTION
if score_gain > 0:
st.session_state.character_stats['score'] += score_gain
save_player_state()
st.session_state.nearby_players = nearby
# ๐ŸŽจ Game Board Creation
def create_game_board():
current_time = get_server_time()
all_players = load_all_players(current_time)['players']
images = get_game_images()
for y in range(GRID_HEIGHT):
cols = st.columns(GRID_WIDTH)
for x in range(GRID_WIDTH):
player_here = None
for player_name, player_data in all_players.items():
if (player_data['position']['x'] == x and
player_data['position']['y'] == y):
player_here = player_name
if x == st.session_state.position['x'] and y == st.session_state.position['y']:
cols[x].image(images['player'], use_column_width=True)
cols[x].markdown(f"**You** ({st.session_state.character_stats['score']} pts)")
elif player_here:
cols[x].image(images['other_player'], use_column_width=True)
cols[x].markdown(f"**{player_here}** ({all_players[player_here]['stats']['score']} pts)")
else:
cols[x].image(images['tile'], use_column_width=True)
# ๐ŸŽฎ Main Game Loop
def main():
create_autorefresh()
update_nearby_players()
st.sidebar.title("Player Info")
if st.session_state.player_name is None:
default_name = generate_fantasy_name()
player_name = st.sidebar.text_input("Enter your name or use generated name:", value=default_name)
if st.sidebar.button("Start Playing"):
st.session_state.player_name = player_name
if st.session_state.character_stats is None:
st.session_state.character_stats = {
'STR': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'DEX': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'CON': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'INT': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'WIS': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'CHA': sum(sorted([random.randint(1, 6) for _ in range(4)])[1:]),
'HP': random.randint(1, 20) * 2 + random.randint(1, 20),
'MAX_HP': 40,
'score': 0,
'created_at': get_server_time()
}
save_player_state()
st.rerun()
else:
st.sidebar.markdown("### Nearby Players")
for player in st.session_state.nearby_players:
st.sidebar.markdown(
f"**{player['name']}** - {player['distance']} tiles away - {player['score']} pts\n"
f"Last seen: {player['last_seen']:.1f}s ago"
)
st.sidebar.markdown("### Movement Controls")
move_cols = st.sidebar.columns(3)
if move_cols[1].button("โฌ†๏ธ", key="up"):
update_position("up")
st.rerun()
cols = st.sidebar.columns(3)
if cols[0].button("โฌ…๏ธ", key="left"):
update_position("left")
st.rerun()
if cols[1].button("โฌ‡๏ธ", key="down"):
update_position("down")
st.rerun()
if cols[2].button("โžก๏ธ", key="right"):
update_position("right")
st.rerun()
if st.sidebar.button("Clear Game State"):
st.query_params.clear()
st.cache_resource.clear()
st.cache_data.clear()
st.rerun()
st.title("Multiplayer Tile Game")
create_game_board()
if __name__ == "__main__":
main()