Last active
August 29, 2015 14:14
-
-
Save tbillington/47e7969ca3234476991f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Observations: