| // 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. |
| |
| // server handles advertising (to be fleshed out when discovery is added), and all local syncbase updates |
| |
| package sync |
| |
| import ( |
| "encoding/json" |
| "fmt" |
| "math/rand" |
| "strconv" |
| "strings" |
| |
| "hearts/img/uistate" |
| |
| "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" |
| ) |
| |
| // Advertises a set of game log and game settings syncgroups |
| func Advertise(logAddress, settingsAddress, gameStartData string, quit chan bool, ctx *context.T) { |
| ctx, stop := context.WithCancel(ctx) |
| 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: CroupierInterface, |
| Attrs: map[string]string{"settings_sgname": settingsAddress, "game_start_data": gameStartData}, |
| Addrs: []string{logAddress}, |
| } |
| if _, err := discoveryService.Advertise(ctx, &gameService, nil); err != nil { |
| ctx.Fatalf("Advertise failed: %v", err) |
| } |
| select { |
| case <-signals.ShutdownOnSignals(ctx): |
| stop() |
| case <-quit: |
| stop() |
| } |
| } |
| |
| // Puts key and value into the syncbase gamelog 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(LogName) |
| valueByte := []byte(value) |
| err := table.Put(ctx, key, valueByte) |
| if err != nil { |
| fmt.Println("PUT ERROR: ", err) |
| return false |
| } |
| return true |
| } |
| |
| // 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(AppName) |
| if isThere, err := app.Exists(u.Ctx); err != nil { |
| fmt.Println("APP EXISTS ERROR: ", err) |
| } else if !isThere { |
| if app.Create(u.Ctx, nil) != nil { |
| fmt.Println("APP ERROR: ", err) |
| } |
| } |
| db := app.NoSQLDatabase(DbName, nil) |
| if isThere, err := db.Exists(u.Ctx); err != nil { |
| fmt.Println("DB EXISTS ERROR: ", err) |
| } else if !isThere { |
| if db.Create(u.Ctx, nil) != nil { |
| fmt.Println("DB ERROR: ", err) |
| } |
| } |
| logTable := db.Table(LogName) |
| if isThere, err := logTable.Exists(u.Ctx); err != nil { |
| fmt.Println("TABLE EXISTS ERROR: ", err) |
| } else if !isThere { |
| if logTable.Create(u.Ctx, nil) != nil { |
| fmt.Println("TABLE ERROR: ", err) |
| } |
| } |
| settingsTable := db.Table(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) |
| } |
| } |
| // Add user settings data to represent this player |
| settingsMap := make(map[string]interface{}) |
| settingsMap["userID"] = UserID |
| settingsMap["avatar"] = UserAvatar |
| settingsMap["name"] = UserName |
| settingsMap["color"] = UserColor |
| u.UserData[UserID] = settingsMap |
| 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", UserID), value) |
| } |
| |
| // Creates a new gamelog syncgroup |
| func CreateLogSyncgroup(ch chan string, u *uistate.UIState) { |
| fmt.Println("Creating Log Syncgroup") |
| u.IsOwner = true |
| // Generate random gameID information to advertise this game |
| gameID := rand.Intn(1000000) |
| u.GameID = gameID |
| gameMap := make(map[string]interface{}) |
| gameMap["type"] = "Hearts" |
| gameMap["playerNumber"] = 0 |
| gameMap["gameID"] = gameID |
| gameMap["ownerID"] = UserID |
| value, err := json.Marshal(gameMap) |
| if err != nil { |
| fmt.Println("WE HAVE A HUGE PROBLEM:", err) |
| } |
| ch <- string(value) |
| // Create gamelog syncgroup |
| logSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/gaming-%d", MountPoint, SBName, gameID) |
| allAccess := access.AccessList{In: []security.BlessingPattern{"..."}} |
| permissions := access.Permissions{ |
| "Admin": allAccess, |
| "Write": allAccess, |
| "Read": allAccess, |
| "Resolve": allAccess, |
| "Debug": allAccess, |
| } |
| logPref := wire.TableRow{LogName, fmt.Sprintf("%d", u.GameID)} |
| logPrefs := []wire.TableRow{logPref} |
| tables := []string{MountPoint + "/croupier"} |
| logSpec := wire.SyncgroupSpec{ |
| Description: "croupier syncgroup", |
| Perms: permissions, |
| Prefixes: logPrefs, |
| MountTables: tables, |
| IsPrivate: false, |
| } |
| myInfoCreator := wire.SyncgroupMemberInfo{8, true} |
| app := u.Service.App(AppName) |
| db := app.NoSQLDatabase(DbName, nil) |
| logSG := db.Syncgroup(logSGName) |
| err = logSG.Create(u.Ctx, logSpec, myInfoCreator) |
| if err != nil { |
| fmt.Println("SYNCGROUP CREATE ERROR: ", err) |
| fmt.Println("JOINING INSTEAD...") |
| _, err2 := logSG.Join(u.Ctx, myInfoCreator) |
| if err2 != nil { |
| fmt.Println("SYNCGROUP JOIN ERROR: ", err2) |
| ch <- "" |
| } else { |
| ch <- logSGName |
| } |
| } else { |
| fmt.Println("Syncgroup created") |
| if logSGName != u.LogSG { |
| resetGame(logSGName, true, u) |
| } |
| ch <- logSGName |
| } |
| } |
| |
| // Creates a new user settings syncgroup |
| func CreateSettingsSyncgroup(ch chan string, u *uistate.UIState) { |
| fmt.Println("Creating Settings Syncgroup") |
| allAccess := access.AccessList{In: []security.BlessingPattern{"..."}} |
| permissions := access.Permissions{ |
| "Admin": allAccess, |
| "Write": allAccess, |
| "Read": allAccess, |
| "Resolve": allAccess, |
| "Debug": allAccess, |
| } |
| tables := []string{MountPoint + "/croupier"} |
| myInfoCreator := wire.SyncgroupMemberInfo{8, true} |
| app := u.Service.App(AppName) |
| db := app.NoSQLDatabase(DbName, nil) |
| settingsSGName := fmt.Sprintf("%s/croupier/%s/%%%%sync/discovery-%d", MountPoint, SBName, UserID) |
| settingsPref := wire.TableRow{SettingsName, fmt.Sprintf("users/%d", UserID)} |
| settingsPrefs := []wire.TableRow{settingsPref} |
| settingsSpec := wire.SyncgroupSpec{ |
| Description: "croupier syncgroup", |
| Perms: permissions, |
| Prefixes: settingsPrefs, |
| MountTables: tables, |
| IsPrivate: false, |
| } |
| settingsSG := db.Syncgroup(settingsSGName) |
| err := settingsSG.Create(u.Ctx, settingsSpec, myInfoCreator) |
| if err != nil { |
| fmt.Println("SYNCGROUP CREATE ERROR: ", err) |
| fmt.Println("JOINING INSTEAD...") |
| _, err2 := settingsSG.Join(u.Ctx, myInfoCreator) |
| if err2 != nil { |
| fmt.Println("SYNCGROUP JOIN ERROR: ", err2) |
| ch <- "" |
| } else { |
| ch <- settingsSGName |
| } |
| } else { |
| fmt.Println("Syncgroup created") |
| ch <- settingsSGName |
| } |
| } |
| |
| func resetGame(logName string, creator bool, u *uistate.UIState) { |
| u.PlayerData = make(map[int]int) |
| u.CurPlayerIndex = -1 |
| u.LogSG = logName |
| if !creator { |
| tmp := strings.Split(logName, "-") |
| gameID, _ := strconv.Atoi(tmp[len(tmp)-1]) |
| u.GameID = gameID |
| } |
| go UpdateGame(u) |
| } |