Skip to content

Instantly share code, notes, and snippets.

@tbillington
Last active August 29, 2015 14:14
Show Gist options
  • Save tbillington/47e7969ca3234476991f to your computer and use it in GitHub Desktop.
Save tbillington/47e7969ca3234476991f to your computer and use it in GitHub Desktop.
package main
import (
"encoding/json"
"fmt"
"golang.org/x/net/websocket"
"net/http"
"strings"
"sync"
"time"
)
type player struct {
name string
id int
ch chan interface{}
}
type idAssignment struct {
id int
}
type playerDisconnect struct {
id int
}
type position2d struct {
x int
y int
}
type playerMovement struct {
id int
pos position2d
}
func getNewPlayerId(players map[int]player) int {
for i := 0; i < 5; i += 1 {
if _, ok := players[i]; ok == false {
return i
}
}
panic("dun goofed")
return -1
}
func server(incoming chan interface{}) {
fmt.Printf("Server started at %s\n", time.Now())
var players = make(map[int]player)
for {
var msg interface{}
select {
case msg = <-incoming:
switch msg.(type) {
case player:
newPlayer := msg.(player)
newPlayer.id = getNewPlayerId(players)
players[newPlayer.id] = newPlayer
fmt.Println("Server got a new player!", newPlayer)
newPlayer.ch <- idAssignment{id: newPlayer.id}
fmt.Println("Total number of players now:", len(players))
case playerDisconnect:
id := msg.(playerDisconnect).id
fmt.Println("Player:", players[id].name, "id:", id, "has disconnected")
delete(players, id)
fmt.Println("Total number of players now:", len(players))
case playerMovement:
outgoingMessage := msg.(playerMovement)
fmt.Println("Received movement message", outgoingMessage)
for _, p := range players {
p.ch <- outgoingMessage
}
default:
fmt.Println("Unindentified type", msg)
}
}
}
}
// Return a handler which will* take a channel to listen on to send messages back to the client
func handlerGen(incoming chan interface{}) func(*websocket.Conn) {
return func(ws *websocket.Conn) {
fmt.Printf("New connection from %s %s\n", ws.RemoteAddr().Network(), ws.RemoteAddr().String())
// Make a buffer to store the websocket messages
var buffer = make([]byte, 1024)
ws.Read([]byte(buffer))
newPlayerJson := []byte(strings.Trim(string(buffer), "\x00"))
var input map[string]interface{}
if err := json.Unmarshal(newPlayerJson, &input); err != nil {
panic(err)
}
var thisPlayer player
for k, v := range input {
switch k {
case "newplayer":
thisPlayer = player{
name: v.(map[string]interface{})["name"].(string),
id: 0, // Server will assign an Id
ch: make(chan interface{}),
}
}
}
incoming <- thisPlayer
var socketChan = make(chan string)
var socketErrorChan = make(chan error)
go func() {
for {
buffer = make([]byte, 1024)
_, err := ws.Read([]byte(buffer))
if err != nil {
socketErrorChan <- err
}
socketChan <- strings.Trim(string(buffer), "\x00")
}
}()
var fromServer interface{}
fromSocket := ""
var fromSocketError error
for {
select {
case fromServer = <-thisPlayer.ch:
switch fromServer.(type) {
case idAssignment:
thisPlayer.id = fromServer.(idAssignment).id
default:
fmt.Println("Got unidentified message from server", fromServer)
}
case fromSocket = <-socketChan:
input = map[string]interface{}{}
if err := json.Unmarshal([]byte(fromSocket), &input); err != nil {
panic(err)
}
for k, v := range input {
switch k {
case "movement":
incoming <- playerMovement{
id: thisPlayer.id,
pos: position2d{
x: int(v.(map[string]interface{})["x"].(float64)),
y: int(v.(map[string]interface{})["y"].(float64)),
},
}
default:
fmt.Printf("Unidentified message from socket: %s\n", fromSocket)
}
}
case fromSocketError = <-socketErrorChan:
switch fromSocketError.Error() {
case "EOF":
// Player has quit/disconnected
incoming <- playerDisconnect{id: thisPlayer.id}
default:
// Some other socket error
fmt.Println("Socket error:", fromSocketError, "for player", thisPlayer)
}
return
}
}
}
}
func main() {
var incoming = make(chan interface{})
go server(incoming)
http.Handle("/echo", websocket.Handler(handlerGen(incoming)))
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
func mapValues(m map[string]chan string) []chan string {
// Make array of the map values
var values = make([]chan string, 0)
for _, value := range m {
values = append(values, value)
}
return values
}
func mergeChannels(messageChans []chan string) chan string {
out := make(chan string)
wg := &sync.WaitGroup{}
wg.Add(len(messageChans))
for _, ch := range messageChans {
go func(ch <-chan string) {
for err := range ch {
out <- err
}
wg.Done()
}(ch)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
@dhowden
Copy link

dhowden commented Jan 26, 2015

Observations:

  1. In the type switch you don't need to cast anything - the types will be already done for you.
  2. You can do fmt.Printf("Unsupported type: %T", msg) and it will print the type too!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment