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)
 	}
 }