游戏包格式
小游戏包的安装格式、目录约束、清单字段与运行时限制。
游戏包格式#
本文档定义飞行雪绒当前小游戏包的安装格式、目录约束和运行时行为。
目标#
- 让外部开发者可以交付一个结构正确的
zip,主程序即可安装。 - 让游戏代码、资源、粒子、特效与桌宠主工程隔离。
- 让卸载、导出、升级都围绕同一套包目录工作。
运行时目录#
小游戏安装到共享根目录,而不是项目源码树。
C:\AemeathDeskPet\
games\
inbox\ # 待扫描 ZIP
archive\ # 已安装 ZIP 归档
rejected\ # 安装失败 ZIP
installed\ # 已安装游戏
data\ # 每个游戏自己的存档
cache\ # 安装和运行缓存
config\
games\
registry.json
如果设置了环境变量 AEMEATH_DESK_PET_HOME,则以上路径全部改为该目录下。
主程序发布包会额外携带一个内置官方包目录:
gamepack/
official/
lahai_tetris.zip
运行时会优先扫描这里的官方 zip 并自动安装;源码开发环境下如果这个目录不存在,再回退到仓库内的官方样板目录。
ZIP 根结构#
游戏 zip 解压后根目录必须直接包含 manifest.json。
manifest.json
code/
assets/
extensions/
README.txt
允许额外文件,但安装器只认以上核心布局。
目录说明#
manifest.json#
描述包自身、入口类、窗口参数和扩展模块声明。
code/#
游戏运行时代码。
- 入口模块必须位于这里。
- 游戏代码应该尽量只依赖宿主提供的公共 API。
- 不要在包代码里写死项目源码根目录。
assets/#
包内资源。
推荐子目录:
assets/
avatars/
images/
sounds/
fonts/
text/
游戏应通过运行时传入的 GameContext.assets_root 或 GameContext.asset_path(...) 读取资源。
extensions/#
游戏私有粒子/特效模块。
extensions/
particles/
effects/
这些模块不会被复制到 lib/script/practical 或 lib/script/effects。安装后仍然留在游戏自己的安装目录,由运行时按 manifest 注册。
README.txt#
可选,写给安装者或开发者的说明。
manifest 字段#
当前版本要求如下:
{
"package_type": "game",
"package_format_version": 1,
"runtime_api_version": 1,
"game_id": "lahai_tetris",
"name": "拉海洛方块",
"version": "1.0.0",
"summary": "圆角彩虹字母俄罗斯方块",
"description": "官方示例包",
"official": true,
"author": "Flying Snow Velvet",
"command_aliases": ["拉海洛方块"],
"entry_module": "lahai_tetris_pkg.entry",
"entry_class": "LahaiTetrisGame",
"bgm_keyword": "星际穿跃",
"bgm_artist": "鸣潮先约电台",
"window": {
"default_width": 1000,
"default_height": 800,
"minimum_width": 600,
"minimum_height": 480,
"aspect_width": 10,
"aspect_height": 8
},
"extensions": {
"particles": [
{
"module": "extensions/particles/example_particle.py",
"class_name": "ExampleParticleScript",
"local_id": "example"
}
],
"effects": [
{
"module": "extensions/effects/example_effect.py",
"class_name": "ExampleEffectScript",
"local_id": "flash"
}
]
}
}
字段约束#
package_type目前固定为gamepackage_format_version当前固定为1runtime_api_version当前固定为1game_id只允许小写字母、数字、下划线、点、短横线entry_module必须指向code/下可导入模块entry_class必须是该模块中的入口类
入口类约束#
入口类当前约定:
class ExampleGame:
def __init__(self, context: GameContext) -> None:
...
def create_widget(self, parent=None):
...
其中:
context.install_dir: 当前游戏安装目录context.code_root:code/context.assets_root:assets/context.data_root: 该游戏存档目录context.cache_root: 该游戏缓存目录
粒子 / 特效扩展规则#
这是当前包格式和旧桌宠结构最大的区别。
1. 扩展模块留在包内#
不要把自定义粒子、特效源码塞进主项目全局目录。
2. manifest 负责声明#
安装器和运行时只根据 manifest.extensions 加载扩展。
3. 运行时统一加前缀#
每个游戏扩展都会注册成:
<game_id>.<local_id>
例如:
lahai_tetris.glow_burstlahai_tetris.preview_riselahai_tetris.line_flash
这样做有三个目的:
- 避免不同游戏撞粒子名
- 卸载时可按游戏前缀回收
- 不污染桌宠原有全局粒子/特效 ID
4. 包内扩展不要直接用全局装饰器注册#
不推荐这样写:
@register_particle("foo")
class FooParticleScript(...):
...
原因是这会污染全局 ID。
推荐做法:
- 类本身只实现脚本逻辑
- 由
manifest.json指定模块路径、类名、local_id - 宿主运行时用
<game_id>.<local_id>完成最终注册
5. 游戏代码应通过 GameContext 取前缀 ID#
例如:
particle_id = context.qualify_particle_id("glow_burst")
effect_id = context.qualify_effect_id("flash")
安装流程#
- 对于随主程序发布的官方包,运行时会先扫描
gamepack/official/*.zip并自动安装 - 对于开发者或玩家手动提供的包,用户可通过游戏管理器选择 ZIP,或将 ZIP 放进
games/inbox - 安装器解压 ZIP
- 校验
manifest.json - 校验入口模块和扩展模块路径
- 安装到:
games/installed/<game_id>/<version>/
- 刷新
config/games/registry.json - 动态注册该游戏的粒子和特效扩展
卸载流程#
- 关闭正在运行的该游戏窗口
- 注销该游戏注册过的粒子/特效 ID
- 删除
games/installed/<game_id>/ - 从
registry.json移除条目 - 保留
games/data/<game_id>/存档目录
导出流程#
已安装游戏可以再次打包导出,导出内容就是安装目录自身的标准结构。
也就是说:
- 一个解压态包
- 导出成 zip
- 再重新安装
这三者是同一套 contract。
官方示例包#
当前官方样板包:
发布包内会将它额外导出为:
gamepack/official/lahai_tetris.zip
其内容包含:
code/lahai_tetris_pkg/assets/avatars/assets/text/tips.txtextensions/particles/manifest.json
它用于展示:
- 包内资源读取
- 本地存档路径
- 自定义粒子前缀注册
#游戏管理器中的安装、打开、卸载、导出流程