mirror of
https://github.com/tiennm99/gomoku.git
synced 2026-09-03 00:18:02 +00:00
refactor: rename Go module and copy core pkgs into server/pkg
- Module: github.com/ratel-online/server → github.com/tiennm99/gomoku/server
- Copied core/{log,util/async,util/json,util/strings,model,network,protocol,consts}
into server/pkg/* (temporary shims until phase-05 replaces protocol/network)
- Rewrote all ratel-online import paths across server/**/*.go
- Trimmed main.go: single -p flag, WS-only on :1999, no TCP/bot/static
- Trimmed network/wss.go: endpoint /gomoku, no static file serving
- Updated Makefile: removed TCP/bot references, port 1999
This commit is contained in:
+15
-51
@@ -1,45 +1,31 @@
|
||||
.PHONY: help build run stop clean logs shell test prod-build prod-up prod-down
|
||||
.PHONY: help build run stop logs shell clean test
|
||||
|
||||
# 检测 Docker Compose 命令
|
||||
# Detect Docker Compose command
|
||||
DOCKER_COMPOSE := $(shell which docker-compose 2>/dev/null)
|
||||
ifeq ($(DOCKER_COMPOSE),)
|
||||
DOCKER_COMPOSE := $(shell docker compose version >/dev/null 2>&1 && echo "docker compose" || echo "")
|
||||
endif
|
||||
ifeq ($(DOCKER_COMPOSE),)
|
||||
$(error "Docker Compose 未安装。请安装 docker-compose 或升级到支持 'docker compose' 的 Docker 版本")
|
||||
$(error "Docker Compose not installed. Install docker-compose or upgrade Docker.")
|
||||
endif
|
||||
|
||||
# 默认目标
|
||||
help:
|
||||
@echo "Ratel游戏服务器 Docker 管理命令"
|
||||
@echo "Gomoku server Docker management"
|
||||
@echo ""
|
||||
@echo "当前使用的 Docker Compose 命令: $(DOCKER_COMPOSE)"
|
||||
@echo ""
|
||||
@echo "开发环境命令:"
|
||||
@echo " make build - 构建Docker镜像"
|
||||
@echo " make run - 启动开发环境"
|
||||
@echo " make stop - 停止开发环境"
|
||||
@echo " make logs - 查看日志"
|
||||
@echo " make shell - 进入容器shell"
|
||||
@echo " make clean - 清理容器和镜像"
|
||||
@echo ""
|
||||
@echo "生产环境命令:"
|
||||
@echo " make prod-build - 构建生产镜像"
|
||||
@echo " make prod-up - 启动生产环境"
|
||||
@echo " make prod-down - 停止生产环境"
|
||||
@echo ""
|
||||
@echo "测试命令:"
|
||||
@echo " make test - 运行测试"
|
||||
@echo " make build - Build Docker image"
|
||||
@echo " make run - Start server (dev)"
|
||||
@echo " make stop - Stop server"
|
||||
@echo " make logs - View logs"
|
||||
@echo " make shell - Open container shell"
|
||||
@echo " make clean - Remove containers and images"
|
||||
@echo " make test - Run Go tests"
|
||||
|
||||
# 开发环境命令
|
||||
build:
|
||||
$(DOCKER_COMPOSE) build
|
||||
|
||||
run:
|
||||
$(DOCKER_COMPOSE) up -d
|
||||
@echo "服务已启动:"
|
||||
@echo " WebSocket: http://localhost:9998"
|
||||
@echo " TCP: localhost:9999"
|
||||
@echo "Server started: ws://localhost:1999/gomoku"
|
||||
|
||||
stop:
|
||||
$(DOCKER_COMPOSE) down
|
||||
@@ -48,33 +34,11 @@ logs:
|
||||
$(DOCKER_COMPOSE) logs -f
|
||||
|
||||
shell:
|
||||
$(DOCKER_COMPOSE) exec ratel-server sh
|
||||
$(DOCKER_COMPOSE) exec gomoku-server sh
|
||||
|
||||
clean:
|
||||
$(DOCKER_COMPOSE) down -v
|
||||
docker rmi ratel-server:latest || true
|
||||
docker rmi gomoku-server:latest || true
|
||||
|
||||
# 生产环境命令
|
||||
prod-build:
|
||||
docker build -t ratel-server:latest .
|
||||
|
||||
prod-up:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.prod.yaml up -d
|
||||
@echo "生产环境已启动:"
|
||||
@echo " WebSocket: http://localhost:9998"
|
||||
@echo " TCP: localhost:9999"
|
||||
@echo " HTTP: http://localhost:80"
|
||||
|
||||
prod-down:
|
||||
$(DOCKER_COMPOSE) -f docker-compose.prod.yaml down
|
||||
|
||||
# 测试命令
|
||||
test:
|
||||
@echo "测试WebSocket连接..."
|
||||
@curl -s -o /dev/null -w "HTTP状态码: %{http_code}\n" http://localhost:9998 || echo "WebSocket端口未响应"
|
||||
@echo ""
|
||||
@echo "测试TCP连接..."
|
||||
@nc -zv localhost 9999 2>&1 || echo "TCP端口未响应"
|
||||
@echo ""
|
||||
@echo "测试Nginx健康检查..."
|
||||
@curl -s http://localhost/health || echo "Nginx未运行"
|
||||
go test ./...
|
||||
|
||||
@@ -3,7 +3,7 @@ package consts
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ratel-online/core/consts"
|
||||
"github.com/tiennm99/gomoku/server/pkg/consts"
|
||||
)
|
||||
|
||||
type StateID int
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/awesome-cap/hashmap"
|
||||
"github.com/ratel-online/core/log"
|
||||
modelx "github.com/ratel-online/core/model"
|
||||
"github.com/ratel-online/core/network"
|
||||
"github.com/ratel-online/core/util/async"
|
||||
"github.com/ratel-online/core/util/json"
|
||||
"github.com/ratel-online/core/util/strings"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
modelx "github.com/tiennm99/gomoku/server/pkg/model"
|
||||
"github.com/tiennm99/gomoku/server/pkg/network"
|
||||
"github.com/tiennm99/gomoku/server/pkg/async"
|
||||
"github.com/tiennm99/gomoku/server/pkg/json"
|
||||
"github.com/tiennm99/gomoku/server/pkg/strings"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
)
|
||||
|
||||
// In-memory data store. All state is volatile — server restart clears everything.
|
||||
|
||||
@@ -3,7 +3,7 @@ package database
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
)
|
||||
|
||||
// Gomoku stores the state of a gomoku (five-in-a-row) game.
|
||||
|
||||
@@ -6,12 +6,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/model"
|
||||
"github.com/ratel-online/core/network"
|
||||
"github.com/ratel-online/core/protocol"
|
||||
"github.com/ratel-online/core/util/json"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/pkg/model"
|
||||
"github.com/tiennm99/gomoku/server/pkg/network"
|
||||
"github.com/tiennm99/gomoku/server/pkg/protocol"
|
||||
"github.com/tiennm99/gomoku/server/pkg/json"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
)
|
||||
|
||||
// Role represents a player's role within a room.
|
||||
|
||||
+3
-6
@@ -1,17 +1,14 @@
|
||||
module github.com/ratel-online/server
|
||||
module github.com/tiennm99/gomoku/server
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/Szzrain/Milky-go-sdk v1.0.1
|
||||
github.com/awesome-cap/hashmap v0.0.0-20211211100532-e3300ac4ae14
|
||||
github.com/awesome-cap/hashmap v0.0.0-20220308123617-f10e2b637d7d
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/ratel-online/core v0.0.0-20250225062905-81b6faff6d25
|
||||
github.com/json-iterator/go v1.1.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/awesome-cap/im v0.0.0-20210720090440-7556eb92965d // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/stretchr/testify v1.8.0 // indirect
|
||||
|
||||
+2
-39
@@ -1,62 +1,25 @@
|
||||
github.com/Szzrain/Milky-go-sdk v1.0.1 h1:dsKIUtW7lI7RGLOuNut2ChxqQpM+bqd+3AafCIIhYRc=
|
||||
github.com/Szzrain/Milky-go-sdk v1.0.1/go.mod h1:vyl5G/6TQha+Ygf9ZWkDN//7dJFq2V0ezdk3Rm2kDKQ=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/awesome-cap/hashmap v0.0.0-20211211100532-e3300ac4ae14 h1:YH6IXdLClQbuKrhYdyXYzdE9cicwU7HaD7YDMmo0oRY=
|
||||
github.com/awesome-cap/hashmap v0.0.0-20211211100532-e3300ac4ae14/go.mod h1:5vIRKw3P2HirHOPm1xQfJ6GmPfZQemzbIXfU0MqLlZU=
|
||||
github.com/awesome-cap/im v0.0.0-20210720090440-7556eb92965d h1:lc5kZK/lqmqAfNeXcRPkwXKdwO/9fQ09Wpz9p/qp4So=
|
||||
github.com/awesome-cap/im v0.0.0-20210720090440-7556eb92965d/go.mod h1:dIipyrtcBTc6up0MNw5w7ONoNKGksz2EGCVlcwiRjE4=
|
||||
github.com/awesome-cap/hashmap v0.0.0-20220308123617-f10e2b637d7d h1:3UBn4jCuvUVEHoz/HONIhhOy8ry2hfemf+PDlKUQnMs=
|
||||
github.com/awesome-cap/hashmap v0.0.0-20220308123617-f10e2b637d7d/go.mod h1:5vIRKw3P2HirHOPm1xQfJ6GmPfZQemzbIXfU0MqLlZU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/memberlist v0.2.4/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/ratel-online/core v0.0.0-20250225062905-81b6faff6d25 h1:9vdciSkTnXNoZkTqOe9rSP40PGRvNk/OzYxPCmWGh1c=
|
||||
github.com/ratel-online/core v0.0.0-20250225062905-81b6faff6d25/go.mod h1:8SYaPGDk9dVGnUIEkanZvV1ErTqd8OdedKNCwyXgRjI=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
+8
-42
@@ -2,54 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/util/async"
|
||||
"github.com/ratel-online/server/bot"
|
||||
"github.com/ratel-online/server/network"
|
||||
"github.com/tiennm99/gomoku/server/network"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
Wsport int
|
||||
Tcpport int
|
||||
StaticDir string
|
||||
BotAddr string
|
||||
BotToken string
|
||||
BotGroup int64
|
||||
)
|
||||
var port int
|
||||
|
||||
func main() {
|
||||
flag.IntVar(&Wsport, "w", 9998, "WebsocketServer Port")
|
||||
flag.IntVar(&Tcpport, "t", 9999, "TcpServer Port")
|
||||
flag.StringVar(&StaticDir, "s", "../web", "Static files directory")
|
||||
flag.StringVar(&BotAddr, "bot", "", "Bot connection address")
|
||||
flag.StringVar(&BotToken, "bot-token", "", "Bot token")
|
||||
flag.Int64Var(&BotGroup, "bot-group", 0, "Bot group ID")
|
||||
|
||||
flag.IntVar(&port, "p", 1999, "WebSocket server port")
|
||||
flag.Parse()
|
||||
// Connect QQ bot if configured
|
||||
if BotAddr != "" && BotToken != "" && BotGroup != 0 {
|
||||
err := bot.Connect(BotAddr, BotToken, BotGroup)
|
||||
if err != nil {
|
||||
log.Panic(fmt.Sprintf("连接Bot失败: %v", err))
|
||||
}
|
||||
// Send test message to the bot group
|
||||
err = bot.SendGroupMessage(BotGroup, "Server started!")
|
||||
if err != nil {
|
||||
log.Errorf("发送群消息失败: %v", err)
|
||||
} else {
|
||||
log.Infof("已发送群消息到 %d", BotGroup)
|
||||
}
|
||||
defer bot.Close()
|
||||
}
|
||||
|
||||
async.Async(func() {
|
||||
wsServer := network.NewWebsocketServer(":" + strconv.Itoa(Wsport), StaticDir)
|
||||
log.Panic(wsServer.Serve())
|
||||
})
|
||||
|
||||
server := network.NewTcpServer(":" + strconv.Itoa(Tcpport))
|
||||
log.Panic(server.Serve())
|
||||
addr := ":" + strconv.Itoa(port)
|
||||
log.Infof("Starting gomoku server on %s/gomoku\n", addr)
|
||||
wsServer := network.NewWebsocketServer(addr)
|
||||
log.Panic(wsServer.Serve())
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/model"
|
||||
"github.com/ratel-online/core/network"
|
||||
"github.com/ratel-online/core/protocol"
|
||||
"github.com/ratel-online/core/util/async"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/ratel-online/server/state"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/pkg/model"
|
||||
"github.com/tiennm99/gomoku/server/pkg/network"
|
||||
"github.com/tiennm99/gomoku/server/pkg/protocol"
|
||||
"github.com/tiennm99/gomoku/server/pkg/async"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
"github.com/tiennm99/gomoku/server/state"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
+27
-29
@@ -1,47 +1,45 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/protocol"
|
||||
"net/http"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/pkg/protocol"
|
||||
)
|
||||
|
||||
// Websocket is a WebSocket-only HTTP server bound to a single /gomoku endpoint.
|
||||
type Websocket struct {
|
||||
addr string
|
||||
StaticDir string
|
||||
addr string
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(r *http.Request) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
func NewWebsocketServer(addr string, staticDir string) Websocket {
|
||||
return Websocket{addr: addr, StaticDir: staticDir}
|
||||
// NewWebsocketServer creates a Websocket server listening on addr (e.g. ":1999").
|
||||
func NewWebsocketServer(addr string) Websocket {
|
||||
return Websocket{addr: addr}
|
||||
}
|
||||
|
||||
func (w Websocket) Serve() error {
|
||||
http.HandleFunc("/ws", serveWs)
|
||||
if w.StaticDir != "" {
|
||||
http.Handle("/", http.FileServer(http.Dir(w.StaticDir)))
|
||||
log.Infof("Serving static files from %s\n", w.StaticDir)
|
||||
}
|
||||
log.Infof("Websocket server listener on %s\n", w.addr)
|
||||
return http.ListenAndServe(w.addr, nil)
|
||||
http.HandleFunc("/gomoku", serveWs)
|
||||
log.Infof("WebSocket server listening on %s/gomoku\n", w.addr)
|
||||
return http.ListenAndServe(w.addr, nil)
|
||||
}
|
||||
|
||||
func serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = handle(protocol.NewWebsocketReadWriteCloser(conn))
|
||||
if err != nil{
|
||||
log.Error(err)
|
||||
}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = handle(protocol.NewWebsocketReadWriteCloser(conn))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package async
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func Async(fun func()) {
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
PrintStackTrace(err)
|
||||
}
|
||||
}()
|
||||
fun()
|
||||
}()
|
||||
}
|
||||
|
||||
func PrintStackTrace(err interface{}) {
|
||||
buf := bytes.Buffer{}
|
||||
buf.WriteString(fmt.Sprintf("%v\n", err))
|
||||
for i := 1; ; i++ {
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s:%d (0x%x)\n", file, line, pc))
|
||||
}
|
||||
fmt.Println(buf.String())
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
IsStart = "INTERACTIVE_SIGNAL_START"
|
||||
IsStop = "INTERACTIVE_SIGNAL_STOP"
|
||||
MaxPacketSize = 65536
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package json
|
||||
|
||||
import (
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
jsoniter.RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
t := *((*[]byte)(ptr))
|
||||
stream.WriteString(string(t))
|
||||
}, nil)
|
||||
jsoniter.RegisterTypeDecoderFunc("[]uint8", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
str := iter.ReadString()
|
||||
*((*[]byte)(ptr)) = []byte(str)
|
||||
})
|
||||
}
|
||||
|
||||
func Marshal(v interface{}) []byte {
|
||||
data, _ := jsoniter.Marshal(v)
|
||||
return data
|
||||
}
|
||||
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
return jsoniter.Unmarshal(data, v)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func sprintf(t string, format string, args ...interface{}) string {
|
||||
_, path, line, _ := runtime.Caller(3)
|
||||
_, file := filepath.Split(path)
|
||||
return fmt.Sprintf(fmt.Sprintf("%s [%s] %s:%d %s", time.Now().Format("2006-01-02 15:04:05.999"), t, file, line, format), args...)
|
||||
}
|
||||
|
||||
func printf(t string, format string, args ...interface{}) {
|
||||
fmt.Printf(sprintf(t, format, args...))
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
printf("INFO", format, args...)
|
||||
}
|
||||
|
||||
func Info(arg interface{}) {
|
||||
printf("INFO", "%v\n", arg)
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
printf("ERROR", format, args...)
|
||||
}
|
||||
|
||||
func Error(arg interface{}) {
|
||||
printf("ERROR", "%v\n", arg)
|
||||
}
|
||||
|
||||
func Panicf(format string, args ...interface{}) {
|
||||
panic(sprintf("PANIC", format, args...))
|
||||
}
|
||||
|
||||
func Panic(arg interface{}) {
|
||||
printf("PANIC", "%v\n", arg)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package model
|
||||
|
||||
// AuthInfo is sent by the client on first connection to identify itself.
|
||||
type AuthInfo struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Score int64 `json:"score"`
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Score int64 `json:"score"`
|
||||
Group int `json:"group"`
|
||||
Pokers int `json:"pokers"`
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type"`
|
||||
TypeDesc string `json:"typeDesc"`
|
||||
Players int `json:"players"`
|
||||
State int `json:"state"`
|
||||
StateDesc string `json:"stateDesc"`
|
||||
Creator int64 `json:"creator"`
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Data
|
||||
Options []Option `json:"options"`
|
||||
}
|
||||
|
||||
type RoomList struct {
|
||||
Data
|
||||
Rooms []Room `json:"rooms"`
|
||||
}
|
||||
|
||||
type RoomInfo struct {
|
||||
Data
|
||||
Room Room `json:"room"`
|
||||
Players []Player `json:"players"`
|
||||
}
|
||||
|
||||
type RoomEvent struct {
|
||||
Data
|
||||
Room Room `json:"room"`
|
||||
Player Player `json:"player"`
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"github.com/tiennm99/gomoku/server/pkg/protocol"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var connId int64
|
||||
|
||||
type Conn struct {
|
||||
id int64
|
||||
state int
|
||||
conn protocol.ReadWriteCloser
|
||||
}
|
||||
|
||||
func Wrapper(conn protocol.ReadWriteCloser) *Conn {
|
||||
return &Conn{
|
||||
id: atomic.AddInt64(&connId, 1),
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) ID() int64 {
|
||||
return c.id
|
||||
}
|
||||
|
||||
func (c *Conn) IP() string {
|
||||
return c.conn.IP()
|
||||
}
|
||||
|
||||
func (c *Conn) Close() error {
|
||||
c.state = 1
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *Conn) State() int {
|
||||
return c.state
|
||||
}
|
||||
|
||||
func (c *Conn) Accept(apply func(msg protocol.Packet, c *Conn)) error {
|
||||
for {
|
||||
packet, err := c.conn.Read()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
apply(*packet, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) Write(packet protocol.Packet) error {
|
||||
return c.conn.Write(packet)
|
||||
}
|
||||
|
||||
func (c *Conn) Read() (*protocol.Packet, error) {
|
||||
return c.conn.Read()
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/tiennm99/gomoku/server/pkg/consts"
|
||||
"github.com/tiennm99/gomoku/server/pkg/json"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
lenSize = 4
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Body []byte `json:"data"`
|
||||
}
|
||||
|
||||
func (p Packet) Int() (int, error) {
|
||||
v, err := strconv.ParseInt(p.String(), 10, 64)
|
||||
return int(v), err
|
||||
}
|
||||
|
||||
func (p Packet) Int64() (int64, error) {
|
||||
v, _ := strconv.ParseInt(p.String(), 10, 64)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (p Packet) String() string {
|
||||
return string(p.Body)
|
||||
}
|
||||
|
||||
func (p Packet) Unmarshal(v interface{}) error {
|
||||
return json.Unmarshal(p.Body, v)
|
||||
}
|
||||
|
||||
func StringPacket(msg string) Packet {
|
||||
return Packet{
|
||||
Body: []byte(msg),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorPacket(err error) Packet {
|
||||
return Packet{
|
||||
Body: []byte(err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
func ObjectPacket(obj interface{}) Packet {
|
||||
return Packet{
|
||||
Body: json.Marshal(obj),
|
||||
}
|
||||
}
|
||||
|
||||
type ReadWriteCloser interface {
|
||||
Read() (*Packet, error)
|
||||
Write(msg Packet) error
|
||||
Close() error
|
||||
IP() string
|
||||
}
|
||||
|
||||
func readUint32(reader io.Reader) (uint32, error) {
|
||||
data := make([]byte, 4)
|
||||
_, err := io.ReadFull(reader, data)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(data), nil
|
||||
}
|
||||
|
||||
func encode(msg Packet) []byte {
|
||||
lenBytes := make([]byte, lenSize)
|
||||
binary.BigEndian.PutUint32(lenBytes, uint32(len(msg.Body)))
|
||||
data := make([]byte, 0)
|
||||
data = append(data, lenBytes...)
|
||||
return append(data, msg.Body...)
|
||||
}
|
||||
|
||||
func decode(r io.Reader) (*Packet, error) {
|
||||
l, err := readUint32(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if l > consts.MaxPacketSize {
|
||||
return nil, errors.New("Overflow max packet size " + strconv.Itoa(consts.MaxPacketSize))
|
||||
}
|
||||
dataBytes := make([]byte, l)
|
||||
_, err = io.ReadFull(r, dataBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Packet{
|
||||
Body: dataBytes,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type TcpReadWriteCloser struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func NewTcpReadWriteCloser(conn net.Conn) TcpReadWriteCloser {
|
||||
return TcpReadWriteCloser{conn: conn}
|
||||
}
|
||||
|
||||
func (t TcpReadWriteCloser) Read() (*Packet, error) {
|
||||
return decode(t.conn)
|
||||
}
|
||||
|
||||
func (t TcpReadWriteCloser) Write(msg Packet) error {
|
||||
_, err := t.conn.Write(encode(msg))
|
||||
return err
|
||||
}
|
||||
|
||||
func (t TcpReadWriteCloser) Close() error {
|
||||
return t.conn.Close()
|
||||
}
|
||||
|
||||
func (t TcpReadWriteCloser) IP() string {
|
||||
return t.conn.RemoteAddr().String()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/tiennm99/gomoku/server/pkg/json"
|
||||
)
|
||||
|
||||
type WebsocketReadWriteCloser struct {
|
||||
conn *websocket.Conn
|
||||
}
|
||||
|
||||
func NewWebsocketReadWriteCloser(conn *websocket.Conn) WebsocketReadWriteCloser {
|
||||
return WebsocketReadWriteCloser{conn: conn}
|
||||
}
|
||||
|
||||
func (w WebsocketReadWriteCloser) Read() (*Packet, error) {
|
||||
_, b, err := w.conn.ReadMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg := &Packet{}
|
||||
json.Unmarshal(b, msg)
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (w WebsocketReadWriteCloser) Write(msg Packet) error {
|
||||
return w.conn.WriteMessage(websocket.BinaryMessage, json.Marshal(msg))
|
||||
}
|
||||
|
||||
func (w WebsocketReadWriteCloser) Close() error {
|
||||
return w.conn.Close()
|
||||
}
|
||||
|
||||
func (w WebsocketReadWriteCloser) IP() string {
|
||||
return w.conn.RemoteAddr().String()
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package strings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func String(dest interface{}) string {
|
||||
var key string
|
||||
if dest == nil {
|
||||
return key
|
||||
}
|
||||
switch dest.(type) {
|
||||
case float64:
|
||||
key = strconv.FormatFloat(dest.(float64), 'f', -1, 64)
|
||||
case *float64:
|
||||
key = strconv.FormatFloat(*dest.(*float64), 'f', -1, 64)
|
||||
case float32:
|
||||
key = strconv.FormatFloat(float64(dest.(float32)), 'f', -1, 32)
|
||||
case *float32:
|
||||
key = strconv.FormatFloat(float64(*dest.(*float32)), 'f', -1, 32)
|
||||
case int:
|
||||
key = strconv.Itoa(dest.(int))
|
||||
case *int:
|
||||
key = strconv.Itoa(*dest.(*int))
|
||||
case uint:
|
||||
key = strconv.Itoa(int(dest.(uint)))
|
||||
case *uint:
|
||||
key = strconv.Itoa(int(*dest.(*uint)))
|
||||
case int8:
|
||||
key = strconv.Itoa(int(dest.(int8)))
|
||||
case *int8:
|
||||
key = strconv.Itoa(int(*dest.(*int8)))
|
||||
case uint8:
|
||||
key = strconv.Itoa(int(dest.(uint8)))
|
||||
case *uint8:
|
||||
key = strconv.Itoa(int(*dest.(*uint8)))
|
||||
case int16:
|
||||
key = strconv.Itoa(int(dest.(int16)))
|
||||
case *int16:
|
||||
key = strconv.Itoa(int(*dest.(*int16)))
|
||||
case uint16:
|
||||
key = strconv.Itoa(int(dest.(uint16)))
|
||||
case *uint16:
|
||||
key = strconv.Itoa(int(*dest.(*uint16)))
|
||||
case int32:
|
||||
key = strconv.Itoa(int(dest.(int32)))
|
||||
case *int32:
|
||||
key = strconv.Itoa(int(*dest.(*int32)))
|
||||
case uint32:
|
||||
key = strconv.Itoa(int(dest.(uint32)))
|
||||
case *uint32:
|
||||
key = strconv.Itoa(int(*dest.(*uint32)))
|
||||
case int64:
|
||||
key = strconv.FormatInt(dest.(int64), 10)
|
||||
case *int64:
|
||||
key = strconv.FormatInt(*dest.(*int64), 10)
|
||||
case uint64:
|
||||
key = strconv.FormatUint(dest.(uint64), 10)
|
||||
case *uint64:
|
||||
key = strconv.FormatUint(*dest.(*uint64), 10)
|
||||
case string:
|
||||
key = dest.(string)
|
||||
case *string:
|
||||
key = *dest.(*string)
|
||||
case []byte:
|
||||
key = string(dest.([]byte))
|
||||
case *[]byte:
|
||||
key = string(*dest.(*[]byte))
|
||||
case bool:
|
||||
if dest.(bool) {
|
||||
key = "true"
|
||||
} else {
|
||||
key = "false"
|
||||
}
|
||||
case *bool:
|
||||
if *dest.(*bool) {
|
||||
key = "true"
|
||||
} else {
|
||||
key = "false"
|
||||
}
|
||||
default:
|
||||
newValue, _ := json.Marshal(dest)
|
||||
key = string(newValue)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func Desensitize(str string) string {
|
||||
// Placeholder — original had Chinese word substitutions not relevant to gomoku.
|
||||
return strings.TrimSpace(str)
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
)
|
||||
|
||||
// create handles room creation. Prompts for game type, creates the room, and
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/util/json"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/pkg/json"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
)
|
||||
|
||||
// Gomoku state constants for channel-based turn sync.
|
||||
|
||||
@@ -2,8 +2,8 @@ package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
)
|
||||
|
||||
// home is the main menu. Player chooses to join an existing room or create a new one.
|
||||
|
||||
@@ -3,8 +3,8 @@ package state
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package state
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/core/util/async"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/ratel-online/server/state/game"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/pkg/async"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
"github.com/tiennm99/gomoku/server/state/game"
|
||||
)
|
||||
|
||||
var states = map[consts.StateID]State{}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ratel-online/core/log"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/ratel-online/server/state/game"
|
||||
"github.com/tiennm99/gomoku/server/pkg/log"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
"github.com/tiennm99/gomoku/server/state/game"
|
||||
)
|
||||
|
||||
type waiting struct{}
|
||||
|
||||
@@ -3,8 +3,8 @@ package state
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ratel-online/server/consts"
|
||||
"github.com/ratel-online/server/database"
|
||||
"github.com/tiennm99/gomoku/server/consts"
|
||||
"github.com/tiennm99/gomoku/server/database"
|
||||
)
|
||||
|
||||
// welcome is the initial state. Sends a greeting and transitions to home.
|
||||
@@ -12,7 +12,7 @@ type welcome struct{}
|
||||
|
||||
func (*welcome) Next(player *database.Player) (consts.StateID, error) {
|
||||
buf := bytes.Buffer{}
|
||||
buf.WriteString(fmt.Sprintf("Hi %s, Welcome to ratel online! rules at https://github.com/ratel-online/server/blob/main/README.md\n", player.Name))
|
||||
buf.WriteString(fmt.Sprintf("Hi %s, Welcome to ratel online! rules at https://github.com/tiennm99/gomoku/server/blob/main/README.md\n", player.Name))
|
||||
err := player.WriteString(buf.String())
|
||||
if err != nil {
|
||||
return 0, player.WriteError(err)
|
||||
|
||||
Reference in New Issue
Block a user