mirror of
https://github.com/blackboxprogramming/BlackRoad-Operating-System.git
synced 2026-03-17 08:57:15 -05:00
This massive update transforms BlackRoad OS into a complete virtual operating system with modern cloud integrations and retro-styled games. New API Integrations: - DigitalOcean: Droplet management, spaces, regions, and account info - GitHub: Repo browsing, commits, PRs, issues, code search, notifications - Hugging Face: Model browser, inference API, datasets, spaces, trending - VS Code: Monaco editor integration with file tree and syntax highlighting Games (SimCity/Sims style): - Road City: City builder with zones, utilities, services, and resources - Road Life: Life simulator with characters, needs, skills, and jobs - RoadCraft: Voxel world builder with block placement Enhanced Features: - RoadView Browser: Web proxy with bookmarks, history, tabs, and search - Device Manager: SSH connections, remote command execution, deployments - Unified Dashboard: Comprehensive overview of all services and stats Backend Enhancements: - 7 new API routers with 100+ endpoints - Enhanced device management with SSH and deployment capabilities - Service health monitoring and activity tracking - Support for DigitalOcean, GitHub, and Hugging Face tokens Configuration: - Added environment variables for new API tokens - All integrations properly registered in main.py - Comprehensive error handling and validation This brings the total to 15+ integrated services creating a complete retro-styled virtual operating system with AI, cloud, games, and dev tools.
491 lines
15 KiB
Python
491 lines
15 KiB
Python
"""
|
|
Games API Router
|
|
|
|
Provides game state management for:
|
|
- Road City (SimCity-style city builder)
|
|
- Road Life (Sims-style life simulator)
|
|
- RoadCraft (Voxel world builder)
|
|
"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy import select
|
|
from typing import List, Optional, Dict, Any
|
|
from datetime import datetime
|
|
from pydantic import BaseModel
|
|
import json
|
|
import random
|
|
|
|
from ..database import get_db
|
|
from ..auth import get_current_user
|
|
from ..models import User
|
|
|
|
router = APIRouter(prefix="/api/games", tags=["games"])
|
|
|
|
|
|
# ============================================================================
|
|
# ROAD CITY - SimCity-style city builder
|
|
# ============================================================================
|
|
|
|
class CityData(BaseModel):
|
|
name: str
|
|
population: int = 0
|
|
money: int = 10000
|
|
buildings: List[Dict[str, Any]] = []
|
|
resources: Dict[str, int] = {
|
|
"power": 0,
|
|
"water": 0,
|
|
"happiness": 50
|
|
}
|
|
|
|
|
|
class BuildingPlace(BaseModel):
|
|
type: str
|
|
x: int
|
|
y: int
|
|
|
|
|
|
@router.get("/road-city/cities")
|
|
async def list_cities(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""List all cities for the current user"""
|
|
# In production, this would be stored in a GameSave table
|
|
# For now, returning a demo city
|
|
return {
|
|
"cities": [
|
|
{
|
|
"id": 1,
|
|
"name": "Road City",
|
|
"population": 1250,
|
|
"money": 45000,
|
|
"level": 5,
|
|
"created_at": "2024-01-01T00:00:00Z",
|
|
"updated_at": datetime.utcnow().isoformat()
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/road-city/{city_id}")
|
|
async def get_city(
|
|
city_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get city details and game state"""
|
|
# Demo city data
|
|
city = {
|
|
"id": city_id,
|
|
"name": "Road City",
|
|
"population": 1250,
|
|
"money": 45000,
|
|
"happiness": 75,
|
|
"buildings": [
|
|
{"id": 1, "type": "residential", "x": 2, "y": 2, "level": 2},
|
|
{"id": 2, "type": "commercial", "x": 5, "y": 2, "level": 1},
|
|
{"id": 3, "type": "industrial", "x": 8, "y": 2, "level": 1},
|
|
{"id": 4, "type": "power_plant", "x": 1, "y": 5, "level": 1},
|
|
{"id": 5, "type": "water_tower", "x": 9, "y": 5, "level": 1},
|
|
{"id": 6, "type": "park", "x": 5, "y": 5, "level": 1},
|
|
{"id": 7, "type": "police", "x": 3, "y": 8, "level": 1},
|
|
{"id": 8, "type": "hospital", "x": 7, "y": 8, "level": 1},
|
|
],
|
|
"resources": {
|
|
"power": 80,
|
|
"water": 90,
|
|
"safety": 85,
|
|
"health": 88
|
|
},
|
|
"stats": {
|
|
"residential_zones": 3,
|
|
"commercial_zones": 2,
|
|
"industrial_zones": 1,
|
|
"total_buildings": 8
|
|
}
|
|
}
|
|
return city
|
|
|
|
|
|
@router.post("/road-city/{city_id}/build")
|
|
async def place_building(
|
|
city_id: int,
|
|
building: BuildingPlace,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Place a building in the city"""
|
|
building_costs = {
|
|
"residential": 500,
|
|
"commercial": 1000,
|
|
"industrial": 1500,
|
|
"power_plant": 3000,
|
|
"water_tower": 2000,
|
|
"park": 300,
|
|
"police": 2500,
|
|
"hospital": 3500,
|
|
"school": 2000,
|
|
"fire_station": 2500,
|
|
"road": 50
|
|
}
|
|
|
|
cost = building_costs.get(building.type, 0)
|
|
|
|
# In production, update database
|
|
new_building = {
|
|
"id": random.randint(100, 999),
|
|
"type": building.type,
|
|
"x": building.x,
|
|
"y": building.y,
|
|
"level": 1,
|
|
"cost": cost
|
|
}
|
|
|
|
return {
|
|
"message": f"{building.type} built successfully",
|
|
"building": new_building,
|
|
"remaining_money": 45000 - cost
|
|
}
|
|
|
|
|
|
@router.get("/road-city/building-types")
|
|
async def get_building_types(
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get all available building types with costs"""
|
|
return {
|
|
"categories": {
|
|
"zones": [
|
|
{"type": "residential", "name": "Residential Zone", "cost": 500, "icon": "🏠"},
|
|
{"type": "commercial", "name": "Commercial Zone", "cost": 1000, "icon": "🏪"},
|
|
{"type": "industrial", "name": "Industrial Zone", "cost": 1500, "icon": "🏭"}
|
|
],
|
|
"utilities": [
|
|
{"type": "power_plant", "name": "Power Plant", "cost": 3000, "icon": "⚡"},
|
|
{"type": "water_tower", "name": "Water Tower", "cost": 2000, "icon": "💧"}
|
|
],
|
|
"services": [
|
|
{"type": "police", "name": "Police Station", "cost": 2500, "icon": "👮"},
|
|
{"type": "hospital", "name": "Hospital", "cost": 3500, "icon": "🏥"},
|
|
{"type": "school", "name": "School", "cost": 2000, "icon": "🏫"},
|
|
{"type": "fire_station", "name": "Fire Station", "cost": 2500, "icon": "🚒"}
|
|
],
|
|
"recreation": [
|
|
{"type": "park", "name": "Park", "cost": 300, "icon": "🌳"}
|
|
],
|
|
"infrastructure": [
|
|
{"type": "road", "name": "Road", "cost": 50, "icon": "🛣️"}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# ROAD LIFE - Sims-style life simulator
|
|
# ============================================================================
|
|
|
|
class CharacterCreate(BaseModel):
|
|
name: str
|
|
traits: List[str] = []
|
|
|
|
|
|
@router.get("/road-life/characters")
|
|
async def list_characters(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""List all characters for the current user"""
|
|
return {
|
|
"characters": [
|
|
{
|
|
"id": 1,
|
|
"name": "John Roadman",
|
|
"age": 25,
|
|
"occupation": "Software Developer",
|
|
"mood": "happy",
|
|
"needs": {
|
|
"hunger": 75,
|
|
"energy": 60,
|
|
"social": 80,
|
|
"fun": 70,
|
|
"hygiene": 85
|
|
},
|
|
"money": 5000,
|
|
"level": 3
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/road-life/{character_id}")
|
|
async def get_character(
|
|
character_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get character details and current state"""
|
|
character = {
|
|
"id": character_id,
|
|
"name": "John Roadman",
|
|
"age": 25,
|
|
"occupation": "Software Developer",
|
|
"mood": "happy",
|
|
"traits": ["Creative", "Bookworm", "Ambitious"],
|
|
"skills": {
|
|
"programming": 7,
|
|
"cooking": 3,
|
|
"fitness": 5,
|
|
"charisma": 4,
|
|
"creativity": 6
|
|
},
|
|
"needs": {
|
|
"hunger": 75,
|
|
"energy": 60,
|
|
"social": 80,
|
|
"fun": 70,
|
|
"hygiene": 85,
|
|
"bladder": 90
|
|
},
|
|
"relationships": [
|
|
{"name": "Sarah", "type": "Friend", "level": 65},
|
|
{"name": "Mike", "type": "Colleague", "level": 45}
|
|
],
|
|
"inventory": [
|
|
{"item": "Laptop", "type": "electronics"},
|
|
{"item": "Coffee", "type": "food", "quantity": 3}
|
|
],
|
|
"location": {
|
|
"type": "home",
|
|
"room": "living_room"
|
|
},
|
|
"money": 5000,
|
|
"job": {
|
|
"title": "Junior Developer",
|
|
"salary": 3000,
|
|
"performance": 85
|
|
}
|
|
}
|
|
return character
|
|
|
|
|
|
@router.post("/road-life/{character_id}/action")
|
|
async def perform_action(
|
|
character_id: int,
|
|
action: str,
|
|
target: Optional[str] = None,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Perform an action with the character"""
|
|
actions = {
|
|
"eat": {"hunger": 20, "time": 30},
|
|
"sleep": {"energy": 40, "time": 120},
|
|
"shower": {"hygiene": 30, "time": 15},
|
|
"work": {"money": 100, "energy": -20, "time": 240},
|
|
"socialize": {"social": 25, "fun": 15, "time": 60},
|
|
"exercise": {"fitness": 5, "energy": -15, "time": 60},
|
|
"code": {"programming": 2, "fun": 10, "time": 120},
|
|
"watch_tv": {"fun": 20, "energy": 5, "time": 60}
|
|
}
|
|
|
|
if action not in actions:
|
|
raise HTTPException(status_code=400, detail="Invalid action")
|
|
|
|
effects = actions[action]
|
|
|
|
return {
|
|
"message": f"Character performed action: {action}",
|
|
"effects": effects,
|
|
"time_elapsed": effects.get("time", 0),
|
|
"new_needs": {
|
|
"hunger": 75 + effects.get("hunger", 0),
|
|
"energy": 60 + effects.get("energy", 0),
|
|
"social": 80 + effects.get("social", 0),
|
|
"fun": 70 + effects.get("fun", 0),
|
|
"hygiene": 85 + effects.get("hygiene", 0)
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/road-life/actions")
|
|
async def get_available_actions(
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get all available actions"""
|
|
return {
|
|
"categories": {
|
|
"basic_needs": [
|
|
{"action": "eat", "name": "Eat", "icon": "🍽️", "time": 30},
|
|
{"action": "sleep", "name": "Sleep", "icon": "😴", "time": 120},
|
|
{"action": "shower", "name": "Shower", "icon": "🚿", "time": 15},
|
|
{"action": "use_toilet", "name": "Use Toilet", "icon": "🚽", "time": 5}
|
|
],
|
|
"work": [
|
|
{"action": "work", "name": "Go to Work", "icon": "💼", "time": 240},
|
|
{"action": "study", "name": "Study", "icon": "📚", "time": 120}
|
|
],
|
|
"social": [
|
|
{"action": "socialize", "name": "Chat", "icon": "💬", "time": 60},
|
|
{"action": "call_friend", "name": "Call Friend", "icon": "📞", "time": 30}
|
|
],
|
|
"recreation": [
|
|
{"action": "watch_tv", "name": "Watch TV", "icon": "📺", "time": 60},
|
|
{"action": "play_games", "name": "Play Video Games", "icon": "🎮", "time": 90},
|
|
{"action": "exercise", "name": "Exercise", "icon": "🏋️", "time": 60}
|
|
],
|
|
"skills": [
|
|
{"action": "code", "name": "Practice Coding", "icon": "💻", "time": 120},
|
|
{"action": "cook", "name": "Cook", "icon": "👨🍳", "time": 45},
|
|
{"action": "paint", "name": "Paint", "icon": "🎨", "time": 90}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# ROADCRAFT - Voxel world builder
|
|
# ============================================================================
|
|
|
|
@router.get("/roadcraft/worlds")
|
|
async def list_worlds(
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""List all RoadCraft worlds"""
|
|
return {
|
|
"worlds": [
|
|
{
|
|
"id": 1,
|
|
"name": "My First World",
|
|
"seed": "roadcraft-2024",
|
|
"mode": "creative",
|
|
"size": {"x": 256, "y": 128, "z": 256},
|
|
"created_at": "2024-01-01T00:00:00Z"
|
|
}
|
|
]
|
|
}
|
|
|
|
|
|
@router.get("/roadcraft/{world_id}")
|
|
async def get_world(
|
|
world_id: int,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get world data"""
|
|
return {
|
|
"id": world_id,
|
|
"name": "My First World",
|
|
"seed": "roadcraft-2024",
|
|
"mode": "creative",
|
|
"size": {"x": 256, "y": 128, "z": 256},
|
|
"player": {
|
|
"position": {"x": 128, "y": 64, "z": 128},
|
|
"inventory": [
|
|
{"block": "dirt", "quantity": 64},
|
|
{"block": "stone", "quantity": 64},
|
|
{"block": "wood", "quantity": 32}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
@router.post("/roadcraft/{world_id}/block")
|
|
async def place_block(
|
|
world_id: int,
|
|
x: int,
|
|
y: int,
|
|
z: int,
|
|
block_type: str,
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Place a block in the world"""
|
|
return {
|
|
"message": "Block placed",
|
|
"position": {"x": x, "y": y, "z": z},
|
|
"block": block_type
|
|
}
|
|
|
|
|
|
@router.get("/roadcraft/blocks")
|
|
async def get_block_types(
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get all available block types"""
|
|
return {
|
|
"blocks": [
|
|
{"type": "grass", "name": "Grass Block", "category": "natural"},
|
|
{"type": "dirt", "name": "Dirt", "category": "natural"},
|
|
{"type": "stone", "name": "Stone", "category": "natural"},
|
|
{"type": "wood", "name": "Wood Planks", "category": "building"},
|
|
{"type": "glass", "name": "Glass", "category": "building"},
|
|
{"type": "brick", "name": "Brick", "category": "building"},
|
|
{"type": "water", "name": "Water", "category": "liquid"},
|
|
{"type": "lava", "name": "Lava", "category": "liquid"}
|
|
]
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# GAME STATS & LEADERBOARDS
|
|
# ============================================================================
|
|
|
|
@router.get("/stats")
|
|
async def get_game_stats(
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get overall game statistics for the user"""
|
|
return {
|
|
"road_city": {
|
|
"total_cities": 1,
|
|
"largest_population": 1250,
|
|
"total_money_earned": 125000
|
|
},
|
|
"road_life": {
|
|
"total_characters": 1,
|
|
"highest_level": 3,
|
|
"total_actions": 547
|
|
},
|
|
"roadcraft": {
|
|
"total_worlds": 1,
|
|
"blocks_placed": 1892,
|
|
"hours_played": 12.5
|
|
}
|
|
}
|
|
|
|
|
|
@router.get("/leaderboard/{game}")
|
|
async def get_leaderboard(
|
|
game: str,
|
|
current_user: User = Depends(get_current_user)
|
|
):
|
|
"""Get leaderboard for a specific game"""
|
|
if game not in ["road-city", "road-life", "roadcraft"]:
|
|
raise HTTPException(status_code=400, detail="Invalid game")
|
|
|
|
# Demo leaderboard
|
|
leaderboards = {
|
|
"road-city": [
|
|
{"rank": 1, "username": "CityBuilder99", "score": 50000, "population": 10000},
|
|
{"rank": 2, "username": "UrbanPlanner", "score": 45000, "population": 8500},
|
|
{"rank": 3, "username": current_user.username, "score": 45000, "population": 1250}
|
|
],
|
|
"road-life": [
|
|
{"rank": 1, "username": "LifeMaster", "score": 10000, "level": 15},
|
|
{"rank": 2, "username": "SimGuru", "score": 8500, "level": 12},
|
|
{"rank": 3, "username": current_user.username, "score": 2500, "level": 3}
|
|
],
|
|
"roadcraft": [
|
|
{"rank": 1, "username": "BuilderPro", "score": 100000, "blocks": 50000},
|
|
{"rank": 2, "username": "VoxelMaster", "score": 85000, "blocks": 35000},
|
|
{"rank": 3, "username": current_user.username, "score": 15000, "blocks": 1892}
|
|
]
|
|
}
|
|
|
|
return {
|
|
"game": game,
|
|
"leaderboard": leaderboards[game]
|
|
}
|