backend token logic

This commit is contained in:
Iris Lightshard 2023-07-07 23:56:41 -06:00
parent f4513a28f7
commit 01fccb55f0
Signed by: Iris Lightshard
GPG key ID: 3B7FBC22144E6398
11 changed files with 313 additions and 132 deletions

View file

@ -48,8 +48,7 @@ func apiGetTableData(next http.Handler, udb auth.UserStore, dbAdapter mongodb.Db
if dbAdapter.CheckTable(tableKey) { if dbAdapter.CheckTable(tableKey) {
mapUrl, _ := dbAdapter.GetMapImageUrl(tableKey) mapUrl, _ := dbAdapter.GetMapImageUrl(tableKey)
auxMessage, _ := dbAdapter.GetAuxMessage(tableKey) auxMessage, _ := dbAdapter.GetAuxMessage(tableKey)
availableTokens, _ := dbAdapter.GetTokens(tableKey, true) tokens, _ := dbAdapter.GetTokens(tableKey, true)
activeTokens, _ := dbAdapter.GetTokens(tableKey, false)
diceRolls, _ := dbAdapter.GetDiceRolls(tableKey) diceRolls, _ := dbAdapter.GetDiceRolls(tableKey)
AddContextValue(req, "tableData", models.Table{ AddContextValue(req, "tableData", models.Table{
@ -57,8 +56,7 @@ func apiGetTableData(next http.Handler, udb auth.UserStore, dbAdapter mongodb.Db
Passcode: tableKey.Passcode, Passcode: tableKey.Passcode,
DiceRolls: diceRolls, DiceRolls: diceRolls,
MapImageUrl: mapUrl, MapImageUrl: mapUrl,
Tokens: activeTokens, Tokens: tokens,
AvailableTokens: availableTokens,
AuxMessage: auxMessage, AuxMessage: auxMessage,
}) })
} else { } else {

View file

@ -192,8 +192,7 @@ func (self *GameTableServer) getCurrentState(tableKey models.TableKey) []byte {
if self.dbAdapter.CheckTable(tableKey) { if self.dbAdapter.CheckTable(tableKey) {
mapUrl, _ := self.dbAdapter.GetMapImageUrl(tableKey) mapUrl, _ := self.dbAdapter.GetMapImageUrl(tableKey)
auxMessage, _ := self.dbAdapter.GetAuxMessage(tableKey) auxMessage, _ := self.dbAdapter.GetAuxMessage(tableKey)
availableTokens, _ := self.dbAdapter.GetTokens(tableKey, true) tokens, _ := self.dbAdapter.GetTokens(tableKey, false)
activeTokens, _ := self.dbAdapter.GetTokens(tableKey, false)
diceRolls, _ := self.dbAdapter.GetDiceRolls(tableKey) diceRolls, _ := self.dbAdapter.GetDiceRolls(tableKey)
table := models.Table{ table := models.Table{
@ -201,8 +200,7 @@ func (self *GameTableServer) getCurrentState(tableKey models.TableKey) []byte {
Passcode: tableKey.Passcode, Passcode: tableKey.Passcode,
DiceRolls: diceRolls, DiceRolls: diceRolls,
MapImageUrl: mapUrl, MapImageUrl: mapUrl,
Tokens: activeTokens, Tokens: tokens,
AvailableTokens: availableTokens,
AuxMessage: auxMessage, AuxMessage: auxMessage,
} }
data, err := json.Marshal(table) data, err := json.Marshal(table)
@ -223,8 +221,41 @@ func (self *GameTableServer) writeToDB(tableMsg *models.TableMessage) error {
return err return err
} }
} }
if tableMsg.Token != nil && tableMsg.Token.Id != nil {
t := *tableMsg.Token
exists, active := self.dbAdapter.CheckToken(key, *t.Id)
if exists {
if active {
if !t.Active {
err := self.dbAdapter.ActivateToken(key, *t.Id, false)
if err != nil {
return err
}
tableMsg.Token.X = nil
tableMsg.Token.Y = nil
} else if t.X != nil && t.Y != nil {
err := self.dbAdapter.MoveToken(key, t)
if err != nil {
return err
}
}
} else {
if t.Active {
err := self.dbAdapter.ActivateToken(key, *t.Id, true)
if err != nil {
return err
}
}
}
} else {
// respond to nonextant IDs as if they were destroyed
tableMsg.Token.X = nil
tableMsg.Token.Y = nil
tableMsg.Token.Active = false
}
}
// map image change, aux message, and token addition/removal require admin authorization // map image change, aux message, and token creation/deletion require admin authorization
if tableMsg.Auth != nil { if tableMsg.Auth != nil {
authorized, _ := self.udb.ValidateToken(*tableMsg.Auth) authorized, _ := self.udb.ValidateToken(*tableMsg.Auth)
if authorized { if authorized {
@ -240,6 +271,22 @@ func (self *GameTableServer) writeToDB(tableMsg *models.TableMessage) error {
return err return err
} }
} }
if tableMsg.Token != nil {
t := *tableMsg.Token
if t.Id == nil {
id, err := self.dbAdapter.CreateToken(key, t)
if err == nil {
*tableMsg.Token.Id = id
} else {
return err
}
} else {
if t.X == nil && t.Y == nil && !t.Active {
err := self.dbAdapter.DestroyToken(key, *t.Id)
return err
}
}
}
} }
} }
return nil return nil

10
go.mod
View file

@ -3,7 +3,7 @@ module hacklab.nilfm.cc/felt
go 1.19 go 1.19
require ( require (
go.mongodb.org/mongo-driver v1.11.0 go.mongodb.org/mongo-driver v1.12.0
golang.org/x/time v0.1.0 golang.org/x/time v0.1.0
hacklab.nilfm.cc/quartzgun v0.3.0 hacklab.nilfm.cc/quartzgun v0.3.0
nhooyr.io/websocket v1.8.7 nhooyr.io/websocket v1.8.7
@ -15,10 +15,10 @@ require (
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.7.0 // indirect
) )

30
go.sum
View file

@ -55,6 +55,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -68,31 +69,60 @@ github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE= go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE=
go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8= go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View file

@ -18,11 +18,16 @@ type DiceRoll struct {
} }
type Token struct { type Token struct {
Id string `json:"id"` Id *string `json:"id" bson:"_id"`
Name string `json:"name"` Name string `json:"name"`
SpriteUri string `json:"spriteUrl"` Sprite string `json:"sprite"`
W int `json:"w"`
H int `json:"h"`
OX int `json:"oX"`
OY int `json:"oY"`
X *int `json:"x"` X *int `json:"x"`
Y *int `json:"y"` Y *int `json:"y"`
Active bool `json:"active"`
} }
type Table struct { type Table struct {
@ -31,7 +36,6 @@ type Table struct {
MapImageUrl string `json:"mapImg"` MapImageUrl string `json:"mapImg"`
DiceRolls []DiceRoll `json:"diceRolls"` DiceRolls []DiceRoll `json:"diceRolls"`
Tokens []Token `json:"tokens"` Tokens []Token `json:"tokens"`
AvailableTokens []Token `json:"availableTokens"`
AuxMessage string `json:"auxMsg"` AuxMessage string `json:"auxMsg"`
} }

View file

@ -5,6 +5,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/options"
"hacklab.nilfm.cc/felt/models" "hacklab.nilfm.cc/felt/models"
@ -14,7 +15,9 @@ import (
const errNoCollection string = "collection not found: felt.%s" const errNoCollection string = "collection not found: felt.%s"
const errNoDocument string = "document with name/id '%s' doesn't exist in collection: %s" const errNoDocument string = "document with name/id '%s' doesn't exist in collection: %s"
const errNotAString string = "document property is not a string: %s<key=%s>.%s" const errNotAString string = "document property is not a string: %s<key=%s>.%s"
const errNotAnArray string = "doccument property is not an array: %s<key=%s>.%s" const errNotAnArray string = "document property is not an array: %s<key=%s>.%s"
const ErrNotFound string = "this token doesn't exist at this table; forget about it"
type DbAdapter interface { type DbAdapter interface {
Init(mongoUri string) error Init(mongoUri string) error
@ -34,10 +37,12 @@ type DbAdapter interface {
SetAuxMessage(table models.TableKey, message string) error SetAuxMessage(table models.TableKey, message string) error
GetAuxMessage(table models.TableKey) (string, error) GetAuxMessage(table models.TableKey) (string, error)
AddToken(table models.TableKey, token models.Token, active bool) error CheckToken(table models.TableKey, tokenId string) (bool, bool)
RemoveToken(table models.TableKey, tokenId string, active bool) error CreateToken(table models.TableKey, token models.Token) (string, error)
ModifyToken(table models.TableKey, token models.Token, active bool) error ActivateToken(table models.TableKey, tokenId string, active bool) error
GetTokens(table models.TableKey, active bool) ([]models.Token, error) MoveToken(table models.TableKey, token models.Token) error
DestroyToken(table models.TableKey, tokenId string) error
GetTokens(table models.TableKey, activeOnly bool) ([]models.Token, error)
} }
type DbEngine struct { type DbEngine struct {
@ -284,13 +289,37 @@ func (self *DbEngine) GetAuxMessage(table models.TableKey) (string, error) {
return "", errors.New(fmt.Sprintf(errNoCollection, "tables")) return "", errors.New(fmt.Sprintf(errNoCollection, "tables"))
} }
func (self *DbEngine) AddToken(table models.TableKey, token models.Token, active bool) error { func (self *DbEngine) CheckToken(table models.TableKey, tokenId string) (bool, bool) {
mongoId, err := primitive.ObjectIDFromHex(tokenId)
if err != nil {
return false, false
}
tables := self.db.Collection("tables") tables := self.db.Collection("tables")
if tables != nil { if tables != nil {
tokenArrKey := "tokens" result := models.Table{}
if !active { err := tables.FindOne(self.mkCtx(10), bson.D{
tokenArrKey = "availableTokens" {"name", table.Name},
{"passcode", table.Passcode},
{"tokens", bson.E{"_id", mongoId}},
}).Decode(&result)
if err != nil {
return false, false
} else {
active := false
for _, t := range result.Tokens {
if *t.Id == tokenId && t.Active {
active = true
} }
}
return true, active
}
}
return false, false
}
func (self *DbEngine) CreateToken(table models.TableKey, token models.Token) (string, error) {
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table var result models.Table
err := tables.FindOneAndUpdate( err := tables.FindOneAndUpdate(
self.mkCtx(10), self.mkCtx(10),
@ -299,7 +328,39 @@ func (self *DbEngine) AddToken(table models.TableKey, token models.Token, active
{"passcode", table.Passcode}, {"passcode", table.Passcode},
}, },
bson.D{ bson.D{
{"$push", bson.D{{tokenArrKey, token}}}, {"$push", bson.D{{"tokens", token}}},
},
).Decode(&result)
if err == nil {
newId := result.Tokens[len(result.Tokens)-1].Id
return *newId, nil
} else {
return "", err
}
}
return "", errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) ActivateToken(table models.TableKey, tokenId string, active bool) error {
mongoId, err := primitive.ObjectIDFromHex(tokenId)
if err != nil {
return err
}
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table
err := tables.FindOneAndUpdate(
self.mkCtx(10),
bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
{"tokens", bson.E{"_id", mongoId}},
},
bson.D{
{"$set", bson.D{{"tokens.$", bson.D{
{"active", active},
}}}},
}, },
).Decode(&result) ).Decode(&result)
return err return err
@ -307,48 +368,23 @@ func (self *DbEngine) AddToken(table models.TableKey, token models.Token, active
return errors.New(fmt.Sprintf(errNoCollection, "tables")) return errors.New(fmt.Sprintf(errNoCollection, "tables"))
} }
func (self *DbEngine) RemoveToken(table models.TableKey, tokenId string, active bool) error { func (self *DbEngine) MoveToken(table models.TableKey, token models.Token) error {
tables := self.db.Collection("tables") mongoId, err := primitive.ObjectIDFromHex(*token.Id)
if tables != nil { if err != nil {
tokenArrKey := "tokens"
if !active {
tokenArrKey = "availableTokens"
}
var result models.Table
err := tables.FindOneAndUpdate(
self.mkCtx(10),
bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
},
bson.D{
{"$pull", bson.D{{tokenArrKey, bson.D{{"_id", tokenId}}}}},
},
).Decode(&result)
return err return err
} }
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) ModifyToken(table models.TableKey, token models.Token, active bool) error {
tables := self.db.Collection("tables") tables := self.db.Collection("tables")
if tables != nil { if tables != nil {
tokenArrKey := "tokens"
if !active {
tokenArrKey = "availableTokens"
}
var result models.Table var result models.Table
err := tables.FindOneAndUpdate( err := tables.FindOneAndUpdate(
self.mkCtx(10), self.mkCtx(10),
bson.D{ bson.D{
{"name", table.Name}, {"name", table.Name},
{"passcode", table.Passcode}, {"passcode", table.Passcode},
{tokenArrKey, bson.E{"_id", token.Id}}, {"tokens", bson.E{"_id", mongoId}},
}, },
bson.D{ bson.D{
{"$set", bson.D{{tokenArrKey + ".$", bson.D{ {"$set", bson.D{{"tokens.$", bson.D{
{"name", token.Name},
{"spriteUri", token.SpriteUri},
{"x", token.X}, {"x", token.X},
{"y", token.Y}, {"y", token.Y},
}}}}, }}}},
@ -359,7 +395,30 @@ func (self *DbEngine) ModifyToken(table models.TableKey, token models.Token, act
return errors.New(fmt.Sprintf(errNoCollection, "tables")) return errors.New(fmt.Sprintf(errNoCollection, "tables"))
} }
func (self *DbEngine) GetTokens(table models.TableKey, active bool) ([]models.Token, error) { func (self *DbEngine) DestroyToken(table models.TableKey, tokenId string) error {
mongoId, err := primitive.ObjectIDFromHex(tokenId)
if err != nil {
return err
}
tables := self.db.Collection("tables")
if tables != nil {
var result models.Table
err := tables.FindOneAndUpdate(
self.mkCtx(10),
bson.D{
{"name", table.Name},
{"passcode", table.Passcode},
},
bson.D{
{"$pull", bson.D{{"tokens", bson.D{{"_id", mongoId}}}}},
},
).Decode(&result)
return err
}
return errors.New(fmt.Sprintf(errNoCollection, "tables"))
}
func (self *DbEngine) GetTokens(table models.TableKey, activeOnly bool) ([]models.Token, error) {
tables := self.db.Collection("tables") tables := self.db.Collection("tables")
if tables != nil { if tables != nil {
var result models.Table var result models.Table
@ -370,11 +429,14 @@ func (self *DbEngine) GetTokens(table models.TableKey, active bool) ([]models.To
{"passcode", table.Passcode}, {"passcode", table.Passcode},
}).Decode(&result) }).Decode(&result)
if err == nil { if err == nil {
if active { tokens := []models.Token{}
return result.Tokens, nil for _, t := range result.Tokens {
} else { if !activeOnly || t.Active {
return result.AvailableTokens, nil tokens = append(tokens, t)
} }
}
return tokens, nil
} else { } else {
return nil, errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables")) return nil, errors.New(fmt.Sprintf(errNoDocument, table.Name, "tables"))
} }

View file

@ -16,6 +16,7 @@ const tokenCY = document.getElementById("token_cy");
const previewZone = document.getElementById("tokenPreview_zone"); const previewZone = document.getElementById("tokenPreview_zone");
const tokenAspect = document.getElementById("tokenKeepAspect"); const tokenAspect = document.getElementById("tokenKeepAspect");
const aspectLockLabel = document.getElementById("aspectLockLabel"); const aspectLockLabel = document.getElementById("aspectLockLabel");
const tokenZone = document.getElementById("tokenZone");
async function getTable(name, pass) { async function getTable(name, pass) {
try { try {
@ -41,8 +42,9 @@ async function getTable(name, pass) {
document.getElementById("input_table_name").value = name; document.getElementById("input_table_name").value = name;
document.getElementById("input_table_pass").value = pass; document.getElementById("input_table_pass").value = pass;
dial(); dial();
const table = await res.json()
infoHtml = "<a href='#' onclick='getTables()'>&larr; table list</a><br>"; infoHtml = "<a href='#' onclick='getTables()'>&larr; table list</a><br>";
infoHtml += `<textarea id='auxMsgZone'>${(await res.json()).auxMsg}</textarea><br><button onclick='publishAuxMsg()'>Set Status</button>` infoHtml += `<textarea id='auxMsgZone'>${table.auxMsg}</textarea><br><button onclick='publishAuxMsg()'>Set Status</button>`
infoHtml += "<button onclick='destroyTable()'>Destroy Table</button><br/>"; infoHtml += "<button onclick='destroyTable()'>Destroy Table</button><br/>";
infoHtml += "<input id='map_img_upload' type='file'/><button onclick='uploadMapImg()'>Upload Map</button><br/>" infoHtml += "<input id='map_img_upload' type='file'/><button onclick='uploadMapImg()'>Upload Map</button><br/>"
if (mapImgs.ok) { if (mapImgs.ok) {
@ -70,18 +72,14 @@ async function getTable(name, pass) {
} }
tokenListHTML += "</ul>"; tokenListHTML += "</ul>";
fillSpriteDropdown(tokens); fillSpriteDropdown(tokens);
redrawTokenMasterList();
} else { } else {
tokenListHTML += "<label>Sprites couldn't be retrieved</label>" tokenListHTML += "<label>Sprites couldn't be retrieved</label>"
} }
spriteZone.innerHTML = tokenListHTML; spriteZone.innerHTML = tokenListHTML;
tokenWrapper.style.display = "inline"; tokenWrapper.style.display = "inline";
// also, we have to fill and toggle the tokens window
} else { } else {
console.log(res.status); console.log(res.status);
} }
@ -90,6 +88,15 @@ async function getTable(name, pass) {
} }
} }
function redrawTokenMasterList() {
if (tokenZone) {
const headers = new Headers();
headers.set('Authorization', 'Bearer ' + adminToken.access_token);
const res = await fetch(`/`
}
}
function fillSpriteDropdown(tokens) { function fillSpriteDropdown(tokens) {
let options = "<option value=''>select</option>"; let options = "<option value=''>select</option>";
for (const t of tokens) { for (const t of tokens) {
@ -127,7 +134,6 @@ function toggleAspectLock() {
function scaleSpritePreview(source) { function scaleSpritePreview(source) {
if (mapImg && mapImg._image) { if (mapImg && mapImg._image) {
console.log(mapImg);
const scaleFactor = mapImg._image.clientWidth / mapImg._image.naturalWidth; const scaleFactor = mapImg._image.clientWidth / mapImg._image.naturalWidth;
const keepAspect = tokenAspect.checked; const keepAspect = tokenAspect.checked;
const img = previewZone.children[0]; const img = previewZone.children[0];
@ -162,19 +168,20 @@ function drawTokenOrigin() {
const img = previewZone.children[0]; const img = previewZone.children[0];
const x = Number(tokenWidth.value) / Number(tokenCX.value); const x = Number(tokenWidth.value) / Number(tokenCX.value);
const y = Number(tokenHeight.value) / Number(tokenCY.value); const y = Number(tokenHeight.value) / Number(tokenCY.value);
const origin = {x: img.width/x, y: img.height/y}; const origin = {x: img.width/x, y: img.height/y};
const originImg = document.createElement("img"); const originImg = document.createElement("img");
originImg.src="/table/origin.png"; originImg.src="/table/origin.png";
originImg.style.position = "absolute"; originImg.style.position = "absolute";
originImg.style.left = (origin.x - 2) + "px"; originImg.style.left = (origin.x - 2) + "px";
originImg.style.top = (origin.y - 2) + "px"; originImg.style.top = (origin.y - 2) + "px";
if (previewZone.children.length > 1) { if (previewZone.children.length > 1) {
previewZone.replaceChild(originImg, previewZone.children[1]); previewZone.replaceChild(originImg, previewZone.children[1]);
} else { } else {
previewZone.appendChild(originImg); previewZone.appendChild(originImg);
} }
} }
function reinitializeSpritePreview() { function reinitializeSpritePreview() {
@ -206,24 +213,19 @@ function createToken() {
const img = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].value; const img = tokenSpriteDropdown[tokenSpriteDropdown.selectedIndex].value;
const name = tokenName.value; const name = tokenName.value;
console.log("creating token");
if (!isNaN(w) && !isNaN(h) && !isNaN(oX) && !isNaN(oY) && img && name) { if (!isNaN(w) && !isNaN(h) && !isNaN(oX) && !isNaN(oY) && img && name) {
console.log("all green"); console.log("all green");
const self = {
sz: [w, h], // create on the frontend for testing
m: L.marker(getCascadingPos(), { /*
icon: L.icon({ const self = NewToken(w, h, oX, oY, img, name);
iconUrl: img,
iconSize: [w,h],
}),
title: name,
draggable: true,
autoPan: true
}),
};
tokens.push(self); tokens.push(self);
self.m.addTo(map); self.m.addTo(map);
resizeMarkers(); resizeMarkers();
*/
// really though we have to send it on the websocket and wait for it to come back
} }
} }
@ -236,10 +238,19 @@ function publishAuxMsg() {
} }
function sendMapImg(url) { function sendMapImg(url) {
console.log("sending " + url);
publish({mapImg: url, auth: adminToken.access_token}); publish({mapImg: url, auth: adminToken.access_token});
} }
function sendToken(t) {
publish({token: t, auth: adminToken.access_token});
}
function revokeToken(t) {
t.x = null;
t.y = null;
sendToken(t);
}
async function uploadMapImg() { async function uploadMapImg() {
try { try {
var input = document.getElementById("map_img_upload"); var input = document.getElementById("map_img_upload");
@ -446,5 +457,9 @@ async function createTable() {
if (res.ok) { if (res.ok) {
getTables(); getTables();
setTableCreateFormVisible(false); setTableCreateFormVisible(false);
} else if (res.status === 422) {
setErr('Table name and passcode must be only alphanumeric and underscores');
} else {
setErr('Error creating table');
} }
} }

View file

@ -88,7 +88,7 @@
<div id="adminWrapper_tokens"> <div id="adminWrapper_tokens">
<details id="admin_token_win" class="ui_win admin_win"><summary>tokens</summary> <details id="admin_token_win" class="ui_win admin_win"><summary>tokens</summary>
<button onclick="setTokenCreateFormVisible(true)">New Token</button> <button onclick="setTokenCreateFormVisible(true)">New Token</button>
<form onsubmit="return false" id="createTokenForm"> <form onsubmit="return false" id="createTokenForm" style="display:none;">
<label>Sprite<select id="token_combobox" onchange="previewSprite(this)"></select></label><br/> <label>Sprite<select id="token_combobox" onchange="previewSprite(this)"></select></label><br/>
<label>Name<input id="token_name"/></label><br/> <label>Name<input id="token_name"/></label><br/>
@ -96,9 +96,9 @@
<label>Height<input type="number" id="token_height" min="1" max="9999" onchange="previewSprite(this)"/></label><br/> <label>Height<input type="number" id="token_height" min="1" max="9999" onchange="previewSprite(this)"/></label><br/>
<label>cX<input type="number" id="token_cx" min="0" max="9999" onchange="previewSprite(this)"/></label><br/> <label>cX<input type="number" id="token_cx" min="0" max="9999" onchange="previewSprite(this)"/></label><br/>
<label>cY<input type="number" id="token_cy" min="0" max="9999" onchange="previewSprite(this)"/></label><br/> <label>cY<input type="number" id="token_cy" min="0" max="9999" onchange="previewSprite(this)"/></label><br/>
<div id="tokenPreview_zone"></div>
<button type="submit" onclick="createToken()">Create</button> <button type="submit" onclick="createToken()">Create</button>
<button onclick="setTokenCreateFormVisible(false)">Cancel</button> <button onclick="setTokenCreateFormVisible(false)">Cancel</button>
<div id="tokenPreview_zone"></div>
</form> </form>
<div id="tokenZone"></div> <div id="tokenZone"></div>
</details><br/> </details><br/>

View file

@ -4,7 +4,9 @@ let tokens = [];
const worldBounds = [[180, -180],[-180, 180]]; const worldBounds = [[180, -180],[-180, 180]];
function initializeMap(mapImgUrl) { function initializeMap(mapImgUrl) {
let init = false;
if (!map) { if (!map) {
init = true;
map = L.map('map', { minZoom: 0, maxZoom: 4, crs: L.CRS.Simple }); map = L.map('map', { minZoom: 0, maxZoom: 4, crs: L.CRS.Simple });
map.on("zoomend", ()=>{resizeMarkers();scaleSpritePreview();}); map.on("zoomend", ()=>{resizeMarkers();scaleSpritePreview();});
} }
@ -14,7 +16,9 @@ function initializeMap(mapImgUrl) {
mapImg = L.imageOverlay(mapImgUrl, worldBounds); mapImg = L.imageOverlay(mapImgUrl, worldBounds);
mapImg.addTo(map); mapImg.addTo(map);
map.setMaxBounds(worldBounds); map.setMaxBounds(worldBounds);
if (init) {
map.setView([0,0], 2); map.setView([0,0], 2);
}
while (tokens.some(t=>t)) { while (tokens.some(t=>t)) {
tokens[0].m.removeFrom(map); tokens[0].m.removeFrom(map);
tokens.shift(); tokens.shift();
@ -39,6 +43,21 @@ function getCascadingPos() {
return topLeft; return topLeft;
} }
function NewToken(w, h, oX, oY, img, name, x, y) {
return {
sz: [w, h],
m: L.marker((x && y) ? [y,x] : getCascadingPos(), {
icon: L.icon({
iconUrl: img,
iconSize: [w,h],
}),
title: name,
draggable: true,
autoPan: true
}),
};
}
function addToken(token) { function addToken(token) {
const self = { sz: token.sz, m: L.marker(token.pos, { const self = { sz: token.sz, m: L.marker(token.pos, {
icon: L.icon({ icon: L.icon({

View file

@ -19,15 +19,17 @@ function fmtLeading(n) {
} }
function formatDice(r) { function formatDice(r) {
console.log(r);
const date = new Date(r.timestamp) const date = new Date(r.timestamp)
const p = document.createElement("p"); const p = document.createElement("p");
const month = date.getMonth() + 1; const month = date.getMonth() + 1;
const day = date.getDate(); const day = date.getDate();
const hours = date.getHours(); const hours = date.getHours();
const minutes = date.getMinutes(); const minutes = date.getMinutes();
const seconds = date.getSeconds(); const seconds = date.getSeconds();
p.innerHTML = `${date.getFullYear()}-${fmtLeading(month)}-${fmtLeading(day)} ${fmtLeading(hours)}:${fmtLeading(minutes)}:${fmtLeading(seconds)} ${r.player} rolled ${r.roll.length}d${r.faces} ${(r.note ? "(" + r.note + ")" : "")}<br>[${r.roll}] (total ${r.roll.reduce((a,c)=>a+c,0)})`; p.innerHTML = `${date.getFullYear()}-${fmtLeading(month)}-${fmtLeading(day)} ${fmtLeading(hours)}:${fmtLeading(minutes)}:${fmtLeading(seconds)} ${r.player} rolled ${r.roll.length}d${r.faces} ${(r.note ? "(" + r.note + ")" : "")}<br>[${r.roll}] (total ${r.roll.reduce((a,c)=>a+c,0)})`;
return p; return p;
} }

View file

@ -147,6 +147,10 @@ pre {
color: var(--fg_color); color: var(--fg_color);
} }
.ui_win ul {
max-height: 10em;
}
#admin_section { #admin_section {
text-align: right; text-align: right;
} }