fix(social): robot blocks
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m21s
CI / changes (pull_request) Successful in 3s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m21s
Blocking an auto-match opponent who is secretly a pooled robot is recorded instead in a separate `robot_blocks` table. Now blocking behaves the same in that game (struck name, hidden composer) and lists the blocked opponent under the name you saw, but is recorded only against that game — the disguise holds, the shared robot is never globally blocked, and the matchmaker keeps pairing you with robots (so you can never block yourself out of opponents). - the shared robot account is never put in `blocks` - the matchmaker keeps it free and it is not blocked under its other per-game names - the blocked list and the in-game card still show it by joining that table; an unblock deletes the row
This commit is contained in:
@@ -42,9 +42,20 @@ type RedeemResultResp struct {
|
||||
Friend AccountRefResp `json:"friend"`
|
||||
}
|
||||
|
||||
// BlockListResp is the accounts the caller has blocked.
|
||||
// BlockListResp is the accounts the caller has blocked, plus the per-game disguised-robot
|
||||
// blocks (not real accounts).
|
||||
type BlockListResp struct {
|
||||
Blocked []AccountRefResp `json:"blocked"`
|
||||
Robots []RobotBlockResp `json:"robots"`
|
||||
}
|
||||
|
||||
// RobotBlockResp is one per-game disguised-robot block: the row id (to unblock it), the game
|
||||
// name the player saw, and the game + seat it was blocked in.
|
||||
type RobotBlockResp struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
GameID string `json:"game_id"`
|
||||
Seat int `json:"seat"`
|
||||
}
|
||||
|
||||
// StatsResp is a durable account's lifetime statistics. BestMoves breaks the best move
|
||||
@@ -188,10 +199,11 @@ func (c *Client) RedeemFriendCode(ctx context.Context, userID, code string) (Red
|
||||
|
||||
// --- blocks ---
|
||||
|
||||
// Block blocks an account.
|
||||
func (c *Client) Block(ctx context.Context, userID, targetID string) error {
|
||||
// Block blocks an account. A non-empty gameID marks an in-game block, so a disguised-robot
|
||||
// opponent is recorded as a per-game block; it is empty for a settings-screen block.
|
||||
func (c *Client) Block(ctx context.Context, userID, targetID, gameID string) error {
|
||||
return c.do(ctx, http.MethodPost, "/api/v1/user/blocks", userID, "",
|
||||
map[string]string{"account_id": targetID}, nil)
|
||||
map[string]string{"account_id": targetID, "game_id": gameID}, nil)
|
||||
}
|
||||
|
||||
// Unblock removes a block.
|
||||
|
||||
@@ -60,12 +60,35 @@ func encodeOutgoingList(r backendclient.OutgoingListResp) []byte {
|
||||
return b.FinishedBytes()
|
||||
}
|
||||
|
||||
// buildRobotBlockVector builds the BlockList robots vector (per-game disguised-robot blocks).
|
||||
func buildRobotBlockVector(b *flatbuffers.Builder, robots []backendclient.RobotBlockResp) flatbuffers.UOffsetT {
|
||||
offs := make([]flatbuffers.UOffsetT, len(robots))
|
||||
for i, r := range robots {
|
||||
id := b.CreateString(r.ID)
|
||||
name := b.CreateString(r.DisplayName)
|
||||
gid := b.CreateString(r.GameID)
|
||||
fb.RobotBlockRefStart(b)
|
||||
fb.RobotBlockRefAddId(b, id)
|
||||
fb.RobotBlockRefAddDisplayName(b, name)
|
||||
fb.RobotBlockRefAddGameId(b, gid)
|
||||
fb.RobotBlockRefAddSeat(b, int32(r.Seat))
|
||||
offs[i] = fb.RobotBlockRefEnd(b)
|
||||
}
|
||||
fb.BlockListStartRobotsVector(b, len(offs))
|
||||
for i := len(offs) - 1; i >= 0; i-- {
|
||||
b.PrependUOffsetT(offs[i])
|
||||
}
|
||||
return b.EndVector(len(offs))
|
||||
}
|
||||
|
||||
// encodeBlockList builds a BlockList payload.
|
||||
func encodeBlockList(r backendclient.BlockListResp) []byte {
|
||||
b := flatbuffers.NewBuilder(256)
|
||||
v := buildAccountRefVector(b, r.Blocked, fb.BlockListStartBlockedVector)
|
||||
rv := buildRobotBlockVector(b, r.Robots)
|
||||
fb.BlockListStart(b)
|
||||
fb.BlockListAddBlocked(b, v)
|
||||
fb.BlockListAddRobots(b, rv)
|
||||
b.Finish(fb.BlockListEnd(b))
|
||||
return b.FinishedBytes()
|
||||
}
|
||||
|
||||
@@ -166,7 +166,9 @@ func blocksListHandler(backend *backendclient.Client) Handler {
|
||||
func blockAddHandler(backend *backendclient.Client) Handler {
|
||||
return func(ctx context.Context, req Request) ([]byte, error) {
|
||||
in := fb.GetRootAsTargetRequest(req.Payload, 0)
|
||||
if err := backend.Block(ctx, req.UserID, string(in.AccountId())); err != nil {
|
||||
// game_id is set only by an in-game block, so a disguised-robot opponent is recorded
|
||||
// per-game; it is empty for a settings-screen block.
|
||||
if err := backend.Block(ctx, req.UserID, string(in.AccountId()), string(in.GameId())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encodeAck(true), nil
|
||||
|
||||
Reference in New Issue
Block a user