<file_sep>package go_tanks

import (
  //log "../log"
  i "../interfaces"
  v "../validators"
  h "./handlers"
  "errors"
  "fmt"
)

type GameController struct {
  Controller
}

func ( c *GameController ) JoinToGame () error {
  c.addToWorld()

  c.World.NewTank( c.Client )
  message := *( <-c.Client.InBox() )

  tank := message["Tank"].(i.Tank)
  c.Client.SetTank( tank )

  message["Message"] = fmt.Sprint("Your tank id = ", tank.GetId())
  c.Client.SendMessage( &message )

  inClientBox := c.Client.InClientBox()
  inBox := c.Client.InBox()

  for {
    select {
    case message, ok := <- inClientBox:

      if !ok {
        c.removeFromWorld()
        return errors.New("Receive channel closed.")
      }

      err := v.ValidateUserMessage( message )

      if ( err != nil ) { c.Client.SendMessage( i.ErrorMessage( err.Error() ) ); continue }

      c.handleMessage( message )

    case message, _ := <- inBox:

      switch message.GetTypeId() {
      case i.DESTROY_TANK:
        c.Client.SendMessage( &i.Message{"Type":"Hit", "Message": "Your are die :("} )
        return nil
      case i.HIT_TANK:
        c.Client.SendMessage( &i.Message{"Type":"Hit", "Message": "Tank hit."} )
      }

    }
  }

  return nil
}

func ( c *GameController ) addToWorld () {
  c.World.AttachClient( c.Client )
}

func ( c *GameController ) removeFromWorld () {
  c.World.DetachClient( c.Client )
}

func ( c *GameController ) handleMessage ( m *i.Message ) error {
  return h.HandleMessage(c.World, c.Client, m)
}
<file_sep>package go_tanks

import (
  i "../interfaces"
)

type Controller struct {
  Client  i.Client
  World   i.World
}
<file_sep>package go_tanks

const (
  NEW_TANK = iota
  NEW_CLIENT
  REMOVE_CLIENT
  TANK_COMMAND
  HIT_TANK
  DESTROY_TANK
)

type Message            map[string]interface{}
type MessageChan        chan *Message

type Client interface {
  SendMessage ( *Message ) error
  ReadMessage () ( *Message, error )

  SetAuthCredentials ( login, password string)
  SetTank ( Tank )
  GetTank () Tank
  HasTank () bool

  Login () *string
  Password () *string

  OutBox () MessageChan
  InBox () MessageChan

  OutClientBox () MessageChan
  InClientBox () MessageChan

  OutBoxHasPlace () bool

  SendWorld ( *Message )
  SetWorldDisabled( bool )
  SetWorldFrequency ( val int )

  ReInit()
}

var typeToIdMaping = map[string]int {
  "TankCommand": TANK_COMMAND,
}

func ( m *Message ) GetTypeId () interface{} {
  message := *m
  if message["TypeId"] == nil {
    message["TypeId"] = typeToIdMaping[ message["Type"].(string) ]
  }

  return message["TypeId"].(int)
}

func ErrorMessage ( message string ) *Message {
  return &Message{"Type":"Error", "Message": message}
}
<file_sep>package go_tanks

import (
  log "./log"
)

type FrontController struct {
  World           *World
  ClientsChannel  <-chan *Client
}

func (fc *FrontController) Accept () {
  for {
    client := <- fc.ClientsChannel
    log.Client("New client connected ( ", client.RemoteAddr(), " )");

    fc.processClient(client)
  }
}

func (fc *FrontController) processClient (client *Client) {
  dispatcher := Dispatcher{ Client: client, World: fc.World }
  go dispatcher.run()
}
<file_sep>package go_tanks

import (
  log "./log"
  controllers "./controllers"
  i "./interfaces"
  //"fmt"
)

type Dispatcher struct {
  World   *World
  Client  *Client
}

func ( d *Dispatcher ) run () {
  d.dispatch()
}

