mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Add tower strength display and remove lights.
This commit is contained in:
@@ -114,10 +114,10 @@ func AllianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
data = struct {
|
data = struct {
|
||||||
RedScore int
|
RedScoreFields *RealtimeScoreFields
|
||||||
BlueScore int
|
BlueScoreFields *RealtimeScoreFields
|
||||||
}{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls),
|
}{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls),
|
||||||
mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)}
|
mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)}
|
||||||
err = websocket.Write("realtimeScore", data)
|
err = websocket.Write("realtimeScore", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Websocket error: %s", err)
|
log.Printf("Websocket error: %s", err)
|
||||||
@@ -175,10 +175,10 @@ func AllianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
messageType = "realtimeScore"
|
messageType = "realtimeScore"
|
||||||
message = struct {
|
message = struct {
|
||||||
RedScore int
|
RedScoreFields *RealtimeScoreFields
|
||||||
BlueScore int
|
BlueScoreFields *RealtimeScoreFields
|
||||||
}{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls),
|
}{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls),
|
||||||
mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)}
|
mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)}
|
||||||
case _, ok := <-reloadDisplaysListener:
|
case _, ok := <-reloadDisplaysListener:
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
|||||||
1
arena.go
1
arena.go
@@ -548,7 +548,6 @@ func (arena *Arena) handleLighting() {
|
|||||||
case ENDGAME_PERIOD:
|
case ENDGAME_PERIOD:
|
||||||
redScoreFields := arena.redRealtimeScore.ScoreFields(arena.blueRealtimeScore.CurrentScore.Fouls)
|
redScoreFields := arena.redRealtimeScore.ScoreFields(arena.blueRealtimeScore.CurrentScore.Fouls)
|
||||||
blueScoreFields := arena.blueRealtimeScore.ScoreFields(arena.redRealtimeScore.CurrentScore.Fouls)
|
blueScoreFields := arena.blueRealtimeScore.ScoreFields(arena.redRealtimeScore.CurrentScore.Fouls)
|
||||||
arena.lights.SetGoals(redScoreFields.TowerStrength, blueScoreFields.TowerStrength)
|
|
||||||
arena.lights.SetDefenses(redScoreFields.DefensesStrength, blueScoreFields.DefensesStrength)
|
arena.lights.SetDefenses(redScoreFields.DefensesStrength, blueScoreFields.DefensesStrength)
|
||||||
case POST_MATCH:
|
case POST_MATCH:
|
||||||
if mainArena.fieldReset {
|
if mainArena.fieldReset {
|
||||||
|
|||||||
59
lights.go
59
lights.go
@@ -12,9 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RED_GOAL = "redGoal"
|
|
||||||
RED_DEFENSE = "redDefense"
|
RED_DEFENSE = "redDefense"
|
||||||
BLUE_GOAL = "blueGoal"
|
|
||||||
BLUE_DEFENSE = "blueDefense"
|
BLUE_DEFENSE = "blueDefense"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,14 +83,10 @@ func (lights *Lights) Setup() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lights.packets = make(map[string]*LightPacket)
|
lights.packets = make(map[string]*LightPacket)
|
||||||
lights.packets[RED_GOAL] = &LightPacket{}
|
|
||||||
lights.packets[RED_DEFENSE] = &LightPacket{}
|
lights.packets[RED_DEFENSE] = &LightPacket{}
|
||||||
lights.packets[BLUE_GOAL] = &LightPacket{}
|
|
||||||
lights.packets[BLUE_DEFENSE] = &LightPacket{}
|
lights.packets[BLUE_DEFENSE] = &LightPacket{}
|
||||||
lights.oldPackets = make(map[string]*LightPacket)
|
lights.oldPackets = make(map[string]*LightPacket)
|
||||||
lights.oldPackets[RED_GOAL] = &LightPacket{}
|
|
||||||
lights.oldPackets[RED_DEFENSE] = &LightPacket{}
|
lights.oldPackets[RED_DEFENSE] = &LightPacket{}
|
||||||
lights.oldPackets[BLUE_GOAL] = &LightPacket{}
|
|
||||||
lights.oldPackets[BLUE_DEFENSE] = &LightPacket{}
|
lights.oldPackets[BLUE_DEFENSE] = &LightPacket{}
|
||||||
|
|
||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
@@ -109,15 +103,9 @@ func (lights *Lights) Setup() error {
|
|||||||
|
|
||||||
func (lights *Lights) SetupConnections() error {
|
func (lights *Lights) SetupConnections() error {
|
||||||
lights.connections = make(map[string]*net.Conn)
|
lights.connections = make(map[string]*net.Conn)
|
||||||
if err := lights.connect(RED_GOAL, eventSettings.RedGoalLightsAddress); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := lights.connect(RED_DEFENSE, eventSettings.RedDefenseLightsAddress); err != nil {
|
if err := lights.connect(RED_DEFENSE, eventSettings.RedDefenseLightsAddress); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := lights.connect(BLUE_GOAL, eventSettings.BlueGoalLightsAddress); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := lights.connect(BLUE_DEFENSE, eventSettings.BlueDefenseLightsAddress); err != nil {
|
if err := lights.connect(BLUE_DEFENSE, eventSettings.BlueDefenseLightsAddress); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -140,18 +128,14 @@ func (lights *Lights) connect(controller, address string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lights *Lights) ClearAll() {
|
func (lights *Lights) ClearAll() {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("off", 10)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("off", 10)
|
lights.packets[RED_DEFENSE].setAllColorFade("off", 10)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("off", 10)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("off", 10)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("off", 10)
|
||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns all lights green to signal that the field is safe to enter.
|
// Turns all lights green to signal that the field is safe to enter.
|
||||||
func (lights *Lights) SetFieldReset() {
|
func (lights *Lights) SetFieldReset() {
|
||||||
lights.packets[RED_GOAL].setAllColor("green")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("green")
|
lights.packets[RED_DEFENSE].setAllColor("green")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("green")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("green")
|
lights.packets[BLUE_DEFENSE].setAllColor("green")
|
||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
}
|
}
|
||||||
@@ -163,29 +147,19 @@ func (lights *Lights) SetMode(mode string) {
|
|||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case "off":
|
case "off":
|
||||||
lights.packets[RED_GOAL].setAllColor("off")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("off")
|
lights.packets[RED_DEFENSE].setAllColor("off")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("off")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("off")
|
lights.packets[BLUE_DEFENSE].setAllColor("off")
|
||||||
case "all_white":
|
case "all_white":
|
||||||
lights.packets[RED_GOAL].setAllColor("white")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("white")
|
lights.packets[RED_DEFENSE].setAllColor("white")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("white")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("white")
|
lights.packets[BLUE_DEFENSE].setAllColor("white")
|
||||||
case "all_red":
|
case "all_red":
|
||||||
lights.packets[RED_GOAL].setAllColor("red")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("red")
|
lights.packets[RED_DEFENSE].setAllColor("red")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("red")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("red")
|
lights.packets[BLUE_DEFENSE].setAllColor("red")
|
||||||
case "all_green":
|
case "all_green":
|
||||||
lights.packets[RED_GOAL].setAllColor("green")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("green")
|
lights.packets[RED_DEFENSE].setAllColor("green")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("green")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("green")
|
lights.packets[BLUE_DEFENSE].setAllColor("green")
|
||||||
case "all_blue":
|
case "all_blue":
|
||||||
lights.packets[RED_GOAL].setAllColor("blue")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("blue")
|
lights.packets[RED_DEFENSE].setAllColor("blue")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("blue")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("blue")
|
lights.packets[BLUE_DEFENSE].setAllColor("blue")
|
||||||
}
|
}
|
||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
@@ -215,14 +189,10 @@ func (lights *Lights) animate() {
|
|||||||
case "strobe":
|
case "strobe":
|
||||||
switch lights.animationCount {
|
switch lights.animationCount {
|
||||||
case 1:
|
case 1:
|
||||||
lights.packets[RED_GOAL].setAllColor("white")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("white")
|
lights.packets[RED_DEFENSE].setAllColor("white")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("off")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("off")
|
lights.packets[BLUE_DEFENSE].setAllColor("off")
|
||||||
case 2:
|
case 2:
|
||||||
lights.packets[RED_GOAL].setAllColor("off")
|
|
||||||
lights.packets[RED_DEFENSE].setAllColor("off")
|
lights.packets[RED_DEFENSE].setAllColor("off")
|
||||||
lights.packets[BLUE_GOAL].setAllColor("white")
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColor("white")
|
lights.packets[BLUE_DEFENSE].setAllColor("white")
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
@@ -231,14 +201,10 @@ func (lights *Lights) animate() {
|
|||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
case "fade_red":
|
case "fade_red":
|
||||||
if lights.animationCount == 1 {
|
if lights.animationCount == 1 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("red", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("red", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("red", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("red", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("red", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("red", 18)
|
||||||
} else if lights.animationCount == 61 {
|
} else if lights.animationCount == 61 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("darkred", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("darkred", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("darkred", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("darkred", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("darkred", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("darkred", 18)
|
||||||
} else if lights.animationCount > 120 {
|
} else if lights.animationCount > 120 {
|
||||||
lights.animationCount = 0
|
lights.animationCount = 0
|
||||||
@@ -246,14 +212,10 @@ func (lights *Lights) animate() {
|
|||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
case "fade_blue":
|
case "fade_blue":
|
||||||
if lights.animationCount == 1 {
|
if lights.animationCount == 1 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("blue", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("blue", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("blue", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("blue", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("blue", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("blue", 18)
|
||||||
} else if lights.animationCount == 61 {
|
} else if lights.animationCount == 61 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("darkblue", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("darkblue", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("darkblue", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("darkblue", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("darkblue", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("darkblue", 18)
|
||||||
} else if lights.animationCount > 120 {
|
} else if lights.animationCount > 120 {
|
||||||
lights.animationCount = 0
|
lights.animationCount = 0
|
||||||
@@ -261,14 +223,10 @@ func (lights *Lights) animate() {
|
|||||||
lights.sendLights()
|
lights.sendLights()
|
||||||
case "fade_red_blue":
|
case "fade_red_blue":
|
||||||
if lights.animationCount == 1 {
|
if lights.animationCount == 1 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("blue", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("blue", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("blue", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("darkred", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("darkred", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("darkred", 18)
|
||||||
} else if lights.animationCount == 61 {
|
} else if lights.animationCount == 61 {
|
||||||
lights.packets[RED_GOAL].setAllColorFade("darkblue", 18)
|
|
||||||
lights.packets[RED_DEFENSE].setAllColorFade("darkblue", 18)
|
lights.packets[RED_DEFENSE].setAllColorFade("darkblue", 18)
|
||||||
lights.packets[BLUE_GOAL].setAllColorFade("red", 18)
|
|
||||||
lights.packets[BLUE_DEFENSE].setAllColorFade("red", 18)
|
lights.packets[BLUE_DEFENSE].setAllColorFade("red", 18)
|
||||||
} else if lights.animationCount > 120 {
|
} else if lights.animationCount > 120 {
|
||||||
lights.animationCount = 0
|
lights.animationCount = 0
|
||||||
@@ -277,23 +235,6 @@ func (lights *Lights) animate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns on a number of channels corresponding to the tower strength (all on for 8 or higher).
|
|
||||||
func (lights *Lights) SetGoals(redTowerStrength, blueTowerStrength int) {
|
|
||||||
for i := 0; i < 8; i++ {
|
|
||||||
if redTowerStrength > i {
|
|
||||||
lights.packets[RED_GOAL].setColorFade(i, "red", 10)
|
|
||||||
} else {
|
|
||||||
lights.packets[RED_GOAL].setColorFade(i, "off", 10)
|
|
||||||
}
|
|
||||||
if blueTowerStrength > i {
|
|
||||||
lights.packets[BLUE_GOAL].setColorFade(i, "blue", 10)
|
|
||||||
} else {
|
|
||||||
lights.packets[BLUE_GOAL].setColorFade(i, "off", 10)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lights.sendLights()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turns on the lights below the defenses, with one channel per defense.
|
// Turns on the lights below the defenses, with one channel per defense.
|
||||||
func (lights *Lights) SetDefenses(redDefensesStrength, blueDefensesStrength [5]int) {
|
func (lights *Lights) SetDefenses(redDefensesStrength, blueDefensesStrength [5]int) {
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
|
|||||||
@@ -100,7 +100,13 @@ body[data-mode=fieldReset] .mode#fieldReset {
|
|||||||
#match[data-state=POST_MATCH] #inMatch {
|
#match[data-state=POST_MATCH] #inMatch {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
body[data-position=redTower] #match[data-state=PRE_MATCH] #inMatch,
|
||||||
|
body[data-position=blueTower] #match[data-state=PRE_MATCH] #inMatch {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body[data-position=redTower] #match, body[data-position=blueTower] #match {
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
/* In Match */
|
/* In Match */
|
||||||
#inMatch .datapoint {
|
#inMatch .datapoint {
|
||||||
@@ -127,6 +133,14 @@ body[data-position=right] #inMatch #blueScore {
|
|||||||
display: block;
|
display: block;
|
||||||
color: #07f;
|
color: #07f;
|
||||||
}
|
}
|
||||||
|
body[data-position=redTower] #inMatch #redTower {
|
||||||
|
display: block;
|
||||||
|
color: #f43;
|
||||||
|
}
|
||||||
|
body[data-position=blueTower] #inMatch #blueTower {
|
||||||
|
display: block;
|
||||||
|
color: #07f;
|
||||||
|
}
|
||||||
|
|
||||||
/* Pre Match */
|
/* Pre Match */
|
||||||
#preMatch #teamNumber {
|
#preMatch #teamNumber {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ var handleSetAllianceStationDisplay = function(targetScreen) {
|
|||||||
targetScreen = "blank";
|
targetScreen = "blank";
|
||||||
}
|
}
|
||||||
$("body").attr("data-mode", targetScreen);
|
$("body").attr("data-mode", targetScreen);
|
||||||
switch(allianceStation[1]){
|
switch (allianceStation[1]) {
|
||||||
case "1":
|
case "1":
|
||||||
$("body").attr("data-position", "right");
|
$("body").attr("data-position", "right");
|
||||||
break;
|
break;
|
||||||
@@ -26,6 +26,13 @@ var handleSetAllianceStationDisplay = function(targetScreen) {
|
|||||||
case "3":
|
case "3":
|
||||||
$("body").attr("data-position", "left");
|
$("body").attr("data-position", "left");
|
||||||
break;
|
break;
|
||||||
|
case "T":
|
||||||
|
if (allianceStation[0] == "R") {
|
||||||
|
$("body").attr("data-position", "redTower");
|
||||||
|
} else {
|
||||||
|
$("body").attr("data-position", "blueTower");
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -106,8 +113,10 @@ var handleMatchTime = function(data) {
|
|||||||
|
|
||||||
// Handles a websocket message to update the match score.
|
// Handles a websocket message to update the match score.
|
||||||
var handleRealtimeScore = function(data) {
|
var handleRealtimeScore = function(data) {
|
||||||
$("#redScore").text(data.RedScore);
|
$("#redScore").text(data.RedScoreFields.Score);
|
||||||
$("#blueScore").text(data.BlueScore);
|
$("#blueScore").text(data.BlueScoreFields.Score);
|
||||||
|
$("#redTower").text(data.RedScoreFields.TowerStrength);
|
||||||
|
$("#blueTower").text(data.BlueScoreFields.TowerStrength);
|
||||||
};
|
};
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|||||||
@@ -27,6 +27,8 @@
|
|||||||
<div id="redScore" class="datapoint"></div>
|
<div id="redScore" class="datapoint"></div>
|
||||||
<div id="blueScore" class="datapoint"></div>
|
<div id="blueScore" class="datapoint"></div>
|
||||||
<div id="timeRemaining" class="datapoint"></div>
|
<div id="timeRemaining" class="datapoint"></div>
|
||||||
|
<div id="redTower" class="datapoint"></div>
|
||||||
|
<div id="blueTower" class="datapoint"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="logo" class="mode">
|
<div id="logo" class="mode">
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
<option value="B1"{{if eq $station "B1"}} selected{{end}}>Blue 1</option>
|
<option value="B1"{{if eq $station "B1"}} selected{{end}}>Blue 1</option>
|
||||||
<option value="B2"{{if eq $station "B2"}} selected{{end}}>Blue 2</option>
|
<option value="B2"{{if eq $station "B2"}} selected{{end}}>Blue 2</option>
|
||||||
<option value="B3"{{if eq $station "B3"}} selected{{end}}>Blue 3</option>
|
<option value="B3"{{if eq $station "B3"}} selected{{end}}>Blue 3</option>
|
||||||
|
<option value="RT"{{if eq $station "RT"}} selected{{end}}>Red Tower</option>
|
||||||
|
<option value="BT"{{if eq $station "BT"}} selected{{end}}>Blue Tower</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user