
ç§ãã¡ã¯Goããã°ã©ãã³ã°èšèªïŒgolangïŒã®ç¥èãç¶ç¶ããŠããŸãã ååã¯ãåºæ¬çãªèšèªæ§é ã«ã€ããŠèŠãŠããŸããã ãã®èšäºã§ã¯ããŽã«ãŒãã³ãšãã£ãã«ã®äœ¿çšæ¹æ³ã瀺ããŸãã ãããŠããã¡ããããã®ãã¹ãŠãå®éã®ã¢ããªã±ãŒã·ã§ã³ïŒãã®å Žåã¯ãã«ããã¬ã€ã€ãŒã²ãŒã ïŒã§å®æŒããŸãã ã²ãŒã å
šäœã§ã¯ãªããWebSoketãä»ãããã¬ãŒã€ãŒéã®ãããã¯ãŒã¯çžäºäœçšãæ
åœããããã¯ãšã³ãã®éšåã®ã¿ãèæ
®ããŸãã
ã²ãŒã ã¯2人ã®ãã¬ã€ã€ãŒã®ããã®ã¿ãŒã³ããŒã¹ã§ãã ãã ãã以äžã§èª¬æããææ³ã¯ãããŒã«ãŒããæŠç¥ãŸã§ãä»ã®ã²ãŒã ãäœæããããã«äœ¿çšã§ããŸãã
ã¡ãªã¿ã«ãããã¯ç§ã®æåã®ã²ãŒã ã§ãããWebSocketã§ã®æåã®ä»äºãªã®ã§ãå³å¯ã«å€æããªãã§ãã ããã ã³ã¡ã³ããåççãªæ¹å€ãããã°ãåãã§èããŸãã
ã¢ã«ãŽãªãºã ã¯æ¬¡ã®ãšããã§ãã ãã¬ã€ã€ãŒã¯ã²ãŒã ã«ãŒã ïŒéšå±ïŒã«æ¥ç¶ããŸãã ãã¬ãŒã€ãŒããæ°ããåããå±ããšãã«ãŒã ã«ïŒãã£ãã«çµç±ã§ïŒãããéç¥ãããã«ãŒã ã«ç»é²ãããŠãããã¹ãŠã®ãã¬ãŒã€ãŒã§ãã²ãŒã ç¶æ
ã®æŽæ°ããšããç¹å¥ãªã¡ãœãããåŒã³åºãããŸãã ãã¹ãŠãéåžžã«ç°¡åã§ãã
æŠç¥çã«ãããã¯æ¬¡ã®ããã«è¡šãããšãã§ããŸãã
ãã¬ãŒã€ãŒãšã®éä¿¡ã¯ããæ¥ç¶ãã¬ã€ã€ãŒãªããžã§ã¯ãïŒå³pConn1ãpConn2ïŒãä»ããŠè¡ãããŸãããã®ãªããžã§ã¯ãã¯ããã¬ãŒã€ãŒã¿ã€ããïŒããèªäœã«åã蟌ãããšã«ããïŒæ¡åŒµããéä¿¡ã®ããã®ã¡ãœãããè¿œå ããŸãã
ã¡ãªã¿ã«ããªããžã§ã¯ãã®OOPã®æå³ã§ã¯ãªãããšã³ãã£ãã£ã®æå®ãšããŠããªããžã§ã¯ãããšããèšèã䜿çšããããšããããŸãïŒç§»åäžã¯è¥å¹²ç°ãªãããïŒã
ãããžã§ã¯ãã®æ§é ãèæ
®ããŠãã ããã
/wsgame/ /game/ game.go /templates/ /utils/ utils.go main.go conn.go room.go
ã«ãŒããã¡ã€ã«ïŒã¡ã€ã³ããã±ãŒãžïŒã«ããããã¯ãŒã¯ã®çžäºäœçšãå®è£
ãããŠããŸãã
/ game /ããã±ãŒãžã«ã¯ãã²ãŒã ãšã³ãžã³èªäœãå«ãŸããŠããŸãã ç§ãã¡ã¯ãããèæ
®ããŸãããããã§ã¯ãã²ãŒã ãå¶åŸ¡ããããã«å¿
èŠãªãã¢ãã¯ã®åœ¢ã§ããã€ãã®ã¡ãœããã®ã¿ãæäŸããŸãã
ã²ãŒã
/game/game.go package game import ( "log" ) type Player struct { Name string Enemy *Player } func NewPlayer(name string) *Player { player := &Player{Name: name} return player } func PairPlayers(p1 *Player, p2 *Player) { p1.Enemy, p2.Enemy = p2, p1 } func (p *Player) Command(command string) { log.Print("Command: '", command, "' received by player: ", p.Name) } func (p *Player) GetState() string { return "Game state for Player: " + p.Name } func (p *Player) GiveUp() { log.Print("Player gave up: ", p.Name) }
ãã¬ã€ã€ãŒïŒãã¬ã€ã€ãŒïŒã«ã¯ãåããã¬ã€ã€ãŒã§ããæµãããŸãïŒãã®æ§é ã§ã¯ãããã¯*ãã¬ã€ã€ãŒãã€ã³ã¿ãŒã§ãïŒã ãã¬ãŒã€ãŒãæ¥ç¶ããã«ã¯ãPairPlayersé¢æ°ã䜿çšããŸãã ããã«ãã²ãŒã ã®å¶åŸ¡ã«å¿
èŠãªæ©èœã®äžéšã以äžã«ç€ºããŸãã ããã§ã¯äœããããã³ã³ãœãŒã«ã«ã¡ãã»ãŒãžã衚瀺ããã ãã§ãã ã³ãã³ã-ã³ãã³ããéä¿¡ïŒç§»åïŒ; GetState-ãã®ãã¬ãŒã€ãŒã®ã²ãŒã ã®çŸåšã®ç¶æ
ãååŸããŸãã ã®ãã¢ãã-éäŒãã察æŠçžæã«åå©ãå²ãåœãŠãŸãã
UPDïŒãã®åŸã1ã€ã®ã²ãŒã ã«1ã€ã®Playeræ§é ããæããªãããšã¯ããŸã䟿å©ã§ã¯ãªãããšãå€æããŸããã ãã¬ã€ã€ãŒãæ¥ç¶ãããŠããã²ãŒã æ§é ãäœæããããšããå§ãããŸãã ããããããã¯å¥ã®è©±ã§ãã
ã¡ã€ã³
main.go package main import ( "github.com/alehano/wsgame/game" "github.com/gorilla/websocket" "html/template" "log" "net/http" "net/url" ) const ( ADDR string = ":8080" ) func homeHandler(c http.ResponseWriter, r *http.Request) { var homeTempl = template.Must(template.ParseFiles("templates/home.html")) data := struct { Host string RoomsCount int }{r.Host, roomsCount} homeTempl.Execute(c, data) } func wsHandler(w http.ResponseWriter, r *http.Request) { ws, err := websocket.Upgrade(w, r, nil, 1024, 1024) if _, ok := err.(websocket.HandshakeError); ok { http.Error(w, "Not a websocket handshake", 400) return } else if err != nil { return } playerName := "Player" params, _ := url.ParseQuery(r.URL.RawQuery) if len(params["name"]) > 0 { playerName = params["name"][0] }
ããã¯ãããã°ã©ã ãžã®ãšã³ããªãã€ã³ãã§ãã mainïŒïŒé¢æ°ã¯ãµãŒããŒãèµ·åãã2ã€ã®ãã³ãã©ãŒãç»é²ããŸããhome.htmlãã³ãã¬ãŒãã®ã¿ã衚瀺ããã¡ã€ã³ããŒãžã®homeHandlerãšãWebSocketæ¥ç¶ã確ç«ããŠãã¬ãŒã€ãŒãç»é²ããããèå³æ·±ãwsHandlerã§ãã
WebSocketã®å ŽåãGorilla Toolkitã®ããã±ãŒãžïŒ "github.com/gorilla/websocket"ïŒã䜿çšããŸãã æåã«ãæ°ããæ¥ç¶ïŒwsïŒãäœæããŸãã 次ã«ãURLãã©ã¡ãŒã¿ãŒãããã¬ãŒã€ãŒã®ååãååŸããŸãã 次ã«ãç¡æã®éšå±ïŒãã¬ã€ã€ãŒ1人ïŒãæ¢ããŸãã ã¹ããŒã¹ããªãå Žåã¯ãäœæããŸãã ãã®åŸããã¬ãŒã€ãŒãšãã¬ãŒã€ãŒã®æ¥ç¶ãªããžã§ã¯ãïŒpConnïŒãäœæããŸãã Webãœã±ããããã¬ãŒã€ãŒãããã³éšå±ãæ¥ç¶ã«è»¢éããŸãã ããæ£ç¢ºã«ã¯ããããã®ãªããžã§ã¯ãã«ãã€ã³ã¿ãŒãæž¡ããŸãã æåŸã®ã¹ãããã¯ãæ¥ç¶ãéšå±ã«æ¥ç¶ããããšã§ãã ããã¯ããªããžã§ã¯ããã«ãŒã ã®åå ãã£ã³ãã«ã«éä¿¡ããããšã«ããè¡ãããŸãã
ãŽã«ãŒãã³ãšãã£ã³ãã«
ãŽã«ãŒãã³ãšãã£ã³ãã«ã«é¢ããå°ããªæè²ããã°ã©ã ã ãŽã«ãŒãã³ã¯ã¹ã¬ããã®ãããªãã®ã§ã䞊è¡ããŠå®è¡ãããŸãã é¢æ°åŒã³åºãã®åã«goã¹ããŒãã¡ã³ãã眮ãã ãã§ååã§ããããã°ã©ã ã¯ãé¢æ°ãå®äºãããŸã§åŸ
æ©ãããããã«æ¬¡ã®åœä»€ã«é²ã¿ãŸãã ãŽã«ãã³ã¯éåžžã«è»œéã§ãã¡ã¢ãªãå¿
èŠãšããŸããã ãŽã«ãŒãã³ãšã®éä¿¡ã¯ãç¹å¥ãªããŒã¿åã§ãããã£ãã«ãä»ããŠè¡ãããŸãã ãã€ãã¯Unixã®ãã€ãã«äŒŒãŠããŸãã ãã£ãã«ããã€ããšããŠæ³åã§ããŸããäžæ¹ã«äœãã眮ããããäžæ¹ããååŸããŸãã ãã£ãã«ã®ã¿ã€ãã¯ä»»æã§ãã ããšãã°ãæååãã£ãã«ãäœæããŠã¡ãã»ãŒãžãéä¿¡ã§ããŸãã ãã£ã³ãã«ãã£ãŒããäœæããããšãã§ããŸãã ãã£ãšæ·±ãããå¿
èŠããããŸãã
å°ããªäŸã ããã§å®è¡ã§ããŸã
http://play.golang.org/p/QUc458nBJYåããªã¯ãšã¹ããè€æ°ã®ãµãŒããŒã«éä¿¡ããããéãå¿çãããµãŒããŒããå¿çãååŸãããšããŸãã ãããŠãæ®ããåŸ
ã¡ãããªãã 次ã®æ¹æ³ã§å®è¡ã§ããŸãã
package main import "fmt" func getDataFromServer(resultCh chan string, serverName string) { resultCh <- "Data from server: " + serverName } func main() { res := make(chan string, 3) go getDataFromServer(res, "Server1") go getDataFromServer(res, "Server2") go getDataFromServer(res, "Server3") data := <- res fmt.Println(data) }
å¿çãåãåãresãã£ãã«ãäœæããŸãã ãããŠãå¥ã®ãŽã«ãŒãã³ã§ããµãŒããŒãžã®ãªã¯ãšã¹ããéå§ããŸãã æäœã¯ãããã¯ãããªããããgoæŒç®åã䜿çšããè¡ã®åŸãããã°ã©ã ã¯æ¬¡ã®è¡ã«ç§»åããŸãã Dalleãããã°ã©ã ã¯è¡
data := <- res
ãããã¯ãããŠã
data := <- res
ããã£ãã«ããã®å¿çãåŸ
ã£ãŠããŸãã åçãåä¿¡ããããšããã«ãç»é¢ã«è¡šç€ºãããããã°ã©ã ãçµäºããŸãã ãã®åæäŸã§ã¯ãServer1ããã®å¿çãè¿ãããŸãã ããããå®éã«ã¯ããªã¯ãšã¹ãã«ç°ãªãæéããããå Žåãæéã®ãµãŒããŒããã®å¿çãè¿ãããŸãã
UPDïŒãã£ã³ãã«ã®äœæäžã®çªå·3ã¯ããã£ã³ãã«ããããã¡ãŒãããŠããããšã瀺ããŸãããµã€ãº3ãããã¯ããã£ã³ãã«ã«éä¿¡ãããšãïŒç©ºãã¹ããŒã¹ãããå ŽåïŒã誰ããããŒã¿ãåä¿¡ãããŸã§åŸ
ã€å¿
èŠããªãããšãæå³ããŸãã ãã®å Žåãããã¯ã§ããŸããã§ããã ãšã«ããããã°ã©ã ã¯çµäºããŸãã ããããããšãã°ãåžžã«åäœããWebãµã€ãã§ããããã£ãã«ããããã¡ãªã³ã°ãããªãå Žåã3ã€ã®ãŽã«ãŒãã³ã®ãã¡2ã€ãããªãŒãºããå察åŽã§ã®åä¿¡ãåŸ
æ©ããŸãã
ããã§ã¯ãã©ã ã«æ»ããŸãããã
æ¥ç¶
conn.go package main import ( "github.com/alehano/wsgame/game" "github.com/gorilla/websocket" ) type playerConn struct { ws *websocket.Conn *game.Player room *room }
äžéå±€ãšã¯äœã§ããïŒ ããã¯ãWebãœã±ããããã¬ãŒã€ãŒãã«ãŒã ãžã®ãã€ã³ã¿ãŒãå«ãplayerConnãªããžã§ã¯ãã§ãã ãã¬ãŒã€ãŒã®å Žåã* game.Playerãšèšè¿°ããŸããã ã€ãŸããPlayerããåã蟌ã¿ããplayerConnã§ãã®ã¡ãœãããçŽæ¥åŒã³åºãããšãã§ããŸãã ç¶æ¿ã®ãããªãã®ã æ°ããæ¥ç¶ïŒNewPlayerConnïŒãäœæãããšãã¬ã·ãŒããŒã¡ãœãããå¥ã®ãŽã«ãŒãã³ïŒgoã¹ããŒãã¡ã³ãïŒã§èµ·åãããŸãã 䞊è¡ããŠïŒéãããã¯æ¹åŒïŒãç¡éã«ãŒãã§ã¡ãã»ãŒãžã®Webãœã±ããããªãã¹ã³ããŸãã ãããåãåããšãCommandã¡ãœããã§ãã¬ãŒã€ãŒã«æž¡ãããŸãïŒç§»åããŸãïŒã ãããŠãããã¹ãŠã®ãã¬ã€ã€ãŒã®ã²ãŒã ã®ç¶æ
ãæŽæ°ãããä¿¡å·ãéšå±ã«éããŸãã ãšã©ãŒïŒããšãã°ãWebãœã±ããã®ãã¬ãŒã¯ïŒãçºçããå Žåãgoroutinã¯ã«ãŒãããåºãŠããsurrenderãä¿¡å·ãã«ãŒã ãã£ãã«ã«éä¿¡ããWebãœã±ãããéããŠçµäºããŸãã
sendStateïŒïŒã¡ãœããã䜿çšããŠãã²ãŒã ã®çŸåšã®ç¶æ
ããã®ãã¬ãŒã€ãŒã«éä¿¡ããŸãã
éšå±
room.go package main import ( "github.com/alehano/wsgame/game" "github.com/alehano/wsgame/utils" "log" ) var allRooms = make(map[string]*room) var freeRooms = make(map[string]*room) var roomsCount int type room struct { name string
æåŸã®éšåã¯éšå±ã§ãã ããã€ãã®ã°ããŒãã«å€æ°ãäœæããŸãïŒallRooms-äœæããããã¹ãŠã®éšå±ã®ãªã¹ããfreeRooms-1人ã®ãã¬ãŒã€ãŒãããéšå±ïŒçè«äžã¯è€æ°ã¯ãªãã¯ãã§ãïŒãroomsCount-äœæ¥éšå±ã®ã«ãŠã³ã¿ãŒã
ã«ãŒã ãªããžã§ã¯ãã«ã¯ãã«ãŒã ã®ååãplayerConns-æ¥ç¶ãããæ¥ç¶ïŒãã¬ãŒã€ãŒïŒã®ãªã¹ããããã³å¶åŸ¡çšã®è€æ°ã®ãã£ãã«ãå«ãŸããŸãã ãã£ãã«ã«ã¯ããŸããŸãªã¿ã€ãããããŸããããã¯ããã£ãã«ãšã®éã§éåä¿¡ã§ãããã®ã§ãã ããšãã°ãupdateAllãã£ãã«ã«ã¯ããŒã«å€ãå«ãŸããŠãããã²ãŒã ã®ç¶æ
ãæŽæ°ããå¿
èŠããããã©ãããéç¥ããã ãã§ãã äœãéä¿¡ããããã¯åé¡ã§ã¯ãªãããã®æäœã«ã®ã¿åå¿ããŸãã 確ãã«ããã®å Žåã¯ç©ºã®æ§é äœ{}ã䜿çšããããšããå§ãããŸãã ãã ããç¹å®ã®æ¥ç¶ïŒãŸãã¯ããããããžã®ãã€ã³ã¿ãŒïŒã¯ãåå ãã£ãã«ã«è»¢éãããŸãã ãããæ§é ã®ããŒãšããŠãplayerConnsã®éšå±ã«ä¿åããŸãã
NewRoomïŒïŒã䜿çšããŠæ°ããã«ãŒã ãäœæããå Žåããã£ãã«ãåæåããgoroutineã§runïŒïŒã¡ãœãããå®è¡ããŸãïŒgo room.runïŒïŒïŒã è€æ°ã®ãã£ãã«ãåæã«ãªãã¹ã³ããç¡éã«ãŒããå®è¡ããããããã®ãã£ãã«ã§ã¡ãã»ãŒãžãåä¿¡ãããšãç¹å®ã®ã¢ã¯ã·ã§ã³ãå®è¡ããŸãã è€æ°ã®ãã£ã³ãã«ããªãã¹ã³ããã«ã¯ãselect-caseã³ã³ã¹ãã©ã¯ãã䜿çšããŸãã ãã®å Žåãæäœã¯ãããã¯ãããŠããŸãã ã€ãŸã ã¡ãã»ãŒãžãããããã®ãã£ãã«ããå°çãããŸã§åŸ
æ©ããŠãããã«ãŒãã®æ¬¡ã®å埩ã«é²ã¿ãå床åŸ
æ©ããŸãã ãã ããselectã³ã³ã¹ãã©ã¯ãã«defaultïŒã»ã¯ã·ã§ã³ãããå Žåãæäœã¯ãããã¯ããããã¡ãã»ãŒãžããªãå Žåã¯ããã©ã«ããããã¯ãå®è¡ãããselectãçµäºããŸãã ãã®å Žåãããã¯ç¡æå³ã§ããããã®ãããªæ©äŒããããŸãã
åå ãã£ã³ãã«ãããªã¬ãŒãããå Žåããã®æ¥ç¶ãïŒãã¬ã€ã€ãŒã®ïŒã«ãŒã ã«ç»é²ããŸãã 2çªç®ã®ãã¬ãŒã€ãŒãæ¥ç¶ããå Žåããã¬ãŒã€ãŒãããã¢ãªã³ã°ãããç¡æã®ãã¬ãŒã€ãŒã®ãªã¹ãããéšå±ãåé€ããŸãã äŒæãããªã¬ãŒãããããæ¥ç¶ãåé€ãããã¬ãŒã€ãŒã§ãsurrenderãã¡ãœãããå®è¡ããŸãã ã«ãŒã lenïŒr.playerConnsïŒ== 0ã«ãã¬ã€ã€ãŒãæ®ã£ãŠããªãå Žåãéåžžã¯ã«ãŒããçµäºããŠïŒgoto ExitïŒã«ãŒã ãéããŸãã ã¯ããgoã«ã¯gotoã¹ããŒãã¡ã³ãããããŸãã ããããå¿é
ããªãã§ãã ããããã£ãã«äœ¿çšããããforãselectãªã©ã®æ§é ãçµäºããããã ãã«äœ¿çšãããŸãã ããšãã°ããã¹ããããã«ãŒããçµäºããŸãã ãã®å Žåããã¬ãŒã¯ãèšå®ãããšãforã«ãŒãã§ã¯ãªããselectæ§æãäžæ¢ãããŸãã
ãããŠæåŸã«ãupdateAllãã£ãã«ãããªã¬ãŒããããšïŒéä¿¡ãããå€ã¯éèŠã§ã¯ãªããããã©ãã«ãä¿åããŸããïŒcase <-r.updateAllïŒãéšå±ã«ç»é²ãããŠãããã¹ãŠã®ãã¬ã€ã€ãŒã«å¯ŸããŠã¡ãœãããã²ãŒã ç¶æ
ã®æŽæ°ããåŒã³åºãããŸãã
ããããããã¯ãŒã¯å
šäœã§ãã å®éã®ãããžã§ã¯ãã§ã¯ãããå°ãè€éã«ãªããŸããã ãã£ãããšã¿ã€ããŒãæ
åœãããã£ãã«ãšãäœããã®çš®é¡ã®ãªã¯ãšã¹ã/ã¬ã¹ãã³ã¹æ§é ïŒJSONã«åºã¥ãïŒãè¿œå ããŸããã
ãã®ãããªããã¯ãšã³ãã䜿çšãããšãããŸããŸãªããã€ã¹ã§ã¯ã©ã€ã¢ã³ããäœæããã®ã¯éåžžã«ç°¡åã§ãã ã¯ãã¹ãã©ãããã©ãŒã çšã®HTML5ã¯ã©ã€ã¢ã³ããäœæããããšã«ããŸããã iOSã§ã¯ãã²ãŒã ã¯åžžã«ã¯ã©ãã·ã¥ããŸããã websocketãµããŒããå®å
šã«å®è£
ãããŠããªãããšãããããŸãã
ãæž
èŽããããšãããããŸããã Goã§ã®ããã°ã©ãã³ã°ã¯æ¥œããã§ãã
åç
§ïŒ