func ( d *Dispatcher ) dispatch () error {
  defer d.Client.Close()
  var err error;

  for {
    err = d.authStep();
    if ( err != nil ) { return d.sendError( err ) }

    err = d.gameStep();
    if ( err != nil ) { return d.sendError( err ) }

    d.Client.ReInit()
  }

  return nil
}

func ( d *Dispatcher ) gameStep () error {
  controller := controllers.GameController{ controllers.Controller { Client: d.Client, World: d.World } }
  return controller.JoinToGame()
}

func ( d *Dispatcher ) authStep () error {
  controller := controllers.AuthController{ controllers.Controller{ Client: d.Client, World: d.World } }
  return controller.Authorize()
}

func ( d *Dispatcher ) sendError (err error) error {
    log.Error(err)
    d.Client.SendMessage( i.ErrorMessage(err.Error()) )

    return err
}
<file_sep>package go_tanks

import (
  "github.com/gorilla/websocket"
  "net"
  i "./interfaces"
  "errors"
  log "./log"
  "time"
)

const (
  NON_AUTHORIZED = iota
  AUTHORIZED
)

const (
  INBOX_CAPACITY = 5
  OUTBOX_CAPACITY = 5
  CLIENT_BUFFER_CAPACITY = 5
)

type Client struct {
  Connection  IConn
  State         int
  login         string
  password      string
  Tank          *Tank
  // inside message channels
  outBox        i.MessageChan
  inBox         i.MessageChan
  // client message channels
  inClientBox   i.MessageChan
  outClientBox  i.MessageChan

  // world would be receive every n ticks
  worldFrequency  int
  worldFrequencyN int

  // do not send world messages
  worldDisabled bool
  Closed        bool
}

func ( c *Client )  RemoteAddr () ( net.Addr ) {
  return c.Connection.RemoteAddr()
}

func NewClient ( conn IConn ) ( *Client ) {
  client := &Client{
    Connection: conn,
    State: NON_AUTHORIZED,
    inBox: make( i.MessageChan, INBOX_CAPACITY ),
    outBox: make( i.MessageChan, OUTBOX_CAPACITY ),
    inClientBox: make( i.MessageChan, CLIENT_BUFFER_CAPACITY ),
    outClientBox: make( i.MessageChan, CLIENT_BUFFER_CAPACITY ),
    worldDisabled: false,
    Closed: false,
  }
  client.Init()
  return client
}

func NewWsClient ( ws *websocket.Conn ) ( *Client ) {
  return NewClient( NewWsConn(ws) )
}

func NewNetClient ( conn *net.Conn ) ( *Client ) {
  return NewClient( NewNetConn(conn) )
}

func ( c *Client ) Init () {
  go c.startSendMessageLoop()
  go c.startReceiveMessageLoop()
}

func ( c *Client ) ReInit () {
  c.Tank = nil
  c.State = NON_AUTHORIZED
  c.login = ""
  c.password = ""
  log.Client("Client reinitialized.")
}

func ( c *Client ) Close () {
  c.Closed = true

  // Wait for write all messages
  <- time.After(time.Second)

  c.Connection.Close()
  close(c.outClientBox)

  log.Client("Client closed.")
}

func ( c *Client ) SendMessage ( m *i.Message ) error {
  select {
  case c.outClientBox <- m:
    return nil
  default:
    return errors.New("Slow client.")
  }
}

func ( c *Client ) startReceiveMessageLoop () {
  defer close(c.inClientBox)

  for {
    message, err := c.Connection.ReadMessage()
    if err != nil { break }

    select {
    case c.inClientBox <- message:
    default:
    }

  }

  log.Client("Receive message finished.")
}
func ( c *Client ) startSendMessageLoop () {

  for {
    message, ok := <- c.outClientBox
    if !ok { break }
    c.Connection.WriteMessage( message )
  }

  log.Client("Send message finished.")
}

