From 045e304853a05532f33b9284e975a14a0290be61 Mon Sep 17 00:00:00 2001 From: Amirmahdi Nourkazemi Date: Sun, 5 Jul 2026 18:41:12 +0330 Subject: [PATCH] fix --- cmd/server/settler.go | 8 ++++---- deploy.sh | 21 ++++++++++++++++----- internal/economy/economy.go | 8 +++++--- internal/ws/hub.go | 8 +++++--- internal/ws/protocol.go | 8 +++++--- internal/ws/room.go | 5 +++-- internal/ws/settler.go | 6 +++--- internal/ws/ws_test.go | 2 +- 8 files changed, 42 insertions(+), 24 deletions(-) diff --git a/cmd/server/settler.go b/cmd/server/settler.go index be39253..45946cb 100644 --- a/cmd/server/settler.go +++ b/cmd/server/settler.go @@ -73,13 +73,13 @@ func (g gameSettler) RecordGameResult(userID int64, won bool) { g.eco.RecordGameResult(context.Background(), userID, won) } -// PlayerProfile سکه و نشانِ رتبه‌ی بازیکن را برای نمایش سرِ میز برمی‌گرداند. -func (g gameSettler) PlayerProfile(userID int64) (int64, string) { +// PlayerProfile سکه، نشانِ رتبه و آواتارِ بازیکن را برای نمایش سرِ میز برمی‌گرداند. +func (g gameSettler) PlayerProfile(userID int64) (int64, string, string) { p, err := g.eco.GetProfile(context.Background(), userID) if err != nil || p == nil { - return 0, "" + return 0, "", "" } - return p.Coins, p.RankTier + return p.Coins, p.RankTier, p.Avatar } // ChargeTicket یک بلیت برای بُرِ مجدد مصرف می‌کند (خطا اگر بلیت کافی نباشد). diff --git a/deploy.sh b/deploy.sh index ee86ef9..8f69a99 100755 --- a/deploy.sh +++ b/deploy.sh @@ -11,18 +11,29 @@ cd "$(dirname "$0")" echo "==> build linux/amd64 (CGO off, pure-Go sqlite)" CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o /tmp/hakemsho-linux ./cmd/server +LOCAL_SHA=$(shasum -a 256 /tmp/hakemsho-linux | cut -d' ' -f1) +echo " sha256=$LOCAL_SHA" echo "==> upload to $HOST" scp -i "$KEY" -o StrictHostKeyChecking=no /tmp/hakemsho-linux "$HOST:$REMOTE_DIR/hakemsho.new" -echo "==> swap in + restart (zero-config; data & env untouched)" -ssh -i "$KEY" -o StrictHostKeyChecking=no "$HOST" ' +# چک‌سام را روی سرور تأیید کن و فقط در صورتِ سالم‌بودن باینری را جابه‌جا کن. +# این از سناریوی «انتقالِ ناقص ⇒ باینریِ خراب ⇒ SEGV» جلوگیری می‌کند. +echo "==> verify checksum + swap in + restart (data & env untouched)" +ssh -i "$KEY" -o StrictHostKeyChecking=no "$HOST" " set -e + REMOTE_SHA=\$(sha256sum ~/hakemsho/hakemsho.new | cut -d' ' -f1) + if [ \"\$REMOTE_SHA\" != \"$LOCAL_SHA\" ]; then + echo \"checksum mismatch (\$REMOTE_SHA != $LOCAL_SHA) — aborting, service untouched\" + rm -f ~/hakemsho/hakemsho.new + exit 1 + fi + chmod +x ~/hakemsho/hakemsho.new mv ~/hakemsho/hakemsho.new ~/hakemsho/hakemsho - chmod +x ~/hakemsho/hakemsho + sudo systemctl reset-failed hakemsho || true sudo systemctl restart hakemsho - sleep 2 + sleep 3 systemctl is-active hakemsho curl -s -m5 http://127.0.0.1:8080/health && echo -' +" echo "==> done: https://hakem.approagency.ir/health" diff --git a/internal/economy/economy.go b/internal/economy/economy.go index e02b5eb..6778671 100644 --- a/internal/economy/economy.go +++ b/internal/economy/economy.go @@ -60,21 +60,23 @@ type Profile struct { RankPoints int64 `json:"rank_points"` RankTier string `json:"rank_tier"` // bronze..king RankIndex int `json:"rank_index"` // ۰..۴ + Avatar string `json:"avatar"` // seed آواتار (برای نمایش سرِ میز) } // GetProfile وضعیت اقتصادی کاربر را می‌خواند. func (s *Service) GetProfile(ctx context.Context, userID int64) (*Profile, error) { var p Profile - var vipUntil sql.NullString + var vipUntil, avatar sql.NullString err := s.db.QueryRowContext(ctx, - `SELECT coins, tickets, xp, trophies, vip_until, selected_card, selected_carpet, rank_points FROM users WHERE id = ?`, userID). - Scan(&p.Coins, &p.Tickets, &p.XP, &p.Trophies, &vipUntil, &p.SelectedCard, &p.SelectedCarpet, &p.RankPoints) + `SELECT coins, tickets, xp, trophies, vip_until, selected_card, selected_carpet, rank_points, avatar FROM users WHERE id = ?`, userID). + Scan(&p.Coins, &p.Tickets, &p.XP, &p.Trophies, &vipUntil, &p.SelectedCard, &p.SelectedCarpet, &p.RankPoints, &avatar) if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } if err != nil { return nil, err } + p.Avatar = avatar.String p.Level, p.XPInto, p.XPNeed = LevelInfo(p.XP) p.RankTier, p.RankIndex = RankTier(p.RankPoints) if vipUntil.Valid { diff --git a/internal/ws/hub.go b/internal/ws/hub.go index 1248fd1..559ac1e 100644 --- a/internal/ws/hub.go +++ b/internal/ws/hub.go @@ -391,10 +391,12 @@ func (h *Hub) removeFromTable(t *pendingTable, c *Client) { func (h *Hub) broadcastLobby(t *pendingTable) { players := make([]lobbyPlayer, len(t.clients)) for i, cl := range t.clients { + _, _, avatar := h.settler.PlayerProfile(cl.UserID) players[i] = lobbyPlayer{ - Name: cl.Name, - Host: cl.UserID == t.host, - Seat: t.seatOf(i), + Name: cl.Name, + Avatar: avatar, + Host: cl.UserID == t.host, + Seat: t.seatOf(i), } } for i, cl := range t.clients { diff --git a/internal/ws/protocol.go b/internal/ws/protocol.go index 92a2f52..3311cb0 100644 --- a/internal/ws/protocol.go +++ b/internal/ws/protocol.go @@ -27,9 +27,10 @@ type chatMsg struct { // lobbyPlayer یک بازیکن در اتاق انتظارِ میز خصوصی. type lobbyPlayer struct { - Name string `json:"name"` - Host bool `json:"host"` - Seat int `json:"seat"` // جایگاهِ مطلق (۰..۳) + Name string `json:"name"` + Avatar string `json:"avatar"` // seed آواتارِ کاربر + Host bool `json:"host"` + Seat int `json:"seat"` // جایگاهِ مطلق (۰..۳) } // tableLobbyMsg وضعیت اتاق انتظارِ میز خصوصی (دورهمی). @@ -64,6 +65,7 @@ type PlayerInfo struct { Connected bool `json:"connected"` Coins int64 `json:"coins"` RankTier string `json:"rank_tier"` // bronze..king (برای نشانِ رتبه) + Avatar string `json:"avatar"` // seed آواتارِ کاربر } // matchedMsg به کلاینت پس از تشکیل میز. diff --git a/internal/ws/room.go b/internal/ws/room.go index 996ec77..90410aa 100644 --- a/internal/ws/room.go +++ b/internal/ws/room.go @@ -39,6 +39,7 @@ type seatInfo struct { left bool // خروج دائمی داوطلبانه (در تسویه از بازگشت ورودی محروم) coins int64 // برای نمایش سرِ میز rankTier string // نشانِ رتبه برای نمایش سرِ میز + avatar string // seed آواتارِ انتخابیِ کاربر (برای نمایش سرِ میز) } // auto مشخص می‌کند جایگاه باید خودکار بازی شود (بات یا قطع‌شده). @@ -143,7 +144,7 @@ func (r *Room) start() { if s.isBot || s.userID == 0 { continue } - s.coins, s.rankTier = r.settler.PlayerProfile(s.userID) + s.coins, s.rankTier, s.avatar = r.settler.PlayerProfile(s.userID) } for seat, s := range r.seats { if s.client != nil { @@ -567,7 +568,7 @@ func (r *Room) playersInfo() []PlayerInfo { out[i] = PlayerInfo{ Seat: i, Name: s.name, Bot: s.isBot, Connected: s.isBot || s.connected, - Coins: s.coins, RankTier: s.rankTier, + Coins: s.coins, RankTier: s.rankTier, Avatar: s.avatar, } } return out diff --git a/internal/ws/settler.go b/internal/ws/settler.go index b8ff087..521468e 100644 --- a/internal/ws/settler.go +++ b/internal/ws/settler.go @@ -12,8 +12,8 @@ type Settler interface { RecordHand(userID int64, won, kot, asHakem bool) // پایان یک هَند RecordCut(userID int64) // بُرِش با حکم RecordGameResult(userID int64, won bool) // پایان بازی (برد/باخت) - // PlayerProfile سکه و نشانِ رتبه‌ی بازیکن را برای نمایش سرِ میز برمی‌گرداند. - PlayerProfile(userID int64) (coins int64, rankTier string) + // PlayerProfile سکه، نشانِ رتبه و seedِ آواتارِ بازیکن را برای نمایش سرِ میز برمی‌گرداند. + PlayerProfile(userID int64) (coins int64, rankTier string, avatar string) // ChargeTicket یک بلیت برای بُرِ مجدد در فازِ انتخابِ حکم مصرف می‌کند. ChargeTicket(userID int64) error // ChargeCoins/AwardCoins برای شرطِ سکه‌ایِ میزِ خصوصی (pot). Charge در صورتِ @@ -38,7 +38,7 @@ func (noopSettler) RecordGame(string, string, int, bool) {} func (noopSettler) RecordHand(int64, bool, bool, bool) {} func (noopSettler) RecordCut(int64) {} func (noopSettler) RecordGameResult(int64, bool) {} -func (noopSettler) PlayerProfile(int64) (int64, string) { return 0, "" } +func (noopSettler) PlayerProfile(int64) (int64, string, string) { return 0, "", "" } func (noopSettler) ChargeTicket(int64) error { return nil } func (noopSettler) ChargeCoins(int64, int64, string) error { return nil } func (noopSettler) AwardCoins(int64, int64, string) {} diff --git a/internal/ws/ws_test.go b/internal/ws/ws_test.go index 5660d19..73cbaf1 100644 --- a/internal/ws/ws_test.go +++ b/internal/ws/ws_test.go @@ -481,7 +481,7 @@ func (s *recordingSettler) RecordGameResult(int64, bool) { s.mu.Unlock() } -func (s *recordingSettler) PlayerProfile(int64) (int64, string) { return 0, "" } +func (s *recordingSettler) PlayerProfile(int64) (int64, string, string) { return 0, "", "" } func (s *recordingSettler) ChargeTicket(int64) error { return nil } func (s *recordingSettler) ChargeCoins(int64, int64, string) error { return nil } func (s *recordingSettler) AwardCoins(int64, int64, string) {}