# -*- coding: utf-8 -*- import json import logging import re from odoo import http from odoo.http import request, Response _logger = logging.getLogger(__name__) def _json(data): """返回 UTF-8 JSON 响应(与 game_base 一致)。""" return Response( json.dumps(data, ensure_ascii=False), content_type='application/json; charset=utf-8', ) def _post_body(): try: raw = request.httprequest.get_data(as_text=True) or '{}' return json.loads(raw) except Exception: return {} class YtWorldApi(http.Controller): """前端沙盘记忆接口(JSON over HTTP)。auth=user:需已登录 Odoo。 前端统一带 ?db=game 且 credentials:'same-origin',故路由须为 type='http' 并以 _json 返回原始 JSON(与 game-api.js 的 fetch().json() 对齐)。""" @http.route('/yt_world/api/world', type='http', auth='user', methods=['GET', 'OPTIONS'], csrf=False) def world(self, **kw): """取或建当前用户世界,导出 seed + 地块覆盖。""" user = request.env.user rec = request.env['yt.world'].sudo().get_or_create_for_user(user) return _json(rec.export_state()) @http.route('/yt_world/api/tiles/save', type='http', auth='user', methods=['POST', 'OPTIONS'], csrf=False) def save_tiles(self, **kw): """增量覆盖保存地块。body: {"tiles": [...]}""" user = request.env.user world = request.env['yt.world'].sudo().search( [('user_id', '=', user.id)], limit=1) if not world: return _json({'ok': False, 'error': 'no_world'}) body = _post_body() tiles = body.get('tiles') or [] world.import_tiles(tiles) return _json({'ok': True, 'saved': len(tiles)}) @http.route('/yt_world/api/settings', type='http', auth='user', methods=['POST', 'OPTIONS'], csrf=False) def save_settings(self, **kw): """保存相机/UI 偏好。body: {"settings": {...}}""" user = request.env.user world = request.env['yt.world'].sudo().search( [('user_id', '=', user.id)], limit=1) body = _post_body() settings = body.get('settings') if world and settings is not None: world.settings = settings return _json({'ok': True}) @http.route('/yt_world/api/world/rename', type='http', auth='user', methods=['POST', 'OPTIONS'], csrf=False) def rename_world(self, **kw): """修改当前用户世界名称。body: {"name": "..."} 校验:trim 后 1-24 字符,禁控制字符,空白正常允许。""" user = request.env.user world = request.env['yt.world'].sudo().search( [('user_id', '=', user.id)], limit=1) if not world: return _json({'ok': False, 'error': 'no_world'}) body = _post_body() name = (body.get('name') or '').strip() if not name: return _json({'ok': False, 'error': 'empty'}) if len(name) > 24: return _json({'ok': False, 'error': 'too_long'}) if re.search(r'[\x00-\x1f\x7f]', name): return _json({'ok': False, 'error': 'invalid_char'}) old = world.name world.name = name _logger.info('yt.world rename uid=%s %r -> %r', user.id, old, name) return _json({'ok': True, 'name': name})