Last active
December 26, 2015 16:09
-
-
Save acsellers/7178119 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 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