Skip to content

Instantly share code, notes, and snippets.

@wookiecooking
Forked from acsellers/server.go
Created October 27, 2013 08:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wookiecooking/7179232 to your computer and use it in GitHub Desktop.
Save wookiecooking/7179232 to your computer and use it in GitHub Desktop.
package paintserver
import (
"net/http"
"code.google.com/p/go.net/websocket"
"image/color"
"image"
"image/png"
)
const (
TILE_WIDTH = 7
TILE_HEIGHT = 4
)
var (
ImageTiles = make([]*image.RGBA, TILE_WIDTH * TILE_HEIGHT)
RegisterChan = make(chan Connection)
DrawChan = make(chan DrawCommand)
Users map[int]User
Tokens map[string]User
)
type Connection struct {
User int
Out chan string
Done chan bool
}
type DrawCommand struct {
Tile int
X,Y uint8
User int
}
func (dc DrawCommand) String() {
return fmt.Sprintf("Pixel:%d:%d-%d:%d", dc.Tile, dc.X, dc.Y, dc.User)
}
type User struct {
ID int
Name string
PixelCount int
Color *color.RGBA
}
main() {
rect := image.Rect(0,0,256,256)
for i, _ := range ImageTiles {
ImageTiles[i] = image.NewRGBA(rect)
}
go BuildDrawStack()
http.Handle("/commands", websocket.Handler(DrawInput))
http.HandleFunc("/tile", ServeTile)
http.Handler("/assets/", http.FileServer(http.Dir("/assets")))
http.Register("/register", RegisterHandler)
http.HandleFunc("/", IndexHandler)
http.ListenAndServe(":80",nil)
}
func BuildDrawStack() {
tileChans := make([]chan DrawCommand, 28)
for i:= range ImageTiles {
tileChans[i] = make(chan DrawCommand)
go TileWatcher(i, tileChans[i])
}
go DrawDispatcher(tileChans)
}
func TileWatcher(tile int, input chan DrawCommand) {
myImage := ImageTiles[tile]
for {
command := <-input
if commandUser, ok := User[command.User]; ok {
myImage.Set(int(command.X), int(command.Y), user.Color
}
}
}
func DrawInput(ws *websocket.Conn) {
// boring code to read the token and pull the user from the Tokens
// variable or kill the connection as unauthorized
user := Tokens[token]
newConn := Connection{user.ID, make(chan string), make(chan bool)}
RegisterChan <- newConn
go Inputter(ws, newConn)
go Outputter(ws, newConn)
}
func Inputter(ws *websocket.Conn, conn Connection) {
var buf bytes.Buffer
for {
_, e := ws.Read(buf)
if e != nil {
close(conn.Done)
close(conn.Out)
} else {
// code to decode into a draw command instance
drawChan <- command
}
}
}
func Outputter(ws *websocket.Conn, conn Connection) {
for {
if out, closed := <-conn.Out; !closed {
_, e := io.WriteString(ws, out)
// we'll error out when user has disconnected
if e != nil {
return
}
} else {
return
}
}
}
func DrawDispatcher(tileChans []chan DrawCommand) {
var command DrawCommand
listeners := make(map[int]Connection)
for {
select {
// new draw item
case command = <-DrawChan:
tileChans[command.Tile] <- command
for user, conn := range listeners {
// skip the user who made the command
if user == command.User {
continue
}
// remove indicates that the user has disconnected
if _, remove := <-conn.Done; remove {
delete(listeners, user)
} else {
conn.Out <- command.String()
}
}
// new listener
case conn := <-RegisterChan:
user := User[conn.User]
userStr := fmt.Sprintf("User:%d:%x%x%x", user.ID, user.Color.R, user.Color.G, user.Color.B)
for _, listener := range listeners {
// remove indicates that the user has disconnected
if _, remove := <-conn.Done; remove {
delete(listeners, user)
} else {
conn.Out <- command.String()
}
}
listeners[conn.User] = conn
}
}
}
func ServeTile(w http.ResponseWriter, r *http.Request) {
// decode which tile they want from the get request
tileNum := r.URL.Query().Get("tile")
if tileNum == "" || len(tileNum) > 2 {
// didn't ask for tile, or too long of a parameter (at most 2 characters)
io.WriteString("Bad tile")
return
}
// convert the tile number (0-27 to an integer
n, e := strconv.Atoi(tileNum)
if e != nil {
// bad input in tile parameter
io.WriteString("bad tile")
return
}
// find the tile in the array of them, then encode out
if tile, ok := ImageTiles[n]; ok {
png.Encode(w, tile)
return
}
// didn't find the tile
io.WriteString("bad tile")
}
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
/*
Boring code to read the user, color form request into variables
then create a random token for the user, and send it back to the user
then save that token into the Tokens variable
*/
}
func IndexHandler(w http.ResponseWriter, r *http.Request) {
// pretend indexContent is the index page's contents as a string
io.WriteString(w, indexContent)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment