158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
import os
|
|
import json
|
|
import threading
|
|
from datetime import datetime, timezone
|
|
|
|
from .state import GotchiState, CONFIG, DEFAULT_STATE
|
|
|
|
STATE_FILE = os.path.join(os.path.dirname(__file__), "state.json")
|
|
_lock = threading.Lock()
|
|
_state = None
|
|
_state_mtime = None
|
|
|
|
def _disk_mtime():
|
|
try:
|
|
return os.path.getmtime(STATE_FILE)
|
|
except OSError:
|
|
return None
|
|
|
|
def _get_state():
|
|
global _state, _state_mtime
|
|
mtime = _disk_mtime()
|
|
if _state is None:
|
|
_state = GotchiState.load(STATE_FILE)
|
|
_state_mtime = mtime
|
|
elif mtime is not None and _state_mtime is not None and mtime != _state_mtime:
|
|
# External edit of state.json detected — reload from disk
|
|
_state = GotchiState.load(STATE_FILE)
|
|
_state_mtime = mtime
|
|
return _state
|
|
|
|
def _reset_state():
|
|
global _state, _state_mtime
|
|
_state = GotchiState()
|
|
_state.save(STATE_FILE)
|
|
_state_mtime = _disk_mtime()
|
|
|
|
def _save_state():
|
|
global _state_mtime
|
|
s = _get_state()
|
|
s.save(STATE_FILE)
|
|
_state_mtime = _disk_mtime()
|
|
|
|
def _now():
|
|
return datetime.now(timezone.utc)
|
|
|
|
def _apply_decay_on_read():
|
|
s = _get_state()
|
|
if s.last_decay_at and s.stage not in ("egg", "ghost"):
|
|
try:
|
|
last = datetime.fromisoformat(s.last_decay_at)
|
|
elapsed = (_now() - last).total_seconds() / 60.0
|
|
if elapsed > 0:
|
|
capped = min(elapsed, CONFIG.CATCHUP_DECAY_MAX_MIN)
|
|
s.apply_tick(capped)
|
|
else:
|
|
s.last_decay_at = _now().isoformat()
|
|
except (ValueError, TypeError):
|
|
s.last_decay_at = _now().isoformat()
|
|
elif not s.last_decay_at:
|
|
s.last_decay_at = _now().isoformat()
|
|
_save_state()
|
|
return s
|
|
|
|
def init_server(server_instance):
|
|
from server import PromptServer
|
|
from aiohttp import web
|
|
|
|
@server_instance.routes.get("/comfygotchi/state")
|
|
async def get_state(request):
|
|
with _lock:
|
|
s = _apply_decay_on_read()
|
|
return web.json_response(s.to_dict())
|
|
|
|
@server_instance.routes.post("/comfygotchi/event")
|
|
async def post_event(request):
|
|
body = await request.json()
|
|
event_type = body.get("type", "")
|
|
caption = body.get("caption", "")
|
|
with _lock:
|
|
s = _get_state()
|
|
if event_type == "feed":
|
|
s.apply_feed()
|
|
elif event_type == "comment":
|
|
if caption:
|
|
s.comment_history.append(caption)
|
|
if len(s.comment_history) > 50:
|
|
s.comment_history = s.comment_history[-50:]
|
|
s.last_comment_qwen = bool(body.get("qwen", False))
|
|
elif event_type == "love":
|
|
s.apply_love()
|
|
elif event_type == "tick":
|
|
elapsed = body.get("elapsed_minutes", 1.0)
|
|
s.apply_tick(elapsed)
|
|
if s.stage == "ghost" and s.died_at:
|
|
try:
|
|
died = datetime.fromisoformat(s.died_at)
|
|
ghost_min = (_now() - died).total_seconds() / 60.0
|
|
s.check_reincarnation(ghost_min, 0)
|
|
except (ValueError, TypeError):
|
|
pass
|
|
elif event_type == "ghost_tick":
|
|
ghost_min = body.get("ghost_minutes", 0)
|
|
ghost_ev = body.get("ghost_events", 0)
|
|
s.check_reincarnation(ghost_min, ghost_ev)
|
|
elif event_type == "egg_caption":
|
|
if hasattr(s, "egg_captions") and caption:
|
|
s.egg_captions.append(caption)
|
|
elif event_type == "set_variant":
|
|
v = body.get("variant", "blob")
|
|
p = body.get("personality", "")
|
|
s.variant = v
|
|
s.personality = p
|
|
s.variant_determined = True
|
|
elif event_type == "play":
|
|
s.apply_play()
|
|
s.comment_history.append("Yay! Let's play!")
|
|
if len(s.comment_history) > 50: s.comment_history = s.comment_history[-50:]
|
|
elif event_type == "clean":
|
|
s.apply_clean()
|
|
s.comment_history.append("All clean now!")
|
|
if len(s.comment_history) > 50: s.comment_history = s.comment_history[-50:]
|
|
elif event_type == "medicine":
|
|
s.apply_medicine()
|
|
s.comment_history.append("Ugh... but I feel better.")
|
|
if len(s.comment_history) > 50: s.comment_history = s.comment_history[-50:]
|
|
s.last_event_at = _now().isoformat()
|
|
_save_state()
|
|
return web.json_response(s.to_dict())
|
|
|
|
@server_instance.routes.post("/comfygotchi/save")
|
|
async def post_save(request):
|
|
with _lock:
|
|
_save_state()
|
|
return web.json_response({"ok": True})
|
|
|
|
@server_instance.routes.post("/comfygotchi/reset")
|
|
async def post_reset(request):
|
|
with _lock:
|
|
_reset_state()
|
|
return web.json_response(_get_state().to_dict())
|
|
|
|
@server_instance.routes.get("/comfygotchi/config")
|
|
async def get_config(request):
|
|
return web.json_response({
|
|
"HATCH_THRESHOLD": CONFIG.HATCH_THRESHOLD,
|
|
"EVOLUTION_THRESHOLD": CONFIG.EVOLUTION_THRESHOLD,
|
|
"DELTA_FEED": CONFIG.DELTA_FEED,
|
|
"DELTA_LOVE": CONFIG.DELTA_LOVE,
|
|
"DELTA_HUNGER_PER_MIN": CONFIG.DELTA_HUNGER_PER_MIN,
|
|
})
|
|
|
|
try:
|
|
from server import PromptServer
|
|
SERVER = PromptServer.instance
|
|
init_server(SERVER)
|
|
except Exception as e:
|
|
print(f"[ComfyGotchi] Could not register server routes: {e}")
|