{"id":20627547,"url":"https://github.com/xubeiyan/tictactoeplus","last_synced_at":"2026-05-06T11:36:46.343Z","repository":{"id":242439825,"uuid":"809549027","full_name":"xubeiyan/TicTacToePlus","owner":"xubeiyan","description":"a new version of tic-tac-toe","archived":false,"fork":false,"pushed_at":"2025-02-12T09:01:34.000Z","size":249,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-04T04:17:23.479Z","etag":null,"topics":["svelte","tailwindcss","websocket"],"latest_commit_sha":null,"homepage":"https://chenhai.net/games/tictactoeplus","language":"Svelte","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xubeiyan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-03T01:33:45.000Z","updated_at":"2025-02-12T09:00:16.000Z","dependencies_parsed_at":"2024-06-03T03:45:51.091Z","dependency_job_id":"bb5f3e5e-a39a-4c7e-8cef-d5203edeee36","html_url":"https://github.com/xubeiyan/TicTacToePlus","commit_stats":null,"previous_names":["xubeiyan/tictactoeplus"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/xubeiyan/TicTacToePlus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xubeiyan%2FTicTacToePlus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xubeiyan%2FTicTacToePlus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xubeiyan%2FTicTacToePlus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xubeiyan%2FTicTacToePlus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xubeiyan","download_url":"https://codeload.github.com/xubeiyan/TicTacToePlus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xubeiyan%2FTicTacToePlus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32692388,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T08:33:17.875Z","status":"ssl_error","status_checked_at":"2026-05-06T08:33:17.221Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["svelte","tailwindcss","websocket"],"created_at":"2024-11-16T13:17:14.376Z","updated_at":"2026-05-06T11:36:46.308Z","avatar_url":"https://github.com/xubeiyan.png","language":"Svelte","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TicTacToe Plus\n\n这是一个井字棋 (Tic-Tac-Toe) 的强化版本，更大的棋子能覆盖更小的棋子。从而下法上就有很多变数。\n\n## websocket通信机制\n\n用 `s2b` 代表服务器发送到浏览器的消息，`b2s`代表浏览器发送到服务器的消息\n\n\u003cdetails\u003e\n\u003csummary\u003eWebSocket 通信接口 (点击展开)\u003c/summary\u003e\n\n* 甲登录服务器，建立房间\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"create_room\",\n        \"content\": {\n            \"nick_name\": \"abc\",\n        }\n    }\n    ```\n    * `s2b`（成功）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"create_room_reply\",\n        \"content\": {\n            \"message\": \"success\",\n            \"room_name\": \"会React的Java新手\",\n            \"room_code\": 2345\n        }\n    }\n    ```\n    * `s2b`（失败）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"create_room_reply\",\n        \"content\": {\n            \"message\": \"fail\",\n            \"reason\": \"server full\"\n        }\n    }\n    ```\n\n* 乙登录加入已存在的房间\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"join_room\",\n        \"content\": {\n            \"nick_name\": \"def\",\n            \"room_code\": 2345,\n        }\n    }\n    ```\n    * `s2b`（成功）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"join_room_reply\",\n        \"content\": {\n            \"message\": \"success\",\n            \"room_name\": \"会React的Java新手\"\n        }\n    }\n    ```\n    * `s2b`（失败）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"join_room_reply\",\n        \"content\": {\n            \"message\": \"fail\",\n            \"reason\": \"no room code\"\n        }\n    }\n    ```\n\n* 两方都加入，请求游戏开始\n    * `s2b`\n    ```javascript \n    {\n        \"v\": \"1\",\n        \"type\": \"start_request\",\n        \"content\": {\n            \"host\": \"Rich\",\n            \"client\": \"Evan\"\n        }\n    }\n    ```\n    * `b2s`（确认开始）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"start_confirm\",\n        \"content\": {\n            \"from\": \"host\",\n            \"confirm\": true,\n        }\n    }\n    ```\n    * `s2b` （确认消息）\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"confirm_change\",\n        \"content\": {\n            \"from\": \"host\",\n            \"confirm\": true\n        }\n    }\n    ```\n\n* 两方roll点，确定谁先手\n    * `s2b`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"initial_roll\",\n        \"content\": {\n            \"initial\": \"host\",\n        }\n    }\n    ```\n* 选择需要下的棋子\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"select_chess\",\n        \"content\": {\n            \"room_name\": \"会React的Java新手\",\n            \"color\": \"red\",\n            \"index\": 0\n        }\n    }\n    ```\n    * `s2b`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"select_chess_broadcast\",\n        \"content\": {\n            \"color\": \"red\",\n            \"index\": 0\n        }\n    }\n    ```\n* 放置棋子\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"put_chess\",\n        \"content\": {\n            \"role\": \"host\",\n            \"room_name\": \"会React的Java新手\",\n            \"color\": \"red\",\n            \"holder_index\": 0,\n            \"board_index\": 1,\n        }\n    }\n    ```\n    * `s2b`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"put_chess_broadcast\",\n        \"content\": {\n            \"role\": \"host\",\n            \"color\": \"red\",\n            \"holder_index\": 0,\n            \"board_index\": 1,\n        }\n    }\n    ```\n* 宣告胜利\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"win\",\n        \"content\": {\n            \"role\": \"host\",\n            \"room_name\": \"会React的Java新手\",\n            \"color\": \"red\",\n            \"position\": \"row1\"\n        }\n    }\n    ```\n    * `s2b`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"win_broadcast\",\n        \"content\": {\n            \"role\": \"host\",\n            \"color\": \"red\",\n            \"position\": \"row1\"\n        }\n    }\n    ```\n* 平局\n    * `b2s`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"no_chess_put\",\n        \"content\": {\n            \"room_name\": \"会React的Java新手\",\n            \"color\": \"red\",\n        }\n    }\n    ```\n    * `s2b` \n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"draw_broadcast\",\n        \"content\": {\n            \"color\": \"red\",\n        }\n    }\n    ```\n\n* 丢失连接\n    * `s2b`\n    ```javascript\n    {\n        \"v\": \"1\",\n        \"type\": \"other_lost_connection\",\n        \"content\": {\n            \"from\": \"host\",\n        }\n    }\n    ```\n\u003c/details\u003e\n\n## 部署策略\n\n分为两个部分，一个是网页客户端，仅有一个静态页面，只需打包后部署到服务器上；另一个则是`websocket`服务端，需要使用`node`环境\n\n### 网页客户端\n\n#### 克隆仓库\n\n```shell\n$ git clone https://github.com/xubeiyan/TicTacToePlus\n```\n\n#### 安装依赖\n\n```shell\n$ cd TicTacToePlus\n$ pnpm i\n```\n\n#### 修改监听的 `websocket` 地址\n\n复制一份 `.env.example` 为 `.env`，修改 `PUBLIC_WEBSOCKET_ADDRESS` 为合适的值，例如 `ws://yourdomain.net:6789`\n\n```shell\n# 在TicTacToePlus目录下\n$ cp .env.example .env\n```\n修改`.env`文件 (省略)\n\n#### 构建打包好的文件\n\n```shell\n# 在TicTacToePlus目录下\n$ pnpm build\n```\n\n文件会生成在 `build` 目录下，复制到你的服务器的路径下\n\n```shell\n# 在TicTacToePlus目录下\n$ cp -r build/* /path/to/your/staticFile\n``` \n\n### `websocket` 服务器\n\n#### 修改 `wsServer` 目录下的配置文件\n\n复制一份 `wsServer/.env.example` 为 `wsServer/.env`，可修改的值包括 `PORT`, `MAX_ROOM`, `PROD`(true则会在 `WebSocket` 连接中进行连接健康度查询)\n\n```shell\n# 在TicTacToePlus目录下\n$ cp wsServer/.env.example wsServer/.env\n```\n\n修改`.env`文件 (省略)\n\n#### 启动服务器\n\n```shell\n# 在TicTacToePlus目录下\n$ node --env-file=wsServer/.env wsServer/server.js\n```\n\n出现下列消息则启动成功\n```\n[GameServer] Started on port 6789, max room size is 5\n```\n\n#### 反向代理服务器\n\n通常为了不额外暴露端口，我们通常使用反向代理服务器（例如 `nginx`）进行一次转发，假设你想通过 `http://yourdomain.net/websocket`进行转发到 `ws://localhost:6789`，下面是 `nginx` 的配置文件的样例\n```conf\nserver {\n    # 域名\n    server_name yourdomain.net;\n\n    # 监听在80端口\n    listen 80;          # IPv4\n    listen [::]:80;     # IPv6\n\n    # 静态文件部署\n    root /path/to/your/staticFile;\n\n    # 转发到/websocket的\n    location /websocket {\n        proxy_pass http://localhost:6789;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $http_host;\n    }\n}\n```\n\n则在前面 `.env` 中的 `PUBLIC_WEBSOCKET_ADDRESS` 则可以修改为 `http://yourdomain.net/websocket`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxubeiyan%2Ftictactoeplus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxubeiyan%2Ftictactoeplus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxubeiyan%2Ftictactoeplus/lists"}