Added discovery, started to sync with dart version
Change-Id: I67f6ac772293517dc6ae8ce55df5cf870822467a
diff --git a/go/Makefile b/go/Makefile
index f2acc35..f49facf 100644
--- a/go/Makefile
+++ b/go/Makefile
@@ -24,7 +24,7 @@
.PHONY:
delete:
- rm -rf tmp/syncbase_$(id)
+ rm -rf tmp/$(name)
# Naming collisions for different instances of syncbase for the same user?
# Easy way to make --v23.permissions.literal?
@@ -35,7 +35,7 @@
bin/syncbased \
--v=5 \
--alsologtostderr=false \
- --root-dir=tmp/syncbase_$(id) \
+ --root-dir=tmp/$(name) \
--name=users/$(email)/croupier/$(name) \
--v23.proxy=proxy \
--v23.credentials=credentials \
@@ -50,4 +50,6 @@
vet:
jiri go vet hearts/...
-all: fmt
\ No newline at end of file
+all: fmt
+
+ #--name=/192.168.86.254:8101/croupier/$(name) \
\ No newline at end of file
diff --git a/go/src/hearts/assets/Deal.png b/go/src/hearts/assets/Deal.png
new file mode 100644
index 0000000..c22ea22
--- /dev/null
+++ b/go/src/hearts/assets/Deal.png
Binary files differ
diff --git a/go/src/hearts/assets/JoinGame.png b/go/src/hearts/assets/JoinGame.png
new file mode 100644
index 0000000..e1925b8
--- /dev/null
+++ b/go/src/hearts/assets/JoinGame.png
Binary files differ
diff --git a/go/src/hearts/img/staticimg/staticimg.go b/go/src/hearts/img/staticimg/staticimg.go
index c090390..97900a1 100644
--- a/go/src/hearts/img/staticimg/staticimg.go
+++ b/go/src/hearts/img/staticimg/staticimg.go
@@ -37,6 +37,8 @@
dimensions *coords.Vec
// cardHere is used if the StaticImg instance is a drop target
cardHere *card.Card
+ // info contains any additional information contained in the image (eg. game syncgroup address, in a join game button)
+ info []string
}
// Returns the node of s
@@ -78,6 +80,11 @@
return s.cardHere
}
+// Returns the additional info associated with s
+func (s *StaticImg) GetInfo() []string {
+ return s.info
+}
+
// Returns the node of s
func (s *StaticImg) SetNode(n *sprite.Node) {
s.node = n
@@ -115,3 +122,8 @@
func (s *StaticImg) SetCardHere(c *card.Card) {
s.cardHere = c
}
+
+// Sets the additional info of s
+func (s *StaticImg) SetInfo(i []string) {
+ s.info = i
+}
diff --git a/go/src/hearts/img/texture/texture.go b/go/src/hearts/img/texture/texture.go
index 7c28d2d..74f4d0e 100644
--- a/go/src/hearts/img/texture/texture.go
+++ b/go/src/hearts/img/texture/texture.go
@@ -276,7 +276,7 @@
"R-Lower-Gray.png", "S-Lower-Gray.png", "T-Lower-Gray.png", "U-Lower-Gray.png", "V-Lower-Gray.png", "W-Lower-Gray.png",
"X-Lower-Gray.png", "Y-Lower-Gray.png", "Z-Lower-Gray.png", "Space-Gray.png", "RoundedRectangle-DBlue.png",
"RoundedRectangle-LBlue.png", "RoundedRectangle-Gray.png", "Rectangle-LBlue.png", "Rectangle-DBlue.png", "HorizontalPullTab.png",
- "VerticalPullTab.png", "NewGame.png", "NewRound.png",
+ "VerticalPullTab.png", "NewGame.png", "NewRound.png", "JoinGame.png", "Deal.png",
}
for _, f := range boundedImgs {
a, err := asset.Open(f)
diff --git a/go/src/hearts/img/uistate/uistate.go b/go/src/hearts/img/uistate/uistate.go
index a4b7617..02378bc 100644
--- a/go/src/hearts/img/uistate/uistate.go
+++ b/go/src/hearts/img/uistate/uistate.go
@@ -25,14 +25,15 @@
type View string
const (
- None View = "None"
- Opening View = "Opening"
- Pass View = "Pass"
- Take View = "Take"
- Table View = "Table"
- Play View = "Play"
- Score View = "Score"
- Split View = "Split"
+ None View = "None"
+ Opening View = "Opening"
+ Discovery View = "Discovery"
+ Pass View = "Pass"
+ Take View = "Take"
+ Table View = "Table"
+ Play View = "Play"
+ Score View = "Score"
+ Split View = "Split"
)
const (
@@ -82,6 +83,8 @@
Ctx *context.T
Service syncbase.Service
Debug bool
+ Shutdown func()
+ GameID int
}
func MakeUIState() *UIState {
diff --git a/go/src/hearts/img/view/view.go b/go/src/hearts/img/view/view.go
index b33319b..f581e75 100644
--- a/go/src/hearts/img/view/view.go
+++ b/go/src/hearts/img/view/view.go
@@ -11,6 +11,7 @@
import (
"sort"
"strconv"
+ "time"
"hearts/img/coords"
"hearts/img/direction"
@@ -27,23 +28,41 @@
// Opening View: Only temporary, for debugging, while discovery is not integrated
func LoadOpeningView(u *uistate.UIState) {
u.CurView = uistate.Opening
- resetScene(u)
- buttonX := (u.WindowSize.X - u.CardDim.X) / 2
- tableButtonY := (u.WindowSize.Y - 2*u.CardDim.Y - u.Padding) / 2
- passButtonY := tableButtonY + u.CardDim.Y + u.Padding
- tableButtonPos := coords.MakeVec(buttonX, tableButtonY)
- passButtonPos := coords.MakeVec(buttonX, passButtonY)
- tableButtonImage := u.Texs["BakuSquare.png"]
- passButtonImage := u.Texs["Clubs-2.png"]
- u.Buttons = append(u.Buttons, texture.MakeImgWithoutAlt(tableButtonImage, tableButtonPos, u.CardDim, u.Eng, u.Scene))
- u.Buttons = append(u.Buttons, texture.MakeImgWithoutAlt(passButtonImage, passButtonPos, u.CardDim, u.Eng, u.Scene))
+ <-time.After(1 * time.Second)
+ resetImgs(u)
+ ResetScene(u)
+ buttonPos := coords.MakeVec((u.WindowSize.X-2*u.CardDim.X)/2, (u.WindowSize.Y-u.CardDim.Y)/2)
+ buttonDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
+ buttonImage := u.Texs["Deal.png"]
+ u.Buttons = append(u.Buttons, texture.MakeImgWithoutAlt(buttonImage, buttonPos, buttonDim, u.Eng, u.Scene))
+}
+
+func LoadDiscoveryView(discChan chan []string, u *uistate.UIState) {
+ u.CurView = uistate.Discovery
+ resetImgs(u)
+ ResetScene(u)
+ newGameImg := u.Texs["NewGame.png"]
+ newGameDim := coords.MakeVec(2*u.CardDim.X, u.CardDim.Y)
+ newGamePos := coords.MakeVec((u.WindowSize.X-newGameDim.X)/2, u.TopPadding)
+ u.Buttons = append(u.Buttons, texture.MakeImgWithoutAlt(newGameImg, newGamePos, newGameDim, u.Eng, u.Scene))
+ buttonNum := 1
+ go func() {
+ for u.CurView == uistate.Discovery {
+ sgSet := <-discChan
+ joinGameImg := u.Texs["JoinGame.png"]
+ joinGamePos := coords.MakeVec(newGamePos.X, newGamePos.Y+float32(buttonNum)*(newGameDim.Y+u.Padding))
+ u.Buttons = append(u.Buttons, texture.MakeImgWithoutAlt(joinGameImg, joinGamePos, newGameDim, u.Eng, u.Scene))
+ u.Buttons[buttonNum].SetInfo(sgSet)
+ buttonNum++
+ }
+ }()
}
// Table View: Displays the table. Intended for public devices
func LoadTableView(u *uistate.UIState) {
u.CurView = uistate.Table
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
scaler := float32(4)
maxWidth := 4 * u.TableCardDim.X
// adding four drop targets for trick
@@ -251,7 +270,7 @@
func LoadScoreView(roundScores, winners []int, u *uistate.UIState) {
u.CurView = uistate.Score
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
addHeader(u)
addScoreViewHeaderText(u)
addPlayerScores(roundScores, u)
@@ -262,7 +281,7 @@
func LoadPassView(u *uistate.UIState) {
u.CurView = uistate.Pass
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
addHeader(u)
addGrayPassBar(u)
addPassDrops(u)
@@ -276,7 +295,7 @@
func LoadTakeView(u *uistate.UIState) {
u.CurView = uistate.Take
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
addHeader(u)
addGrayTakeBar(u)
addHand(u)
@@ -292,7 +311,7 @@
func LoadPlayView(u *uistate.UIState) {
u.CurView = uistate.Play
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
addPlaySlot(u)
addHand(u)
addPlayHeader(getTurnText(u), false, u)
@@ -308,7 +327,7 @@
func LoadSplitView(reloading bool, u *uistate.UIState) {
u.CurView = uistate.Split
resetImgs(u)
- resetScene(u)
+ ResetScene(u)
addPlayHeader(getTurnText(u), !reloading, u)
addSplitViewPlayerIcons(!reloading, u)
addHand(u)
@@ -801,7 +820,7 @@
u.Other = make([]*staticimg.StaticImg, 0)
}
-func resetScene(u *uistate.UIState) {
+func ResetScene(u *uistate.UIState) {
u.Scene = &sprite.Node{}
u.Eng.Register(u.Scene)
u.Eng.SetTransform(u.Scene, f32.Affine{
diff --git a/go/src/hearts/logic/table/table.go b/go/src/hearts/logic/table/table.go
index f374dc4..74ed5ba 100644
--- a/go/src/hearts/logic/table/table.go
+++ b/go/src/hearts/logic/table/table.go
@@ -40,7 +40,7 @@
heartsBroken: false,
firstTrick: true,
winCondition: 100,
- dir: direction.None,
+ dir: direction.Right,
}
}
diff --git a/go/src/hearts/main.go b/go/src/hearts/main.go
index 65066d4..2dc70cc 100644
--- a/go/src/hearts/main.go
+++ b/go/src/hearts/main.go
@@ -11,12 +11,10 @@
"time"
"v.io/v23"
- "v.io/v23/context"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/syncbase"
"v.io/x/lib/vlog"
- "v.io/x/ref/lib/signals"
"hearts/img/resize"
"hearts/img/texture"
@@ -25,6 +23,7 @@
"hearts/logic/table"
"hearts/syncbase/client"
"hearts/syncbase/server"
+ "hearts/syncbase/util"
"hearts/syncbase/watch"
"hearts/touchhandler"
@@ -84,16 +83,13 @@
}
func onStart(glctx gl.Context, u *uistate.UIState) {
+ flag.Set("v23.credentials", "/sdcard/credentials")
vlog.Log.Configure(vlog.OverridePriorConfiguration(true), vlog.LogToStderr(true))
vlog.Log.Configure(vlog.OverridePriorConfiguration(true), vlog.Level(0))
-
- sgName := "users/emshack@google.com/croupier/syncbase/%%sync/croupiersync"
- contextChan := make(chan *context.T)
- serviceChan := make(chan syncbase.Service)
- go makeServerClient(contextChan, serviceChan)
- // wait for server to generate ctx, client to generate service
- u.Ctx = <-contextChan
- u.Service = <-serviceChan
+ ctx, shutdown := v23.Init()
+ u.Shutdown = shutdown
+ u.Ctx = ctx
+ u.Service = syncbase.NewService(util.MountPoint + "/croupier/" + util.SBName)
namespace := v23.GetNamespace(u.Ctx)
allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
permissions := access.Permissions{
@@ -103,17 +99,19 @@
"Resolve": allAccess,
"Debug": allAccess,
}
- namespace.SetPermissions(u.Ctx, "users/emshack@google.com/croupier", permissions, "")
+ namespace.SetPermissions(u.Ctx, util.MountPoint, permissions, "")
+ namespace.SetPermissions(u.Ctx, util.MountPoint+"/croupier", permissions, "")
u.Service.SetPermissions(u.Ctx, permissions, "")
u.Images = glutil.NewImages(glctx)
fps = debug.NewFPS(u.Images)
u.Eng = glsprite.Engine(u.Images)
u.Texs = texture.LoadTextures(u.Eng)
u.CurTable = table.InitializeGame(u.NumPlayers, u.Texs)
- server.CreateTable(u)
- server.CreateOrJoinSyncgroup(u, sgName)
+ server.CreateTables(u)
// Create watch stream to update game state based on Syncbase updates
go watch.Update(u)
+ go watch.PrintStream("Stream 1", u)
+ go watch.PrintStream("Stream 2", u)
}
func onStop(u *uistate.UIState) {
@@ -121,11 +119,14 @@
fps.Release()
u.Images.Release()
u.Done = true
+ u.Shutdown()
}
func onPaint(glctx gl.Context, sz size.Event, u *uistate.UIState) {
if u.CurView == uistate.None {
- view.LoadOpeningView(u)
+ discChan := make(chan []string)
+ go client.ScanForSG(discChan, u.Ctx)
+ view.LoadDiscoveryView(discChan, u)
}
glctx.ClearColor(1, 1, 1, 1)
glctx.Clear(gl.COLOR_BUFFER_BIT)
@@ -133,22 +134,3 @@
u.Eng.Render(u.Scene, now, sz)
fps.Draw(sz)
}
-
-func makeServerClient(contextChan chan *context.T, serviceChan chan syncbase.Service) {
- flag.Set("v23.credentials", "/sdcard/credentials")
- context, shutdown := v23.Init()
- contextChan <- context
- serviceChan <- client.GetService()
- defer shutdown()
-
- stop := server.Advertise(context)
- <-signals.ShutdownOnSignals(context)
- stop()
-
- // Time for stopping advertisements gracefully like advertising
- // goodbye through mDNS before the program exits.
- //
- // This is not required and the advertised services will be garbage
- // collected by TTL anyway.
- time.Sleep(1 * time.Second)
-}
diff --git a/go/src/hearts/syncbase/client/main.go b/go/src/hearts/syncbase/client/main.go
index 6536a22..880bf04 100644
--- a/go/src/hearts/syncbase/client/main.go
+++ b/go/src/hearts/syncbase/client/main.go
@@ -8,30 +8,103 @@
import (
"fmt"
-
+ "strings"
+ "encoding/json"
"hearts/img/uistate"
- "v.io/v23/syncbase"
+ "hearts/syncbase/util"
+ "v.io/v23/context"
+ "v.io/v23/discovery"
+ wire "v.io/v23/services/syncbase/nosql"
"v.io/v23/syncbase/nosql"
+ ldiscovery "v.io/x/ref/lib/discovery"
+ "v.io/x/ref/lib/discovery/plugins/mdns"
+ "v.io/x/ref/lib/signals"
_ "v.io/x/ref/runtime/factories/generic"
)
-var (
- appName = "x"
- dbName = "y"
- tableName = "z"
-)
-
-func GetService() syncbase.Service {
- service := syncbase.NewService("users/emshack@google.com/croupier/syncbase")
- return service
+// Searches for new syncgroups being advertised, sends found syncgroups to sgChan
+func ScanForSG(sgChan chan []string, ctx *context.T) {
+ mdns, err := mdns.New("")
+ if err != nil {
+ ctx.Fatalf("Plugin failed: %v", err)
+ }
+ ds := ldiscovery.NewWithPlugins([]ldiscovery.Plugin{mdns})
+ fmt.Printf("Start scanning...\n")
+ ch, err := ds.Scan(ctx, "")
+ if err != nil {
+ ctx.Fatalf("Scan failed: %v", err)
+ }
+ instances := make(map[string]string)
+loop:
+ for {
+ select {
+ case update := <-ch:
+ sgNames := GetSG(instances, update)
+ if sgNames != nil {
+ sgChan <- sgNames
+ }
+ case <-signals.ShutdownOnSignals(ctx):
+ break loop
+ }
+ }
}
+// Returns the addresses of any discovered syncgroups that contain croupier game information
+func GetSG(instances map[string]string, update discovery.Update) []string {
+ switch u := update.(type) {
+ case discovery.UpdateFound:
+ found := u.Value
+ instances[string(found.Service.InstanceUuid)] = found.Service.InstanceName
+ fmt.Printf("Discovered %q: Instance=%x, Interface=%q, Addrs=%v\n", found.Service.InstanceName, found.Service.InstanceUuid, found.Service.InterfaceName, found.Service.Addrs)
+ if found.Service.InterfaceName == util.CroupierInterface {
+ return found.Service.Addrs
+ }
+ case discovery.UpdateLost:
+ lost := u.Value
+ name, ok := instances[string(lost.InstanceUuid)]
+ if !ok {
+ name = "unknown"
+ }
+ delete(instances, string(lost.InstanceUuid))
+ fmt.Printf("Lost %q: Instance=%x\n", name, lost.InstanceUuid)
+ }
+ return nil
+}
+
+// Returns a watchstream of the gamelog data
func WatchData(u *uistate.UIState) (nosql.WatchStream, error) {
- db := u.Service.App(appName).NoSQLDatabase(dbName, nil)
+ db := u.Service.App(util.AppName).NoSQLDatabase(util.DbName, nil)
prefix := ""
resumeMarker, err := db.GetResumeMarker(u.Ctx)
if err != nil {
fmt.Println("RESUMEMARKER ERR: ", err)
}
- return db.Watch(u.Ctx, tableName, prefix, resumeMarker)
+ return db.Watch(u.Ctx, util.LogName, prefix, resumeMarker)
+}
+
+// Joins a set of gamelog and game settings syncgroups
+func JoinSyncgroups(ch chan bool, logName, settingsName string, u *uistate.UIState) {
+ fmt.Println("Joining syncgroup")
+ app := u.Service.App(util.AppName)
+ db := app.NoSQLDatabase(util.DbName, nil)
+ logSg := db.Syncgroup(logName)
+ settingsSg := db.Syncgroup(settingsName)
+ myInfoJoiner := wire.SyncgroupMemberInfo{8, false}
+ _, err := logSg.Join(u.Ctx, myInfoJoiner)
+ _, err2 := settingsSg.Join(u.Ctx, myInfoJoiner)
+ if err != nil || err2 != nil {
+ fmt.Println("SYNCGROUP JOIN ERROR: ", err)
+ ch <- false
+ } else {
+ fmt.Println("Syncgroup joined")
+ tmp := strings.Split(logName, "-")
+ lasttmp := tmp[len(tmp)-1]
+ tmpMap := make(map[string]interface{})
+ err = json.Unmarshal([]byte(lasttmp), &tmpMap)
+ if err != nil {
+ fmt.Println("ERROR UNMARSHALLING")
+ }
+ u.GameID = int(tmpMap["gameID"].(float64))
+ ch <- true
+ }
}
diff --git a/go/src/hearts/syncbase/gamelog/logWriter.go b/go/src/hearts/syncbase/gamelog/logWriter.go
index d611caa..d17d163 100644
--- a/go/src/hearts/syncbase/gamelog/logWriter.go
+++ b/go/src/hearts/syncbase/gamelog/logWriter.go
@@ -32,14 +32,14 @@
Bar string = "|"
Space string = " "
Colon string = ":"
- Plus string = "+"
+ Dash string = "-"
End string = "END"
)
// Formats deal command and sends to Syncbase
func LogDeal(u *uistate.UIState, playerIndex int, hands [][]*card.Card) bool {
for i, h := range hands {
- key := getKey(playerIndex)
+ key := getKey(playerIndex, u)
value := Deal + Bar
value += strconv.Itoa(i) + Colon
for _, c := range h {
@@ -56,7 +56,7 @@
// Formats pass command and sends to Syncbase
func LogPass(u *uistate.UIState, cards []*card.Card) bool {
- key := getKey(u.CurPlayerIndex)
+ key := getKey(u.CurPlayerIndex, u)
value := Pass + Bar + strconv.Itoa(u.CurPlayerIndex) + Colon
for _, c := range cards {
value += cardType + Space + c.GetSuit().String() + c.GetFace().String() + Colon
@@ -67,14 +67,14 @@
// Formats take command and sends to Syncbase
func LogTake(u *uistate.UIState) bool {
- key := getKey(u.CurPlayerIndex)
+ key := getKey(u.CurPlayerIndex, u)
value := Take + Bar + strconv.Itoa(u.CurPlayerIndex) + Colon + End
return logKeyValue(u.Service, u.Ctx, key, value)
}
// Formats play command and sends to Syncbase
func LogPlay(u *uistate.UIState, c *card.Card) bool {
- key := getKey(u.CurPlayerIndex)
+ key := getKey(u.CurPlayerIndex, u)
value := Play + Bar + strconv.Itoa(u.CurPlayerIndex) + Colon
value += cardType + Space + c.GetSuit().String() + c.GetFace().String() + Colon + End
return logKeyValue(u.Service, u.Ctx, key, value)
@@ -82,15 +82,15 @@
// Formats ready command and sends to Syncbase
func LogReady(u *uistate.UIState) bool {
- key := getKey(u.CurPlayerIndex)
+ key := getKey(u.CurPlayerIndex, u)
value := Ready + Bar + strconv.Itoa(u.CurPlayerIndex) + Colon + End
return logKeyValue(u.Service, u.Ctx, key, value)
}
// Note: The + is syntax used to replicate the way Croupier in Dart/Flutter writes keys.
-func getKey(playerId int) string {
+func getKey(playerId int, u *uistate.UIState) string {
t := int(time.Now().UnixNano() / 1000000)
- key := strconv.Itoa(t) + Plus + strconv.Itoa(playerId)
+ key := strconv.Itoa(u.GameID) + "/log/" + strconv.Itoa(t) + Dash + strconv.Itoa(playerId)
return key
}
diff --git a/go/src/hearts/syncbase/server/main.go b/go/src/hearts/syncbase/server/main.go
index e4f4fe6..0f17dd8 100644
--- a/go/src/hearts/syncbase/server/main.go
+++ b/go/src/hearts/syncbase/server/main.go
@@ -7,36 +7,51 @@
package server
import (
+ "math/rand"
+ "encoding/json"
"fmt"
-
+ "hearts/img/uistate"
+ "hearts/syncbase/util"
"v.io/v23/context"
+ "v.io/v23/discovery"
"v.io/v23/security"
"v.io/v23/security/access"
wire "v.io/v23/services/syncbase/nosql"
"v.io/v23/syncbase"
+ ldiscovery "v.io/x/ref/lib/discovery"
+ "v.io/x/ref/lib/discovery/plugins/mdns"
+ "v.io/x/ref/lib/signals"
_ "v.io/x/ref/runtime/factories/generic"
-
- "hearts/img/uistate"
)
-var (
- appName = "x"
- dbName = "y"
- tableName = "z"
-)
-
-// Will be fleshed out with addition of discovery
-func Advertise(ctx *context.T) func() {
+// Advertises a set of game log and game settings syncgroups
+func Advertise(logAddress, settingsAddress string, ctx *context.T) {
ctx, stop := context.WithCancel(ctx)
- return stop
+ mdns, err := mdns.New("")
+ if err != nil {
+ ctx.Fatalf("mDNS failed: %v", err)
+ }
+ discoveryService := ldiscovery.NewWithPlugins([]ldiscovery.Plugin{mdns})
+ gameService := discovery.Service{
+ InstanceName: "A sample game service",
+ InterfaceName: util.CroupierInterface,
+ Addrs: []string{settingsAddress, logAddress},
+ }
+ fmt.Println(gameService)
+ if _, err := discoveryService.Advertise(ctx, gameService, nil); err != nil {
+ ctx.Fatalf("Advertise failed: %v", err)
+ }
+ <-signals.ShutdownOnSignals(ctx)
+ stop()
}
// Puts key and value into the syncbase table
func AddKeyValue(service syncbase.Service, ctx *context.T, key, value string) bool {
- app := service.App(appName)
- db := app.NoSQLDatabase(dbName, nil)
- table := db.Table(tableName)
- err := table.Put(ctx, key, value)
+ app := service.App(util.AppName)
+ db := app.NoSQLDatabase(util.DbName, nil)
+ table := db.Table(util.LogName)
+ valueByte := []byte(value)
+ err := table.Put(ctx, key, valueByte)
if err != nil {
fmt.Println("PUT ERROR: ", err)
return false
@@ -44,9 +59,10 @@
return true
}
-// Creates an app, db and table in syncbase if they don't already exist
-func CreateTable(u *uistate.UIState) {
- app := u.Service.App(appName)
+// Creates an app, db, game log table and game settings table in syncbase if they don't already exist
+// Adds appropriate data to settings table
+func CreateTables(u *uistate.UIState) {
+ app := u.Service.App(util.AppName)
if isThere, err := app.Exists(u.Ctx); err != nil {
fmt.Println("APP EXISTS ERROR: ", err)
} else if !isThere {
@@ -54,7 +70,7 @@
fmt.Println("APP ERROR: ", err)
}
}
- db := app.NoSQLDatabase(dbName, nil)
+ db := app.NoSQLDatabase(util.DbName, nil)
if isThere, err := db.Exists(u.Ctx); err != nil {
fmt.Println("DB EXISTS ERROR: ", err)
} else if !isThere {
@@ -62,18 +78,48 @@
fmt.Println("DB ERROR: ", err)
}
}
- table := db.Table(tableName)
- if isThere, err := table.Exists(u.Ctx); err != nil {
+ logTable := db.Table(util.LogName)
+ if isThere, err := logTable.Exists(u.Ctx); err != nil {
fmt.Println("TABLE EXISTS ERROR: ", err)
} else if !isThere {
- if table.Create(u.Ctx, nil) != nil {
+ if logTable.Create(u.Ctx, nil) != nil {
fmt.Println("TABLE ERROR: ", err)
}
}
+ settingsTable := db.Table(util.SettingsName)
+ if isThere, err := settingsTable.Exists(u.Ctx); err != nil {
+ fmt.Println("TABLE EXISTS ERROR: ", err)
+ } else if !isThere {
+ if settingsTable.Create(u.Ctx, nil) != nil {
+ fmt.Println("TABLE ERROR: ", err)
+ }
+ }
+ settingsMap := make(map[string]interface{})
+ settingsMap["userID"] = util.UserID
+ settingsMap["avatar"] = util.UserAvatar
+ settingsMap["name"] = util.UserName
+ settingsMap["color"] = util.UserColor
+ value, err := json.Marshal(settingsMap)
+ if err != nil {
+ fmt.Println("WE HAVE A HUGE PROBLEM:", err)
+ }
+ settingsTable.Put(u.Ctx, fmt.Sprintf("users/%d/settings", util.UserID), value)
}
-// Attempts to join a syncgroup; upon failure, creates a new one
-func CreateOrJoinSyncgroup(u *uistate.UIState, sgName string) {
+// Creates a new syncgroup
+func CreateSyncgroup(ch chan string, u *uistate.UIState) {
+ fmt.Println("Creating Syncgroup")
+ gameID := rand.Intn(1000000)
+ gameMap := make(map[string]interface{})
+ gameMap["type"] = "Hearts"
+ gameMap["playerNumber"] = 0
+ gameMap["gameID"] = gameID
+ gameMap["ownerID"] = util.UserID
+ value, err := json.Marshal(gameMap)
+ if err != nil {
+ fmt.Println("WE HAVE A HUGE PROBLEM:", err)
+ }
+ sgName := util.MountPoint + "/croupier/" + util.SBName + "/%%sync/gaming-" + string(value)
allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
permissions := access.Permissions{
"Admin": allAccess,
@@ -82,29 +128,63 @@
"Resolve": allAccess,
"Debug": allAccess,
}
- app := u.Service.App(appName)
- db := app.NoSQLDatabase(dbName, nil)
+ pref := wire.TableRow{util.LogName, ""}
+ prefs := []wire.TableRow{pref}
+ tables := []string{util.MountPoint + "/croupier"}
+ spec := wire.SyncgroupSpec{
+ Description: "croupier syncgroup",
+ Perms: permissions,
+ Prefixes: prefs,
+ MountTables: tables,
+ IsPrivate: false,
+ }
+ myInfoCreator := wire.SyncgroupMemberInfo{8, true}
+ app := u.Service.App(util.AppName)
+ db := app.NoSQLDatabase(util.DbName, nil)
sg := db.Syncgroup(sgName)
- myInfoJoiner := wire.SyncgroupMemberInfo{8, false}
- _, err := sg.Join(u.Ctx, myInfoJoiner)
- if err == nil {
- fmt.Println("Successfully joined syncgroup")
+ err = sg.Create(u.Ctx, spec, myInfoCreator)
+ if err != nil {
+ fmt.Println("SYNCGROUP CREATE ERROR: ", err)
+ ch <- ""
} else {
- fmt.Printf("err: %v\n", err)
- fmt.Println("Creating Syncgroup")
- pref := wire.TableRow{tableName, ""}
- prefs := []wire.TableRow{pref}
- tables := []string{"/ns.dev.v.io:8101/users/emshack@google.com/croupier"}
- spec := wire.SyncgroupSpec{
- Description: "croupier syncgroup",
- Perms: permissions,
- Prefixes: prefs,
- MountTables: tables,
- IsPrivate: false}
- myInfoCreator := wire.SyncgroupMemberInfo{8, true}
- err := sg.Create(u.Ctx, spec, myInfoCreator)
- if err != nil {
- fmt.Println("SYNCGROUP CREATE ERROR: ", err)
- }
+ fmt.Println("Syncgroup created")
+ u.GameID = gameID
+ ch <- sgName
+ }
+}
+
+// Creates a new syncgroup
+func CreateSettingsSyncgroup(ch chan string, u *uistate.UIState) {
+ fmt.Println("Creating Settings Syncgroup")
+ sgName := fmt.Sprintf("%s/croupier/%s/%%%%sync/discovery-%d", util.MountPoint, util.SBName, util.UserID)
+ allAccess := access.AccessList{In: []security.BlessingPattern{"..."}}
+ permissions := access.Permissions{
+ "Admin": allAccess,
+ "Write": allAccess,
+ "Read": allAccess,
+ "Resolve": allAccess,
+ "Debug": allAccess,
+ }
+ pref := wire.TableRow{util.SettingsName, ""}
+ prefs := []wire.TableRow{pref}
+ tables := []string{util.MountPoint + "/croupier"}
+ spec := wire.SyncgroupSpec{
+ Description: "croupier syncgroup",
+ Perms: permissions,
+ Prefixes: prefs,
+ MountTables: tables,
+ IsPrivate: false,
+ }
+ myInfoCreator := wire.SyncgroupMemberInfo{8, true}
+ app := u.Service.App(util.AppName)
+ db := app.NoSQLDatabase(util.DbName, nil)
+ sg := db.Syncgroup(sgName)
+ err := sg.Create(u.Ctx, spec, myInfoCreator)
+ if err != nil {
+ fmt.Println("SYNCGROUP CREATE ERROR: ", err)
+ ch <- ""
+ } else {
+ fmt.Println("Syncgroup created")
+ ch <- sgName
}
}
diff --git a/go/src/hearts/syncbase/util/util.go b/go/src/hearts/syncbase/util/util.go
new file mode 100644
index 0000000..9581f90
--- /dev/null
+++ b/go/src/hearts/syncbase/util/util.go
@@ -0,0 +1,23 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// util.go stores constants relevant to the syncbase hierarchy
+
+package util
+
+const (
+ // switch back to my mountpoint with the following code:
+ // MountPoint = "users/emshack@google.com"
+ MountPoint = "/192.168.86.254:8101"
+ UserID = 123
+ UserColor = 16777215
+ UserAvatar = "Club.png"
+ UserName = "Croupier Go Player (the blue)"
+ SBName = "syncbase1"
+ AppName = "app"
+ DbName = "db"
+ LogName = "games"
+ SettingsName = "table_settings"
+ CroupierInterface = "CroupierSettingsAndGame"
+)
diff --git a/go/src/hearts/syncbase/watch/watch.go b/go/src/hearts/syncbase/watch/watch.go
index db2f4fc..e2fc0ad 100644
--- a/go/src/hearts/syncbase/watch/watch.go
+++ b/go/src/hearts/syncbase/watch/watch.go
@@ -24,6 +24,26 @@
"v.io/v23/syncbase/nosql"
)
+func PrintStream(prefix string, u *uistate.UIState) {
+ stream, err := client.WatchData(u)
+ if err != nil {
+ fmt.Println("WatchData error:", err)
+ }
+ for {
+ if updateExists := stream.Advance(); updateExists {
+ c := stream.Change()
+ if c.ChangeType == nosql.PutChange {
+ var value []byte
+ if err := c.Value(&value); err != nil {
+ fmt.Println("Value error:", err)
+ }
+ valueStr := string(value)
+ fmt.Println(prefix, valueStr)
+ }
+ }
+ }
+}
+
func Update(u *uistate.UIState) {
stream, err := client.WatchData(u)
if err != nil {
@@ -33,27 +53,30 @@
if updateExists := stream.Advance(); updateExists {
c := stream.Change()
if c.ChangeType == nosql.PutChange {
- var value string
+ var value []byte
if err := c.Value(&value); err != nil {
fmt.Println("Value error:", err)
}
- updateType := strings.Split(value, "|")[0]
+ valueStr := string(value)
+ fmt.Println(valueStr)
+ updateType := strings.Split(valueStr, "|")[0]
switch updateType {
case gamelog.Deal:
- onDeal(value, u)
+ onDeal(valueStr, u)
case gamelog.Pass:
- onPass(value, u)
+ onPass(valueStr, u)
case gamelog.Take:
- onTake(value, u)
+ onTake(valueStr, u)
case gamelog.Play:
- onPlay(value, u)
+ onPlay(valueStr, u)
case gamelog.Ready:
- onReady(value, u)
+ onReady(valueStr, u)
}
} else {
fmt.Println("Unexpected ChangeType: ", c.ChangeType)
}
}
+ fmt.Println(stream.Err())
}
}
diff --git a/go/src/hearts/touchhandler/touchhandler.go b/go/src/hearts/touchhandler/touchhandler.go
index 2d8de91..5adfe92 100644
--- a/go/src/hearts/touchhandler/touchhandler.go
+++ b/go/src/hearts/touchhandler/touchhandler.go
@@ -16,11 +16,18 @@
"hearts/img/uistate"
"hearts/img/view"
"hearts/logic/card"
+ "hearts/syncbase/client"
"hearts/syncbase/gamelog"
+ "hearts/syncbase/server"
)
func OnTouch(t touch.Event, u *uistate.UIState) {
switch u.CurView {
+ case uistate.Discovery:
+ switch t.Type {
+ case touch.TypeBegin:
+ beginClickDiscovery(t, u)
+ }
case uistate.Opening:
switch t.Type {
case touch.TypeBegin:
@@ -93,14 +100,44 @@
u.LastMouseXY.Y = t.Y
}
+func beginClickDiscovery(t touch.Event, u *uistate.UIState) {
+ buttonList := findClickedButton(t, u)
+ if len(buttonList) > 0 {
+ if buttonList[0] == u.Buttons[0] {
+ logChan := make(chan string)
+ settingsChan := make(chan string)
+ go server.CreateSyncgroup(logChan, u)
+ go server.CreateSettingsSyncgroup(settingsChan, u)
+ logName := <-logChan
+ settingsName := <-settingsChan
+ if logName != "" && settingsName != "" {
+ go server.Advertise(logName, settingsName, u.Ctx)
+ view.LoadOpeningView(u)
+ }
+ } else {
+ for _, b := range u.Buttons {
+ if buttonList[0] == b {
+ joinDone := make(chan bool)
+ settingsAddr := b.GetInfo()[0]
+ logAddr := b.GetInfo()[1]
+ go client.JoinSyncgroups(joinDone, logAddr, settingsAddr, u)
+ if success := <-joinDone; success {
+ view.LoadOpeningView(u)
+ } else {
+ fmt.Println("Failed to join")
+ }
+ }
+ }
+ }
+ }
+}
+
func beginClickOpening(t touch.Event, u *uistate.UIState) {
buttonList := findClickedButton(t, u)
if len(buttonList) > 0 {
- if u.CurTable.GetPlayers()[0].GetHand() == nil {
- fmt.Println("Dealing")
- allHands := u.CurTable.Deal()
- gamelog.LogDeal(u, u.CurPlayerIndex, allHands)
- }
+ fmt.Println("Dealing")
+ allHands := u.CurTable.Deal()
+ gamelog.LogDeal(u, u.CurPlayerIndex, allHands)
}
}