Permalink
Browse files

Inital checkin of the go source files

  • Loading branch information...
0 parents commit 450a166918e5037a00432d6bd0f6b1d68c998ee7 @jasondelponte committed Apr 12, 2012
Showing with 528 additions and 0 deletions.
  1. +17 −0 apollo.go
  2. +47 −0 board.go
  3. +163 −0 conn.go
  4. +6 −0 entity.go
  5. +130 −0 game.go
  6. +49 −0 player.go
  7. +45 −0 simulation.go
  8. +71 −0 templates/home.html
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "flag"
+ "log"
+)
+
+var addr = flag.String("addr", ":8080", "Address and Port server is to run on")
+
+func main() {
+ flag.Parse()
+
+ defer func() { log.Println("Existing apollo") }()
+ go game.Run()
+
+ game.InitConnections(*addr)
+}
@@ -0,0 +1,47 @@
+package main
+
+import ()
+
+type Board struct {
+ entities []*Entity
+ players []*Player
+}
+
+func NewBoard() *Board {
+ return &Board{entities: make([]*Entity, 0, 10), players: make([]*Player, 0, 5)}
+}
+
+func (b *Board) AddPlayer(p *Player) {
+ b.players = append(b.players, p)
+}
+
+func (b *Board) AddEntities(e []*Entity) {
+ slice := b.entities
+ orgLen := len(slice)
+ newLen := orgLen + len(e)
+ if newLen > cap(slice) { // reallocate
+ // Allocate double what we have, for future growth.
+ newSlice := make([]*Entity, 0, (newLen+1)*2)
+ copy(newSlice, slice)
+ slice = newSlice
+ }
+ slice = slice[0:newLen]
+ copy(slice[orgLen:newLen], e)
+
+ b.entities = slice
+}
+
+func (b *Board) AddEntity(e *Entity) {
+ slice := b.entities
+ l := len(slice)
+ if l+1 > cap(slice) { // reallocate
+ // Allocate double what we have, for future growth.
+ newSlice := make([]*Entity, 0, (cap(slice)+1)*2)
+ copy(newSlice, slice)
+ slice = newSlice
+ }
+ slice = slice[0 : l+1]
+ slice[l+1] = e
+
+ b.entities = slice
+}
163 conn.go
@@ -0,0 +1,163 @@
+package main
+
+import (
+ "encoding/json"
+ "github.com/garyburd/go-websocket/websocket"
+ "io"
+ "io/ioutil"
+ "log"
+ "time"
+)
+
+type Connection interface {
+ AttachReader(reader chan MessageIn)
+ Send(msg interface{}) error
+ ReadPump()
+ WritePump()
+ Close()
+}
+
+type MessageIn struct {
+ ReqId string
+ Cmd *MsgPartCommand
+}
+
+type MsgPartCommand struct {
+ OpCode string
+}
+
+type MsgResponse struct {
+ Rsp string
+}
+
+type MsgBlock struct {
+ X int
+ Y int
+ R int
+ G int
+ B int
+ A int
+ W int
+ H int
+}
+
+const (
+ readWait = 60 * time.Second
+ pingPeriod = 25 * time.Second
+ writeWait = 10 * time.Second
+ maxMessageSize = 512
+)
+
+// Creates a new instance of the ws
+func NewWsConn() *WsConn {
+ return &WsConn{}
+}
+
+type WsConn struct {
+ id uint64
+
+ // The websocket connection.
+ ws *websocket.Conn
+
+ // Buffered channel of outbound messages.
+ send chan []byte
+
+ // Buffered channel for readers
+ reader chan MessageIn
+}
+
+// Sets the channel the connection should forward incomming messages to
+func (c *WsConn) AttachReader(reader chan MessageIn) {
+ c.reader = reader
+}
+
+// Serializes an object and sends it across the the wire
+func (c *WsConn) Send(msg interface{}) error {
+ marshaled, err := json.Marshal(msg)
+ if err != nil {
+ log.Println("ERROR", "Connection", c.id, "Failed to marshal data to send to client")
+ return err
+ }
+ c.send <- marshaled
+ return nil
+}
+
+// Closes the send and attached reader channels
+func (c *WsConn) Close() {
+ close(c.send)
+ close(c.reader)
+}
+
+// Handler furnction for periodic reading from the input socks.
+// Handles closing of the socket, of the connection drops
+func (c *WsConn) ReadPump() {
+ defer c.ws.Close()
+ for {
+ c.ws.SetReadDeadline(time.Now().Add(readWait))
+ op, r, err := c.ws.NextReader()
+ if err != nil {
+ log.Println("Error getting next reader", err)
+ return
+ }
+ if op != websocket.OpText {
+ continue
+ }
+ lr := io.LimitedReader{R: r, N: maxMessageSize + 1}
+ message, err := ioutil.ReadAll(&lr)
+ if err != nil {
+ log.Println("Error reading message", err)
+ return
+ }
+ if lr.N <= 0 {
+ log.Println("No data received from message, closing")
+ c.ws.WriteControl(websocket.OpClose,
+ websocket.FormatCloseMessage(websocket.CloseMessageTooBig, ""),
+ time.Now().Add(time.Second))
+ return
+ }
+
+ var unmarshaled MessageIn
+ json.Unmarshal(message, &unmarshaled)
+
+ c.reader <- unmarshaled
+ }
+}
+
+// write writes a message with the given opCode and payload.
+func (c *WsConn) write(opCode int, payload []byte) error {
+ c.ws.SetWriteDeadline(time.Now().Add(writeWait))
+ w, err := c.ws.NextWriter(opCode)
+ if err != nil {
+ log.Println("Failed to get next writer", err)
+ return err
+ }
+ if _, err := w.Write(payload); err != nil {
+ log.Println("Failed to write payload", err)
+ w.Close()
+ return err
+ }
+ return w.Close()
+}
+
+// writePump pumps messages from the hub to the websocket connection.
+func (c *WsConn) WritePump() {
+ defer c.ws.Close()
+ ticker := time.NewTicker(pingPeriod)
+ defer ticker.Stop()
+ for {
+ select {
+ case message, ok := <-c.send:
+ if !ok {
+ c.write(websocket.OpClose, []byte{})
+ return
+ }
+ if err := c.write(websocket.OpText, message); err != nil {
+ return
+ }
+ case <-ticker.C:
+ if err := c.write(websocket.OpPing, []byte{}); err != nil {
+ return
+ }
+ }
+ }
+}
@@ -0,0 +1,6 @@
+package main
+
+import ()
+
+type Entity interface {
+}
130 game.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+ "github.com/garyburd/go-websocket/websocket"
+ "html/template"
+ "log"
+ "net/http"
+)
+
+type Game struct {
+ sim *Simulation
+ players map[*Player]bool
+ register chan *Player
+ unregister chan *Player
+ playerCtrl chan interface{}
+ simUpdate chan interface{}
+}
+
+var homeTempl = template.Must(template.ParseFiles("templates/home.html"))
+
+// Initalization of the game object.game It s being done in the package's
+// global scope so the network event handler will have access to it when
+// receiving new player connections. TODO figure out how to remove it from global
+var game = &Game{
+ players: make(map[*Player]bool),
+ register: make(chan *Player),
+ unregister: make(chan *Player),
+ playerCtrl: make(chan interface{}),
+ simUpdate: make(chan interface{}),
+}
+
+// Creates and setups the the network event listers for html
+// and websocket interfaces
+func (g *Game) InitConnections(addr string) {
+ http.HandleFunc("/", serveHome)
+ http.HandleFunc("/ws", serveWsConn)
+
+ err := http.ListenAndServe(addr, nil)
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+}
+
+// Event receiver to processing messages between the simulation and
+// the players. If players are connected to the game the simulation
+// will be started, but as soon as the last player drops out the
+// simulation will be terminated.
+func (g *Game) Run() {
+ for {
+ select {
+ case p := <-g.register:
+ log.Println("New Player registered")
+ g.players[p] = true
+
+ // Create the sim if this is the first user connected
+ if g.sim == nil {
+ g.startSim()
+ }
+
+ case p := <-g.unregister:
+ log.Println("Player unregistered")
+ delete(g.players, p)
+ p.Disconnect()
+
+ // Disable the sim if there are no more players
+ if len(g.players) == 0 {
+ g.stopSim()
+ }
+ case <-g.playerCtrl:
+ // TODO do soemthing with the incomming player control object
+
+ case update := <-g.simUpdate:
+ for p, _ := range g.players {
+ p.UpdateBoard(update)
+ }
+ }
+
+ }
+}
+
+// Create the simulator, and start it running
+func (g *Game) startSim() {
+ g.sim = NewSimulation(g.simUpdate)
+ go g.sim.Run()
+}
+
+// Terminate the simulator, and remove its instance
+func (g *Game) stopSim() {
+ close(g.sim.halt)
+ g.sim = nil
+}
+
+// Network event handler for HTTP trafic. Serves up the
+// home.html file which will allow connection to the websocket
+func serveHome(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ http.Error(w, "Not found", 404)
+ return
+ }
+ if r.Method != "GET" {
+ http.Error(w, "Method nod allowed", 405)
+ return
+ }
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ homeTempl.Execute(w, r.Host)
+}
+
+// handles webocket requests from the client.
+func serveWsConn(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "GET" {
+ http.Error(w, "Method not allowed", 405)
+ return
+ }
+ ws, err := websocket.Upgrade(w, r.Header, "", 1024, 1024)
+ if err != nil {
+ log.Println(err)
+ http.Error(w, "Bad request", 400)
+ return
+ }
+
+ conn := &WsConn{send: make(chan []byte, 256), ws: ws}
+ player := NewPlayer(conn)
+
+ defer func() { game.unregister <- player }()
+ go conn.WritePump()
+ go player.Run(game)
+
+ // Read pump will hold the connection open until we are finished with it.
+ conn.ReadPump()
+}
Oops, something went wrong.

0 comments on commit 450a166

Please sign in to comment.