func ( c *Client ) ReadMessage () ( *i.Message, error ) {
  m, ok := <- c.inClientBox
  if !ok { return nil, errors.New("Read channel was closed.") }
  return m, nil
}

func ( c *Client ) SetAuthCredentials ( login, password string ) {
  c.login = login
  c.password = <PASSWORD>
}

func ( c *Client ) Login () *string {
  return &c.login
}

func ( c *Client ) Password () *string {
  return &c.password
}

func ( c *Client ) HasTank () bool {
  return c.Tank != nil
}

func ( c *Client ) SetTank ( tank i.Tank ) {
  c.Tank = tank.(*Tank)
}

func ( c *Client ) GetTank () i.Tank {
  return c.Tank
}

func ( c *Client ) InBox () i.MessageChan {
  return c.inBox
}

func ( c *Client ) OutBox () i.MessageChan {
  return c.outBox
}

func ( c *Client ) InClientBox () i.MessageChan {
  return c.inClientBox
}

func ( c *Client ) OutClientBox () i.MessageChan {
  return c.outClientBox
}

func ( c *Client ) SendWorld ( m *i.Message ) {
  if( c.worldDisabled ) { return }

  if( c.worldFrequencyN == 0 ) {
    c.worldFrequencyN = c.worldFrequency
    err := c.SendMessage(m)
    if( err != nil ) { log.Error(err) }
  } else {
    c.worldFrequencyN--
  }

}

func ( c *Client ) SetWorldDisabled ( val bool ) {
  c.worldDisabled = val
}

func ( c *Client ) SetWorldFrequency ( val int ) {
  c.worldFrequency = val
  c.worldFrequencyN = c.worldFrequency
}

func ( c *Client ) OutBoxHasPlace () bool {
  return cap(c.outBox) - len(c.outBox) > 0
}
<file_sep>package go_tanks

import(
  log "./log"
)

func NewServer(cfg Config) *Server {
  return &Server{ config: &cfg }
}

type Server struct {
  config          *Config
  world           *World
  frontController *FrontController
};

func (srv *Server) Run () {
  srv.runWorld();

  log.Server("Server starting...");
  srv.run()
}

func (srv *Server) run () {
  tcpServer := TCPServer { Port: srv.config.Port, Address: srv.config.Address }
  channel := ClientChannel

  go tcpServer.run( channel )

  srv.frontController = &FrontController{ World: srv.world, ClientsChannel: channel }
  srv.frontController.Accept();
}

func (srv *Server) runWorld () {
  srv.world = NewWorld( *srv.config.World )
  srv.world.run();
}

<file_sep>package go_tanks

import (
  "errors"
  log "../log"
  i "../interfaces"
  "fmt"
  v "../validators"
)

const (
  HELLO = "Hello! You should authorize befor join the game!"
)

type AuthController struct {
  Controller
}

func ( c *AuthController ) Authorize () error {
  c.sendHello();
  c.readAuth();

  if ( !c.isAuthorized() ){
    return errors.New( fmt.Sprintf("Authorization failed with credentials: %s / %s", *c.Client.Login(), *c.Client.Password() ) )
  }

  return nil
}

func ( c *AuthController ) sendHello () {
  c.Client.SendMessage( &i.Message{
    "Message":  HELLO,
    "Type": "Auth",
  })
}

func ( c *AuthController ) readAuth () error {

  for !c.isAuthorized() {
    m, err := c.Client.ReadMessage()
    if err != nil { log.Error("Auth failed: ", err); return err }

    err = v.ValidateAuthForm(m)
    if( err != nil ) { c.Client.SendMessage( i.ErrorMessage( err.Error() ) ); continue; }

    message := *m
    c.Client.SetAuthCredentials( message["Login"].(string) , message["Password"].(string) )
  }

  return nil
}

func ( c * AuthController ) isAuthorized () bool {
  return len( *c.Client.Login() ) > 0
}
<file_sep>package go_tanks

type World interface {
  NewTank ( client Client )
  AttachClient ( client Client )
  DetachClient ( client Client )
}
