-
-
Notifications
You must be signed in to change notification settings - Fork 14
/
event_sanitizer.go
104 lines (93 loc) · 3.45 KB
/
event_sanitizer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package bus
import (
"context"
"errors"
"strconv"
"unicode/utf8"
"github.com/golang/protobuf/proto"
macondopb "github.com/domino14/macondo/gen/api/proto/macondo"
"github.com/domino14/liwords/pkg/entity"
"github.com/domino14/liwords/pkg/mod"
"github.com/domino14/liwords/pkg/user"
pb "github.com/domino14/liwords/rpc/api/proto/ipc"
)
// Events need to be sanitized so that we don't send user racks to people
// who shouldn't get them. Note that sanitize only runs for events that are
// sent DIRECTLY to a player (see AudUser), and not for AudGameTv for example.
func sanitize(us user.Store, evt *entity.EventWrapper, userID string) (*entity.EventWrapper, error) {
// Depending on the event type and even the state of the game, we return a
// sanitized event (or not).
switch evt.Type {
case pb.MessageType_GAME_HISTORY_REFRESHER:
// When sent to AudUser, we should sanitize ONLY if we are someone
// who is playing in the game. This is because observers can also
// receive these events directly (through AudUser).
subevt, ok := evt.Event.(*pb.GameHistoryRefresher)
if !ok {
return nil, errors.New("subevt-wrong-format")
}
// Possibly censors users
cloned := proto.Clone(subevt).(*pb.GameHistoryRefresher)
cloned.History = mod.CensorHistory(context.Background(), us, cloned.History)
if subevt.History.PlayState == macondopb.PlayState_GAME_OVER {
// no need to sanitize if the game is over.
return entity.WrapEvent(cloned, pb.MessageType_GAME_HISTORY_REFRESHER), nil
}
mynick := nicknameFromUserID(userID, subevt.History.Players)
if mynick == "" {
// No need to sanitize if we don't have a nickname IN THE GAME;
// this only happens if we are not playing the game.
return entity.WrapEvent(cloned, pb.MessageType_GAME_HISTORY_REFRESHER), nil
}
// Only sanitize if the nickname is not empty. The nickname is
// empty if they are not playing in this game.
for _, evt := range cloned.History.Events {
if evt.Nickname != mynick {
evt.Rack = ""
if evt.Type == macondopb.GameEvent_EXCHANGE {
evt.Exchanged = strconv.Itoa(utf8.RuneCountInString(evt.Exchanged))
}
}
}
if cloned.History.Players[0].UserId == userID {
cloned.History.LastKnownRacks[1] = ""
} else if cloned.History.Players[1].UserId == userID {
cloned.History.LastKnownRacks[0] = ""
}
return entity.WrapEvent(cloned, pb.MessageType_GAME_HISTORY_REFRESHER), nil
case pb.MessageType_SERVER_GAMEPLAY_EVENT:
// Server gameplay events
// When sent to AudUser, we need to sanitize them here. When sent to
// an AudGameTV, they are unsanitized, and handled elsewhere.
subevt, ok := evt.Event.(*pb.ServerGameplayEvent)
if !ok {
return nil, errors.New("subevt-wrong-format")
}
if subevt.Playing == macondopb.PlayState_GAME_OVER {
return evt, nil
}
if subevt.UserId == userID {
return evt, nil
}
// Otherwise clone it.
cloned := proto.Clone(subevt).(*pb.ServerGameplayEvent)
cloned.NewRack = ""
cloned.Event.Rack = ""
if cloned.Event.Type == macondopb.GameEvent_EXCHANGE {
cloned.Event.Exchanged = strconv.Itoa(utf8.RuneCountInString(cloned.Event.Exchanged))
}
return entity.WrapEvent(cloned, pb.MessageType_SERVER_GAMEPLAY_EVENT), nil
default:
return evt, nil
}
}
func nicknameFromUserID(userid string, playerinfo []*macondopb.PlayerInfo) string {
// given a user id, return the nickname of the given user.
nick := ""
for _, p := range playerinfo {
if p.UserId == userid {
return p.Nickname
}
}
return nick
}