mirror of https://github.com/gabehf/massflip.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.7 KiB
199 lines
5.7 KiB
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// Chat Hub code adapted from https://github.com/gorilla/websocket/tree/master/examples/chat
|
|
|
|
// Hub maintains the set of active clients and broadcasts messages to the
|
|
// clients.
|
|
type Hub struct {
|
|
// Registered clients.
|
|
clients map[*Client]bool
|
|
|
|
// Inbound messages from the clients.
|
|
broadcast chan []byte
|
|
|
|
// Register requests from the clients.
|
|
register chan *Client
|
|
|
|
// Unregister requests from clients.
|
|
unregister chan *Client
|
|
|
|
// Betting Hub Data
|
|
headsPool int
|
|
headsBetters map[string]int
|
|
tailsPool int
|
|
tailsBetters map[string]int
|
|
allUsers map[string]string
|
|
largestBet int
|
|
}
|
|
|
|
type WsMessage struct {
|
|
Type string `json:"type"`
|
|
Username string `json:"username,omitempty"`
|
|
}
|
|
|
|
type WsBetMessage struct {
|
|
Type string `json:"type"`
|
|
Username string `json:"username"`
|
|
Bet string `json:"bet"`
|
|
Amount int `json:"amount"`
|
|
}
|
|
|
|
func newHub() *Hub {
|
|
return &Hub{
|
|
broadcast: make(chan []byte),
|
|
register: make(chan *Client),
|
|
unregister: make(chan *Client),
|
|
clients: make(map[*Client]bool),
|
|
headsBetters: make(map[string]int),
|
|
tailsBetters: make(map[string]int),
|
|
allUsers: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
func (h *Hub) broadcastPoolUpdate() {
|
|
var msg string
|
|
heads := strconv.Itoa(len(h.headsBetters))
|
|
tails := strconv.Itoa(len(h.tailsBetters))
|
|
headspool := strconv.Itoa(h.headsPool)
|
|
tailspool := strconv.Itoa(h.tailsPool)
|
|
msg = "{\"type\":\"pool\",\"heads\":" + heads + ",\"tails\":" + tails + ",\"headspool\":" + headspool + ",\"tailspool\":" + tailspool + "}"
|
|
|
|
h.broadcast <- []byte(msg)
|
|
}
|
|
|
|
func (h *Hub) tick(s int) {
|
|
var msg string
|
|
heads := strconv.Itoa(len(h.headsBetters))
|
|
tails := strconv.Itoa(len(h.tailsBetters))
|
|
headspool := strconv.Itoa(h.headsPool)
|
|
tailspool := strconv.Itoa(h.tailsPool)
|
|
msg = "{\"type\":\"tick\",\"clock\":" + strconv.Itoa(s) + ",\"heads\":" + heads + ",\"tails\":" + tails + ",\"headspool\":" + headspool + ",\"tailspool\":" + tailspool + "}"
|
|
|
|
h.broadcast <- []byte(msg)
|
|
}
|
|
|
|
func (h *Hub) processMessage(message []byte) error {
|
|
var msg WsMessage
|
|
err := json.Unmarshal(message, &msg)
|
|
if err != nil {
|
|
// need error handling
|
|
fmt.Println("* Error unmarshalling WebSocket message")
|
|
log.Println(string(message))
|
|
}
|
|
if msg.Type == "bet" {
|
|
var betMsg WsBetMessage
|
|
err := json.Unmarshal(message, &betMsg)
|
|
if err != nil {
|
|
fmt.Println("* Error unmarshalling WebSocket bet message")
|
|
}
|
|
if h.allUsers[betMsg.Username] != "" {
|
|
log.Println("* Disallowed bet from user " + betMsg.Username + ": You can't bet twice")
|
|
return fmt.Errorf("cant bet twice")
|
|
}
|
|
if betMsg.Amount > DBGetUserByUsername(betMsg.Username).Points || betMsg.Amount <= 0 {
|
|
log.Println("* Disallowed bet from user " + betMsg.Username + ": Cannot bet more gp than you have or bet 0")
|
|
return fmt.Errorf("cant bet more gp than you have")
|
|
}
|
|
err = DBSubtractPoints(betMsg.Username, betMsg.Amount)
|
|
if err != nil {
|
|
log.Println("* Error subtracting points from user " + betMsg.Username + " in DB")
|
|
}
|
|
if betMsg.Bet == "heads" {
|
|
h.allUsers[betMsg.Username] = "heads"
|
|
h.headsBetters[betMsg.Username] = betMsg.Amount
|
|
h.headsPool += betMsg.Amount
|
|
fmt.Println("* " + betMsg.Username + " has bet " + strconv.Itoa(h.headsBetters[betMsg.Username]) + " on heads")
|
|
} else if betMsg.Bet == "tails" {
|
|
h.allUsers[betMsg.Username] = "tails"
|
|
h.tailsBetters[betMsg.Username] = betMsg.Amount
|
|
h.tailsPool += betMsg.Amount
|
|
fmt.Println("* " + betMsg.Username + " has bet " + strconv.Itoa(h.tailsBetters[betMsg.Username]) + " on tails")
|
|
}
|
|
h.broadcastPoolUpdate()
|
|
} else if msg.Type == "flip" {
|
|
|
|
for client := range h.clients {
|
|
select {
|
|
case client.send <- message:
|
|
default:
|
|
close(client.send)
|
|
delete(h.clients, client)
|
|
}
|
|
}
|
|
|
|
// this 50ms delay prevents the WS messages from colliding when they reach
|
|
// the client and causing a json parsing error
|
|
time.Sleep(50 * time.Millisecond)
|
|
|
|
var v struct {
|
|
Type string `json:"type"`
|
|
Value string `json:"value"`
|
|
}
|
|
err := json.Unmarshal(message, &v)
|
|
if err != nil {
|
|
fmt.Println("* Error unmarshalling WebSocket bet message")
|
|
}
|
|
|
|
if h.headsPool+h.tailsPool == 0 {
|
|
//fmt.Println("* No betters in pool, skipping payout")
|
|
return fmt.Errorf("no betters in pool")
|
|
}
|
|
for c := range h.clients {
|
|
if h.headsBetters[c.username] > 0 && v.Value == "heads" {
|
|
winAmt := int(float64(h.headsBetters[c.username])/float64(h.headsPool)*float64(h.tailsPool)) + h.headsBetters[c.username]
|
|
m := "{\"type\":\"win\",\"value\":" + strconv.Itoa(winAmt) + "}"
|
|
c.send <- []byte(m)
|
|
DBAddPoints(c.username, winAmt)
|
|
fmt.Println("* " + strconv.Itoa(winAmt) + " paid out to " + c.username)
|
|
} else if h.tailsBetters[c.username] > 0 && v.Value == "tails" {
|
|
winAmt := int(float64(h.tailsBetters[c.username])/float64(h.tailsPool)*float64(h.headsPool)) + h.tailsBetters[c.username]
|
|
m := "{\"type\":\"win\",\"value\":" + strconv.Itoa(winAmt) + "}"
|
|
c.send <- []byte(m)
|
|
DBAddPoints(c.username, winAmt)
|
|
fmt.Println("* " + strconv.Itoa(winAmt) + " paid out to " + c.username)
|
|
}
|
|
}
|
|
h.headsBetters = make(map[string]int)
|
|
h.tailsBetters = make(map[string]int)
|
|
h.allUsers = make(map[string]string)
|
|
h.headsPool = 0
|
|
h.tailsPool = 0
|
|
h.largestBet = 0
|
|
// reset pool for clients
|
|
} else {
|
|
for client := range h.clients {
|
|
select {
|
|
case client.send <- message:
|
|
default:
|
|
close(client.send)
|
|
delete(h.clients, client)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *Hub) run() {
|
|
for {
|
|
select {
|
|
case client := <-h.register:
|
|
h.clients[client] = true
|
|
case client := <-h.unregister:
|
|
if _, ok := h.clients[client]; ok {
|
|
delete(h.clients, client)
|
|
close(client.send)
|
|
}
|
|
case message := <-h.broadcast:
|
|
go h.processMessage(message)
|
|
}
|
|
}
|
|
}
|