Compare commits
32 Commits
cb92ffe3df
...
2738de85db
Author | SHA1 | Date |
---|---|---|
jeff | 2738de85db | |
jeff | 92c402e254 | |
jeff | 69d9d7ff80 | |
jeff | 6c1d19aa44 | |
jeff | 5ea5d627bb | |
jeff | ae6d5bcfa3 | |
jeff | ffe4f9298c | |
jeff | ea4723cbe5 | |
jeff | c42f13af52 | |
jeff | e6845d8c7a | |
jeff | 9ca1f989c5 | |
jeff | 31091fcb8e | |
jeff | d6944cd4c1 | |
jeff | f249ab2674 | |
jeff | bf75b84f3d | |
jeff | 06dc6c1274 | |
jeff | 133b4e6236 | |
jeff | 2008b90cba | |
jeff | 3fc9378f50 | |
jeff | dd2741a0c1 | |
jeff | dcbf801ecc | |
jeff | af01a56907 | |
jeff | 06262e6ad3 | |
jeff | 592c925bc1 | |
jeff | f479da9001 | |
jeff | 06954caaaf | |
jeff | c99a41bcbd | |
jeff | 41a8514c00 | |
jeff | ab274de581 | |
jeff | 96baba6a50 | |
jeff | e384370e24 | |
jeff | e601ae0149 |
|
@ -2,3 +2,7 @@
|
|||
todo.org
|
||||
*.sqlite*
|
||||
*.sqlite.*
|
||||
node_modules
|
||||
static/scripts/dist
|
||||
static/templates/*
|
||||
generated/
|
||||
|
|
|
@ -98,6 +98,27 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5"
|
||||
dependencies = [
|
||||
"async-stream-impl",
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream-impl"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.42"
|
||||
|
@ -156,6 +177,12 @@ version = "0.2.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.0"
|
||||
|
@ -346,7 +373,7 @@ version = "0.15.0-dev"
|
|||
source = "git+https://github.com/SergioBenitez/cookie-rs.git?rev=1c3ca83#1c3ca838543b60a4448d279dc4b903cc7a2bc22a"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"base64",
|
||||
"base64 0.13.0",
|
||||
"hkdf",
|
||||
"percent-encoding",
|
||||
"rand 0.7.3",
|
||||
|
@ -1668,6 +1695,7 @@ dependencies = [
|
|||
"rand_chacha 0.2.2",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
"rand_pcg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1738,6 +1766,15 @@ dependencies = [
|
|||
"rand_core 0.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
|
@ -1835,6 +1872,21 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket"
|
||||
version = "0.5.0-dev"
|
||||
|
@ -1910,6 +1962,7 @@ dependencies = [
|
|||
"state",
|
||||
"time 0.2.23",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"uncased",
|
||||
"unicode-xid",
|
||||
"version_check",
|
||||
|
@ -1936,7 +1989,7 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.0",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
|
@ -1951,6 +2004,19 @@ dependencies = [
|
|||
"semver 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81"
|
||||
dependencies = [
|
||||
"base64 0.12.3",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
@ -1982,6 +2048,16 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.0.0"
|
||||
|
@ -2150,6 +2226,12 @@ dependencies = [
|
|||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.1.5"
|
||||
|
@ -2403,11 +2485,13 @@ dependencies = [
|
|||
"rocket_contrib",
|
||||
"rust-argon2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"strum",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2569,6 +2653,18 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.3.1"
|
||||
|
@ -2592,12 +2688,226 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74a5d6e7439ecf910463667080de772a9c7ddf26bc9fb4f3252ac3862e43337d"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
"base64 0.12.3",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"percent-encoding",
|
||||
"pin-project 0.4.27",
|
||||
"prost",
|
||||
"prost-derive",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-balance",
|
||||
"tower-load",
|
||||
"tower-make",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tonic-build"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19970cf58f3acc820962be74c4021b8bbc8e8a1c4e3a02095d0aa60cde5f3633"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"prost-build",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3169017c090b7a28fce80abaad0ab4f5566423677c9331bb320af7e49cfe62"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"tower-buffer",
|
||||
"tower-discover",
|
||||
"tower-layer",
|
||||
"tower-limit",
|
||||
"tower-load-shed",
|
||||
"tower-retry",
|
||||
"tower-service",
|
||||
"tower-timeout",
|
||||
"tower-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-balance"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a792277613b7052448851efcf98a2c433e6f1d01460832dc60bef676bc275d4c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"pin-project 0.4.27",
|
||||
"rand 0.7.3",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tower-discover",
|
||||
"tower-layer",
|
||||
"tower-load",
|
||||
"tower-make",
|
||||
"tower-ready-cache",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-buffer"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4887dc2a65d464c8b9b66e0e4d51c2fd6cf5b3373afc72805b0a60bce00446a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project 0.4.27",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-discover"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6b5000c3c54d269cc695dff28136bb33d08cbf1df2c48129e143ab65bf3c2a"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project 0.4.27",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a35d656f2638b288b33495d1053ea74c40dc05ec0b92084dd71ca5566c4ed1dc"
|
||||
|
||||
[[package]]
|
||||
name = "tower-limit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92c3040c5dbed68abffaa0d4517ac1a454cd741044f33ab0eefab6b8d1361404"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project 0.4.27",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-load",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-load"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cc79fc3afd07492b7966d7efa7c6c50f8ed58d768a6075dd7ae6591c5d2017b"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"log",
|
||||
"pin-project 0.4.27",
|
||||
"tokio",
|
||||
"tower-discover",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-load-shed"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f021e23900173dc315feb4b6922510dae3e79c689b74c089112066c11f0ae4e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project 0.4.27",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-make"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce50370d644a0364bf4877ffd4f76404156a248d104e2cc234cd391ea5cdc965"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-ready-cache"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4eabb6620e5481267e2ec832c780b31cad0c15dcb14ed825df5076b26b591e1f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"log",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-retry"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6727956aaa2f8957d4d9232b308fe8e4e65d99db30f42b225646e86c9b6a952"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project 0.4.27",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"
|
||||
|
||||
[[package]]
|
||||
name = "tower-timeout"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "127b8924b357be938823eaaec0608c482d40add25609481027b96198b2e4b31e"
|
||||
dependencies = [
|
||||
"pin-project 0.4.27",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-util"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1093c19826d33807c72511e68f73b4a0469a3f22c2bd5f7d5212178b4b89674"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project 0.4.27",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.22"
|
||||
|
@ -2607,9 +2917,21 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
"log",
|
||||
"pin-project-lite 0.2.0",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80e0ccfc3378da0cce270c946b676a376943f5cd16aeba64568e7939806f4ada"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.17"
|
||||
|
@ -2761,6 +3083,12 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.0"
|
||||
|
@ -2882,6 +3210,16 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "3.1.1"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -6,6 +6,9 @@ edition = "2018"
|
|||
build = "build.rs"
|
||||
default-run = "tenebrous"
|
||||
|
||||
[package.metadata.scripts]
|
||||
grpc-proxy = 'docker run -d --rm -v "$(pwd)"/envoy.yaml:/etc/envoy/envoy.yaml:ro --network=host envoyproxy/envoy:v1.16.1'
|
||||
|
||||
[[bin]]
|
||||
name = "tenebrous-migrate"
|
||||
path = "src/migrate.rs"
|
||||
|
@ -16,11 +19,12 @@ path = "src/main.rs"
|
|||
|
||||
[build-dependencies]
|
||||
prost-build = "0.6"
|
||||
tonic-build = "0.3"
|
||||
|
||||
[dependencies]
|
||||
tonic = "0.3"
|
||||
prost = "0.6"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
erased-serde = "0.3"
|
||||
thiserror = "1.0"
|
||||
|
@ -28,6 +32,7 @@ rust-argon2 = "0.8"
|
|||
log = "0.4"
|
||||
rand = "0.7"
|
||||
futures = "0.3"
|
||||
tokio = { version = "0.2", features = ["macros"] }
|
||||
strum = { version = "0.20", features = ["derive"] }
|
||||
sqlx = { version = "0.4.2", features = [ "offline", "sqlite", "runtime-tokio-native-tls" ] }
|
||||
refinery = { version = "0.3", features = ["rusqlite"]}
|
||||
|
@ -36,7 +41,7 @@ barrel = { version = "0.6", features = ["sqlite3"] }
|
|||
[dependencies.rocket]
|
||||
git = "https://github.com/SergioBenitez/Rocket"
|
||||
branch = "master"
|
||||
features = ["secrets"]
|
||||
features = ["secrets", "tls"]
|
||||
|
||||
[dependencies.rocket_contrib]
|
||||
git = "https://github.com/SergioBenitez/Rocket"
|
||||
|
|
68
README.md
68
README.md
|
@ -4,41 +4,66 @@ An open source character sheet service for tabletop roleplaying games.
|
|||
|
||||
Currently under heavy development.
|
||||
|
||||
## The Stack
|
||||
|
||||
This project makes use of these technologies:
|
||||
|
||||
- Rust
|
||||
- Rocket Web Framework
|
||||
- Tonic gRPC Framework
|
||||
- Typescript
|
||||
|
||||
Building is backed by: cargo, npm, and webpack.
|
||||
|
||||
## Build Instructions
|
||||
|
||||
These are very basic build instructions. They assume you already have
|
||||
cargo set up and installed.
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
Install dependencies. The exact method depends on your OS.
|
||||
|
||||
* sqlite3 and development headers (Void Linux: `xbps-install
|
||||
sqlite sqlite-devel`, Ubuntu: `apt install sqlite3 libsqlite3-dev`).
|
||||
* protoc: protocol buffers compiler. There is one baked into the
|
||||
build, so you should not need this unless you are not using
|
||||
Linux/Mac/Windows.
|
||||
|
||||
### Initial Setup
|
||||
|
||||
Follow these instructions from the root of the repository.
|
||||
Quick initial setup instructions that will set up a development
|
||||
environment.
|
||||
|
||||
Set up database:
|
||||
First, install dependencies by either using the command below (Void
|
||||
Linux), or reading the "Dependencies Required" section:
|
||||
|
||||
```
|
||||
xbps-install sqlite sqlite-devel protobuf nodejs docker
|
||||
```
|
||||
|
||||
Then run the required `cargo` commands to install management tools and
|
||||
create a development database:
|
||||
|
||||
```
|
||||
|
||||
cargo install --version=0.2.0 sqlx-cli
|
||||
cargo install cargo-run-script
|
||||
cargo run --bin tenebrous-migrate
|
||||
```
|
||||
|
||||
### Run Application
|
||||
### Dependencies Required
|
||||
|
||||
If you are using `rustup`, then it should automatically switch to the
|
||||
stable version of Rust in this repository. This is because of the
|
||||
`rust-toolchain` file.
|
||||
Dependencies required to build the project. The exact installation
|
||||
method depends on your OS.
|
||||
|
||||
* sqlite3 and development headers (Void Linux: `xbps-install
|
||||
sqlite sqlite-devel`, Ubuntu: `apt install sqlite3 libsqlite3-dev`).
|
||||
* protoc: protocol buffers compiler. Needed to compile protobuf files
|
||||
for both the server and web frontends (Void Linux: `xbps-install
|
||||
protobuf`).
|
||||
* Node and npm: Needed to run webpack for compiling and injecting
|
||||
Typescript into various web pages (Void Linux: `xbps-install
|
||||
nodejs`).
|
||||
* Docker: Needed to run the grpc proxy (Void Linux `xbps-install
|
||||
docker`).
|
||||
|
||||
### Run Application
|
||||
|
||||
Command line "instructions" to build and run the application:
|
||||
|
||||
```
|
||||
cargo run-script grpc-proxy # only required if proxy not already running
|
||||
cargo run
|
||||
```
|
||||
|
||||
|
@ -65,4 +90,15 @@ to update the SQLx data JSON file:
|
|||
cargo sqlx prepare -- --bin tenebrous
|
||||
```
|
||||
|
||||
### gRPC-Web Proxy
|
||||
|
||||
The frontend web application makes use of the gRPC-Web protocol to
|
||||
call gPRC endpoints from the browser. This requires a proxy to
|
||||
translate the calls from the browser to HTTP2 calls gRPC understands.
|
||||
For development, executing the `cargo run-script grpc-proxy` command
|
||||
will start the envoy proxy recommended by Google's gRPC-Web project.
|
||||
|
||||
The envoy configuration assumes you are on Linux. If you are using Mac
|
||||
OS or Windows, see the note in the envoy configuration.
|
||||
|
||||
[rustup]: https://rust-lang.github.io/rustup/index.html
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[default]
|
||||
template_dir = "generated/templates/"
|
||||
|
||||
[development]
|
||||
address = "localhost"
|
||||
port = 8000
|
||||
|
|
46
build.rs
46
build.rs
|
@ -1,10 +1,50 @@
|
|||
use std::process::Command;
|
||||
|
||||
fn js_protos() {
|
||||
let output = Command::new("npm")
|
||||
.arg("run")
|
||||
.arg("build:protobuf")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if output.status.success() {
|
||||
return;
|
||||
} else {
|
||||
let err = String::from_utf8(output.stderr).unwrap();
|
||||
panic!("JS Protobuf build failure:\n {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
fn webpack() {
|
||||
let output = Command::new("npm")
|
||||
.arg("run")
|
||||
.arg("build:webpack")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
if output.status.success() {
|
||||
return;
|
||||
} else {
|
||||
let err = String::from_utf8(output.stderr).unwrap();
|
||||
panic!("Webpack build failure:\n {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=static/scripts/webpack.config.js");
|
||||
js_protos();
|
||||
webpack();
|
||||
|
||||
let mut config = prost_build::Config::new();
|
||||
config.btree_map(&["."]);
|
||||
config.type_attribute(".", "#[derive(Serialize)]");
|
||||
config.type_attribute(".", "#[derive(::serde::Serialize)]");
|
||||
config.type_attribute(".", "#[serde(rename_all = \"camelCase\")]");
|
||||
config
|
||||
.compile_protos(
|
||||
|
||||
tonic_build::configure()
|
||||
.build_server(false)
|
||||
.build_client(false)
|
||||
.compile_with_config(
|
||||
config,
|
||||
&["proto/cofd.proto", "proto/cofd_api.proto"],
|
||||
&["src/", "proto/"],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
admin:
|
||||
access_log_path: /tmp/admin_access.log
|
||||
address:
|
||||
socket_address: { address: 0.0.0.0, port_value: 9901 }
|
||||
|
||||
static_resources:
|
||||
listeners:
|
||||
- name: listener_0
|
||||
address:
|
||||
socket_address: { address: 0.0.0.0, port_value: 8080 }
|
||||
filter_chains:
|
||||
- filters:
|
||||
- name: envoy.filters.network.http_connection_manager
|
||||
typed_config:
|
||||
"@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
|
||||
codec_type: auto
|
||||
stat_prefix: ingress_http
|
||||
route_config:
|
||||
name: local_route
|
||||
virtual_hosts:
|
||||
- name: local_service
|
||||
domains: ["*"]
|
||||
routes:
|
||||
- match: { prefix: "/" }
|
||||
route:
|
||||
cluster: greeter_service
|
||||
max_grpc_timeout: 0s
|
||||
cors:
|
||||
allow_origin_string_match:
|
||||
- prefix: "*"
|
||||
allow_methods: GET, PUT, DELETE, POST, OPTIONS
|
||||
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
|
||||
max_age: "1728000"
|
||||
expose_headers: custom-header-1,grpc-status,grpc-message
|
||||
http_filters:
|
||||
- name: envoy.filters.http.grpc_web
|
||||
- name: envoy.filters.http.cors
|
||||
- name: envoy.filters.http.router
|
||||
clusters:
|
||||
- name: greeter_service
|
||||
connect_timeout: 0.25s
|
||||
type: logical_dns
|
||||
http2_protocol_options: {}
|
||||
lb_policy: round_robin
|
||||
# win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below
|
||||
load_assignment:
|
||||
cluster_name: cluster_0
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: localhost
|
||||
port_value: 9090
|
|
@ -0,0 +1,3 @@
|
|||
FROM envoyproxy/envoy:v1.16-latest
|
||||
COPY ./envoy.yaml /etc/envoy/envoy.yaml
|
||||
CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "tenebrous-sheets",
|
||||
"version": "0.1.0",
|
||||
"description": "An open source character sheet service for tabletop roleplaying games.",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"webpack-dev": "webpack --watch",
|
||||
"build:protobuf": "mkdir -p src/frontend/_proto && protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts -I ./proto --js_out=import_style=commonjs,binary:./src/frontend/_proto --ts_out=service=grpc-web:./src/frontend/_proto ./proto/*.proto",
|
||||
"build:webpack": "webpack"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "ssh://git@git.agnos.is:22022/projectmoon/tenebrous-sheets.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@improbable-eng/grpc-web": "^0.13.0",
|
||||
"google-protobuf": "^3.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/google-protobuf": "^3.7.4",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"html-webpack-plugin": "^4.5.0",
|
||||
"ts-loader": "^8.0.13",
|
||||
"ts-protoc-gen": "^0.14.0",
|
||||
"typescript": "^4.1.3",
|
||||
"webpack": "^5.11.1",
|
||||
"webpack-cli": "^4.3.1",
|
||||
"webpack-dev-server": "^3.11.1"
|
||||
}
|
||||
}
|
|
@ -57,43 +57,47 @@ message CofdSheet {
|
|||
}
|
||||
|
||||
string name = 1;
|
||||
string player = 2;
|
||||
string campaign = 3;
|
||||
string description = 4;
|
||||
string gender = 2;
|
||||
string concept = 3;
|
||||
int32 age = 4;
|
||||
|
||||
int32 strength = 6;
|
||||
int32 dexterity = 7;
|
||||
int32 stamina = 8;
|
||||
string player = 5;
|
||||
string chronicle = 6;
|
||||
string description = 7;
|
||||
|
||||
int32 intelligence = 9;
|
||||
int32 wits = 10;
|
||||
int32 resolve = 11;
|
||||
int32 strength = 8;
|
||||
int32 dexterity = 9;
|
||||
int32 stamina = 10;
|
||||
|
||||
int32 presence = 12;
|
||||
int32 manipulation = 13;
|
||||
int32 composure = 14;
|
||||
int32 intelligence = 11;
|
||||
int32 wits = 12;
|
||||
int32 resolve = 13;
|
||||
|
||||
map<string, Skill> physical_skills = 16;
|
||||
map<string, Skill> mental_skills = 17;
|
||||
map<string, Skill> social_skills = 18;
|
||||
int32 presence = 14;
|
||||
int32 manipulation = 15;
|
||||
int32 composure = 16;
|
||||
|
||||
repeated Merit merits = 15;
|
||||
repeated Condition conditions = 19;
|
||||
map<string, Skill> physical_skills = 17;
|
||||
map<string, Skill> mental_skills = 18;
|
||||
map<string, Skill> social_skills = 19;
|
||||
|
||||
int32 size = 20;
|
||||
int32 health = 21;
|
||||
int32 willpower = 22;
|
||||
int32 experience_points = 23;
|
||||
int32 beats = 24;
|
||||
repeated Merit merits = 20;
|
||||
repeated Condition conditions = 21;
|
||||
|
||||
repeated Item items = 25;
|
||||
repeated Attack attacks = 26;
|
||||
int32 size = 22;
|
||||
int32 health = 23;
|
||||
int32 willpower = 24;
|
||||
int32 experience_points = 25;
|
||||
int32 beats = 26;
|
||||
|
||||
map<string, string> other_data = 27;
|
||||
repeated Item items = 27;
|
||||
repeated Attack attacks = 28;
|
||||
|
||||
map<string, string> other_data = 29;
|
||||
|
||||
oneof system_fields {
|
||||
CoreFields core = 28;
|
||||
MageFields mage = 29;
|
||||
ChangelingFields changeling = 30;
|
||||
CoreFields core = 30;
|
||||
MageFields mage = 31;
|
||||
ChangelingFields changeling = 32;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,77 +3,70 @@ import "cofd.proto";
|
|||
|
||||
package models.proto.cofd.api;
|
||||
|
||||
message CharacterIdentifier {
|
||||
string owner = 1;
|
||||
int32 id = 2;
|
||||
}
|
||||
|
||||
//Update basic information about a Chronicles of Darkness (or
|
||||
//derivative system) character sheet. This is a straight overwrite of
|
||||
//all basic information on the sheet.
|
||||
message BasicInfo {
|
||||
string name = 1;
|
||||
string gender = 2;
|
||||
string concept = 3;
|
||||
string chronicle = 4;
|
||||
int32 age = 5;
|
||||
message UpdateBasicInfoRequest {
|
||||
CharacterIdentifier character = 1;
|
||||
reserved 2;
|
||||
// string owner = 1;
|
||||
// int32 character_id = 2;
|
||||
|
||||
string name = 3;
|
||||
string gender = 4;
|
||||
string concept = 5;
|
||||
string chronicle = 6;
|
||||
int32 age = 7;
|
||||
}
|
||||
|
||||
//Update all attributes in a Chronicles of Darkness character (or
|
||||
//derivative system) character sheet. This is a straight overwrite of
|
||||
//all basic information on the sheet.
|
||||
message Attributes {
|
||||
int32 strength = 1;
|
||||
int32 dexterity = 2;
|
||||
int32 stamina = 3;
|
||||
|
||||
int32 intelligence = 4;
|
||||
int32 wits = 5;
|
||||
int32 resolve = 6;
|
||||
|
||||
int32 presence = 7;
|
||||
int32 manipulation = 8;
|
||||
int32 composure = 9;
|
||||
//Generic "did something succeed or not" response.
|
||||
message ApiResult {
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
//Update an attribute's dot amount. TODO rename to AttributesUpdate.
|
||||
message UpdateAttributeRequest {
|
||||
string character_username = 1;
|
||||
int32 character_id = 2;
|
||||
string attribute_name = 3;
|
||||
int32 attribute_value = 4;
|
||||
}
|
||||
|
||||
//Update skill entries in a Chronicles of Darkness character sheet.
|
||||
//This is a straight overwrite of all skills in the sheet.
|
||||
message Skills {
|
||||
repeated CofdSheet.Skill physical_skills = 1;
|
||||
repeated CofdSheet.Skill mental_skills = 2;
|
||||
repeated CofdSheet.Skill social_skills = 3;
|
||||
CharacterIdentifier character = 1;
|
||||
string attribute_name = 2;
|
||||
int32 attribute_value = 3;
|
||||
}
|
||||
|
||||
//Full update of a single skill
|
||||
message SkillUpdate {
|
||||
string name = 1;
|
||||
message UpdateSkillRequest {
|
||||
CharacterIdentifier character = 1;
|
||||
CofdSheet.Skill skill = 2;
|
||||
}
|
||||
|
||||
|
||||
//Partial update of a single skill dot amount.
|
||||
message UpdateSkillValueRequest {
|
||||
string character_username = 1;
|
||||
int32 character_id = 2;
|
||||
string skill_name = 3;
|
||||
int32 skill_value = 4;
|
||||
CharacterIdentifier character = 1;
|
||||
string skill_name = 2;
|
||||
int32 skill_value = 3;
|
||||
}
|
||||
|
||||
//Partial update of only a skill's specializations. The
|
||||
//specializations will be overwritten with the new values.
|
||||
message SkillSpecializationsUpdate {
|
||||
message UpdateSkillSpecializationsRequest {
|
||||
string name = 1;
|
||||
repeated string specializations = 2;
|
||||
}
|
||||
|
||||
//Add a Condition to a Chronicles of Darkness character sheet.
|
||||
message Condition {
|
||||
string name = 1;
|
||||
message AddConditionRequest {
|
||||
string character_username = 1;
|
||||
int32 character_id = 2;
|
||||
string condition_name = 3;
|
||||
}
|
||||
|
||||
service CofdApi {
|
||||
rpc UpdateSkillValue(UpdateSkillValueRequest) returns (CofdSheet.Skill);
|
||||
//Remove a Condition from a Chronicles of Darkness character sheet.
|
||||
message RemoveConditionRequest {
|
||||
string character_username = 1;
|
||||
int32 character_id = 2;
|
||||
string condition_name = 3;
|
||||
}
|
21
src/db.rs
21
src/db.rs
|
@ -39,6 +39,8 @@ pub(crate) trait Dao {
|
|||
|
||||
async fn insert_character(&self, new_character: NewCharacter<'_>) -> sqlx::Result<()>;
|
||||
|
||||
async fn update_character<'a>(&self, character: &'a Character) -> sqlx::Result<()>;
|
||||
|
||||
async fn update_character_sheet<'a>(&self, character: &'a Character) -> sqlx::Result<()>;
|
||||
}
|
||||
|
||||
|
@ -124,6 +126,25 @@ impl Dao for SqlitePool {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_character<'a>(&self, character: &'a Character) -> sqlx::Result<()> {
|
||||
sqlx::query(
|
||||
"UPDATE characters
|
||||
set user_id = ?, viewable = ?, character_name = ?,
|
||||
data_type = ?, data_version = ?, data = ? where id = ?",
|
||||
)
|
||||
.bind(character.user_id)
|
||||
.bind(character.viewable)
|
||||
.bind(&character.character_name)
|
||||
.bind(character.data_type)
|
||||
.bind(character.data_version)
|
||||
.bind(&character.data)
|
||||
.bind(character.id)
|
||||
.execute(self)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_character_sheet<'a>(&self, character: &'a Character) -> sqlx::Result<()> {
|
||||
sqlx::query("UPDATE characters set data = ? where id = ?")
|
||||
.bind(&character.data)
|
||||
|
|
|
@ -4,6 +4,7 @@ use rocket::response::status;
|
|||
use rocket::response::{self, Responder};
|
||||
use rocket_contrib::templates::Template;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::Into;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
@ -48,6 +49,28 @@ impl Error {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn message(&self) -> String {
|
||||
if self.is_sensitive() {
|
||||
"internal error".to_string()
|
||||
} else {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for tonic::Status {
|
||||
fn from(err: Error) -> tonic::Status {
|
||||
use tonic::{Code, Status};
|
||||
use Error::*;
|
||||
match err {
|
||||
NotFound => Status::new(Code::NotFound, err.message()),
|
||||
NotLoggedIn => Status::new(Code::Unauthenticated, err.message()),
|
||||
NoPermission => Status::new(Code::PermissionDenied, err.message()),
|
||||
InvalidInput => Status::new(Code::InvalidArgument, err.message()),
|
||||
_ => Status::new(Code::Internal, err.message()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
|
|
|
@ -0,0 +1,266 @@
|
|||
// package: models.proto.cofd.api
|
||||
// file: cofd_api.proto
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
import * as cofd_pb from "./cofd_pb";
|
||||
|
||||
export class CharacterIdentifier extends jspb.Message {
|
||||
getOwner(): string;
|
||||
setOwner(value: string): void;
|
||||
|
||||
getId(): number;
|
||||
setId(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): CharacterIdentifier.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: CharacterIdentifier): CharacterIdentifier.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: CharacterIdentifier, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): CharacterIdentifier;
|
||||
static deserializeBinaryFromReader(message: CharacterIdentifier, reader: jspb.BinaryReader): CharacterIdentifier;
|
||||
}
|
||||
|
||||
export namespace CharacterIdentifier {
|
||||
export type AsObject = {
|
||||
owner: string,
|
||||
id: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateBasicInfoRequest extends jspb.Message {
|
||||
hasCharacter(): boolean;
|
||||
clearCharacter(): void;
|
||||
getCharacter(): CharacterIdentifier | undefined;
|
||||
setCharacter(value?: CharacterIdentifier): void;
|
||||
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getGender(): string;
|
||||
setGender(value: string): void;
|
||||
|
||||
getConcept(): string;
|
||||
setConcept(value: string): void;
|
||||
|
||||
getChronicle(): string;
|
||||
setChronicle(value: string): void;
|
||||
|
||||
getAge(): number;
|
||||
setAge(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateBasicInfoRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateBasicInfoRequest): UpdateBasicInfoRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateBasicInfoRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateBasicInfoRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateBasicInfoRequest, reader: jspb.BinaryReader): UpdateBasicInfoRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateBasicInfoRequest {
|
||||
export type AsObject = {
|
||||
character?: CharacterIdentifier.AsObject,
|
||||
name: string,
|
||||
gender: string,
|
||||
concept: string,
|
||||
chronicle: string,
|
||||
age: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class ApiResult extends jspb.Message {
|
||||
getSuccess(): boolean;
|
||||
setSuccess(value: boolean): void;
|
||||
|
||||
getError(): string;
|
||||
setError(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ApiResult.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: ApiResult): ApiResult.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: ApiResult, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): ApiResult;
|
||||
static deserializeBinaryFromReader(message: ApiResult, reader: jspb.BinaryReader): ApiResult;
|
||||
}
|
||||
|
||||
export namespace ApiResult {
|
||||
export type AsObject = {
|
||||
success: boolean,
|
||||
error: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateAttributeRequest extends jspb.Message {
|
||||
hasCharacter(): boolean;
|
||||
clearCharacter(): void;
|
||||
getCharacter(): CharacterIdentifier | undefined;
|
||||
setCharacter(value?: CharacterIdentifier): void;
|
||||
|
||||
getAttributeName(): string;
|
||||
setAttributeName(value: string): void;
|
||||
|
||||
getAttributeValue(): number;
|
||||
setAttributeValue(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateAttributeRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateAttributeRequest): UpdateAttributeRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateAttributeRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateAttributeRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateAttributeRequest, reader: jspb.BinaryReader): UpdateAttributeRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateAttributeRequest {
|
||||
export type AsObject = {
|
||||
character?: CharacterIdentifier.AsObject,
|
||||
attributeName: string,
|
||||
attributeValue: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateSkillRequest extends jspb.Message {
|
||||
hasCharacter(): boolean;
|
||||
clearCharacter(): void;
|
||||
getCharacter(): CharacterIdentifier | undefined;
|
||||
setCharacter(value?: CharacterIdentifier): void;
|
||||
|
||||
hasSkill(): boolean;
|
||||
clearSkill(): void;
|
||||
getSkill(): cofd_pb.CofdSheet.Skill | undefined;
|
||||
setSkill(value?: cofd_pb.CofdSheet.Skill): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateSkillRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateSkillRequest): UpdateSkillRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateSkillRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateSkillRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateSkillRequest, reader: jspb.BinaryReader): UpdateSkillRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateSkillRequest {
|
||||
export type AsObject = {
|
||||
character?: CharacterIdentifier.AsObject,
|
||||
skill?: cofd_pb.CofdSheet.Skill.AsObject,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateSkillValueRequest extends jspb.Message {
|
||||
hasCharacter(): boolean;
|
||||
clearCharacter(): void;
|
||||
getCharacter(): CharacterIdentifier | undefined;
|
||||
setCharacter(value?: CharacterIdentifier): void;
|
||||
|
||||
getSkillName(): string;
|
||||
setSkillName(value: string): void;
|
||||
|
||||
getSkillValue(): number;
|
||||
setSkillValue(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateSkillValueRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateSkillValueRequest): UpdateSkillValueRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateSkillValueRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateSkillValueRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateSkillValueRequest, reader: jspb.BinaryReader): UpdateSkillValueRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateSkillValueRequest {
|
||||
export type AsObject = {
|
||||
character?: CharacterIdentifier.AsObject,
|
||||
skillName: string,
|
||||
skillValue: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class UpdateSkillSpecializationsRequest extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
clearSpecializationsList(): void;
|
||||
getSpecializationsList(): Array<string>;
|
||||
setSpecializationsList(value: Array<string>): void;
|
||||
addSpecializations(value: string, index?: number): string;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): UpdateSkillSpecializationsRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: UpdateSkillSpecializationsRequest): UpdateSkillSpecializationsRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: UpdateSkillSpecializationsRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): UpdateSkillSpecializationsRequest;
|
||||
static deserializeBinaryFromReader(message: UpdateSkillSpecializationsRequest, reader: jspb.BinaryReader): UpdateSkillSpecializationsRequest;
|
||||
}
|
||||
|
||||
export namespace UpdateSkillSpecializationsRequest {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
specializationsList: Array<string>,
|
||||
}
|
||||
}
|
||||
|
||||
export class AddConditionRequest extends jspb.Message {
|
||||
getCharacterUsername(): string;
|
||||
setCharacterUsername(value: string): void;
|
||||
|
||||
getCharacterId(): number;
|
||||
setCharacterId(value: number): void;
|
||||
|
||||
getConditionName(): string;
|
||||
setConditionName(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): AddConditionRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: AddConditionRequest): AddConditionRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: AddConditionRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): AddConditionRequest;
|
||||
static deserializeBinaryFromReader(message: AddConditionRequest, reader: jspb.BinaryReader): AddConditionRequest;
|
||||
}
|
||||
|
||||
export namespace AddConditionRequest {
|
||||
export type AsObject = {
|
||||
characterUsername: string,
|
||||
characterId: number,
|
||||
conditionName: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class RemoveConditionRequest extends jspb.Message {
|
||||
getCharacterUsername(): string;
|
||||
setCharacterUsername(value: string): void;
|
||||
|
||||
getCharacterId(): number;
|
||||
setCharacterId(value: number): void;
|
||||
|
||||
getConditionName(): string;
|
||||
setConditionName(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): RemoveConditionRequest.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: RemoveConditionRequest): RemoveConditionRequest.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: RemoveConditionRequest, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): RemoveConditionRequest;
|
||||
static deserializeBinaryFromReader(message: RemoveConditionRequest, reader: jspb.BinaryReader): RemoveConditionRequest;
|
||||
}
|
||||
|
||||
export namespace RemoveConditionRequest {
|
||||
export type AsObject = {
|
||||
characterUsername: string,
|
||||
characterId: number,
|
||||
conditionName: string,
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
// package: models.proto.cofd.api
|
||||
// file: cofd_api.proto
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// package: models.proto.cofd.api
|
||||
// file: cofd_api.proto
|
||||
|
|
@ -0,0 +1,373 @@
|
|||
// package: models.proto.cofd
|
||||
// file: cofd.proto
|
||||
|
||||
import * as jspb from "google-protobuf";
|
||||
|
||||
export class CoreFields extends jspb.Message {
|
||||
getIntegrity(): number;
|
||||
setIntegrity(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): CoreFields.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: CoreFields): CoreFields.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: CoreFields, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): CoreFields;
|
||||
static deserializeBinaryFromReader(message: CoreFields, reader: jspb.BinaryReader): CoreFields;
|
||||
}
|
||||
|
||||
export namespace CoreFields {
|
||||
export type AsObject = {
|
||||
integrity: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class MageFields extends jspb.Message {
|
||||
getWidsom(): number;
|
||||
setWidsom(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): MageFields.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: MageFields): MageFields.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: MageFields, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): MageFields;
|
||||
static deserializeBinaryFromReader(message: MageFields, reader: jspb.BinaryReader): MageFields;
|
||||
}
|
||||
|
||||
export namespace MageFields {
|
||||
export type AsObject = {
|
||||
widsom: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangelingFields extends jspb.Message {
|
||||
getClarity(): number;
|
||||
setClarity(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): ChangelingFields.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: ChangelingFields): ChangelingFields.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: ChangelingFields, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): ChangelingFields;
|
||||
static deserializeBinaryFromReader(message: ChangelingFields, reader: jspb.BinaryReader): ChangelingFields;
|
||||
}
|
||||
|
||||
export namespace ChangelingFields {
|
||||
export type AsObject = {
|
||||
clarity: number,
|
||||
}
|
||||
}
|
||||
|
||||
export class CofdSheet extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getGender(): string;
|
||||
setGender(value: string): void;
|
||||
|
||||
getConcept(): string;
|
||||
setConcept(value: string): void;
|
||||
|
||||
getAge(): number;
|
||||
setAge(value: number): void;
|
||||
|
||||
getPlayer(): string;
|
||||
setPlayer(value: string): void;
|
||||
|
||||
getChronicle(): string;
|
||||
setChronicle(value: string): void;
|
||||
|
||||
getDescription(): string;
|
||||
setDescription(value: string): void;
|
||||
|
||||
getStrength(): number;
|
||||
setStrength(value: number): void;
|
||||
|
||||
getDexterity(): number;
|
||||
setDexterity(value: number): void;
|
||||
|
||||
getStamina(): number;
|
||||
setStamina(value: number): void;
|
||||
|
||||
getIntelligence(): number;
|
||||
setIntelligence(value: number): void;
|
||||
|
||||
getWits(): number;
|
||||
setWits(value: number): void;
|
||||
|
||||
getResolve(): number;
|
||||
setResolve(value: number): void;
|
||||
|
||||
getPresence(): number;
|
||||
setPresence(value: number): void;
|
||||
|
||||
getManipulation(): number;
|
||||
setManipulation(value: number): void;
|
||||
|
||||
getComposure(): number;
|
||||
setComposure(value: number): void;
|
||||
|
||||
getPhysicalSkillsMap(): jspb.Map<string, CofdSheet.Skill>;
|
||||
clearPhysicalSkillsMap(): void;
|
||||
getMentalSkillsMap(): jspb.Map<string, CofdSheet.Skill>;
|
||||
clearMentalSkillsMap(): void;
|
||||
getSocialSkillsMap(): jspb.Map<string, CofdSheet.Skill>;
|
||||
clearSocialSkillsMap(): void;
|
||||
clearMeritsList(): void;
|
||||
getMeritsList(): Array<CofdSheet.Merit>;
|
||||
setMeritsList(value: Array<CofdSheet.Merit>): void;
|
||||
addMerits(value?: CofdSheet.Merit, index?: number): CofdSheet.Merit;
|
||||
|
||||
clearConditionsList(): void;
|
||||
getConditionsList(): Array<CofdSheet.Condition>;
|
||||
setConditionsList(value: Array<CofdSheet.Condition>): void;
|
||||
addConditions(value?: CofdSheet.Condition, index?: number): CofdSheet.Condition;
|
||||
|
||||
getSize(): number;
|
||||
setSize(value: number): void;
|
||||
|
||||
getHealth(): number;
|
||||
setHealth(value: number): void;
|
||||
|
||||
getWillpower(): number;
|
||||
setWillpower(value: number): void;
|
||||
|
||||
getExperiencePoints(): number;
|
||||
setExperiencePoints(value: number): void;
|
||||
|
||||
getBeats(): number;
|
||||
setBeats(value: number): void;
|
||||
|
||||
clearItemsList(): void;
|
||||
getItemsList(): Array<CofdSheet.Item>;
|
||||
setItemsList(value: Array<CofdSheet.Item>): void;
|
||||
addItems(value?: CofdSheet.Item, index?: number): CofdSheet.Item;
|
||||
|
||||
clearAttacksList(): void;
|
||||
getAttacksList(): Array<CofdSheet.Attack>;
|
||||
setAttacksList(value: Array<CofdSheet.Attack>): void;
|
||||
addAttacks(value?: CofdSheet.Attack, index?: number): CofdSheet.Attack;
|
||||
|
||||
getOtherDataMap(): jspb.Map<string, string>;
|
||||
clearOtherDataMap(): void;
|
||||
hasCore(): boolean;
|
||||
clearCore(): void;
|
||||
getCore(): CoreFields | undefined;
|
||||
setCore(value?: CoreFields): void;
|
||||
|
||||
hasMage(): boolean;
|
||||
clearMage(): void;
|
||||
getMage(): MageFields | undefined;
|
||||
setMage(value?: MageFields): void;
|
||||
|
||||
hasChangeling(): boolean;
|
||||
clearChangeling(): void;
|
||||
getChangeling(): ChangelingFields | undefined;
|
||||
setChangeling(value?: ChangelingFields): void;
|
||||
|
||||
getSystemFieldsCase(): CofdSheet.SystemFieldsCase;
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): CofdSheet.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: CofdSheet): CofdSheet.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: CofdSheet, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): CofdSheet;
|
||||
static deserializeBinaryFromReader(message: CofdSheet, reader: jspb.BinaryReader): CofdSheet;
|
||||
}
|
||||
|
||||
export namespace CofdSheet {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
gender: string,
|
||||
concept: string,
|
||||
age: number,
|
||||
player: string,
|
||||
chronicle: string,
|
||||
description: string,
|
||||
strength: number,
|
||||
dexterity: number,
|
||||
stamina: number,
|
||||
intelligence: number,
|
||||
wits: number,
|
||||
resolve: number,
|
||||
presence: number,
|
||||
manipulation: number,
|
||||
composure: number,
|
||||
physicalSkillsMap: Array<[string, CofdSheet.Skill.AsObject]>,
|
||||
mentalSkillsMap: Array<[string, CofdSheet.Skill.AsObject]>,
|
||||
socialSkillsMap: Array<[string, CofdSheet.Skill.AsObject]>,
|
||||
meritsList: Array<CofdSheet.Merit.AsObject>,
|
||||
conditionsList: Array<CofdSheet.Condition.AsObject>,
|
||||
size: number,
|
||||
health: number,
|
||||
willpower: number,
|
||||
experiencePoints: number,
|
||||
beats: number,
|
||||
itemsList: Array<CofdSheet.Item.AsObject>,
|
||||
attacksList: Array<CofdSheet.Attack.AsObject>,
|
||||
otherDataMap: Array<[string, string]>,
|
||||
core?: CoreFields.AsObject,
|
||||
mage?: MageFields.AsObject,
|
||||
changeling?: ChangelingFields.AsObject,
|
||||
}
|
||||
|
||||
export class Merit extends jspb.Message {
|
||||
getDots(): number;
|
||||
setDots(value: number): void;
|
||||
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Merit.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Merit): Merit.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Merit, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Merit;
|
||||
static deserializeBinaryFromReader(message: Merit, reader: jspb.BinaryReader): Merit;
|
||||
}
|
||||
|
||||
export namespace Merit {
|
||||
export type AsObject = {
|
||||
dots: number,
|
||||
name: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class Condition extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Condition.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Condition): Condition.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Condition, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Condition;
|
||||
static deserializeBinaryFromReader(message: Condition, reader: jspb.BinaryReader): Condition;
|
||||
}
|
||||
|
||||
export namespace Condition {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class Skill extends jspb.Message {
|
||||
getDots(): number;
|
||||
setDots(value: number): void;
|
||||
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getUntrainedPenalty(): number;
|
||||
setUntrainedPenalty(value: number): void;
|
||||
|
||||
clearSpecializationsList(): void;
|
||||
getSpecializationsList(): Array<string>;
|
||||
setSpecializationsList(value: Array<string>): void;
|
||||
addSpecializations(value: string, index?: number): string;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Skill.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Skill): Skill.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Skill, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Skill;
|
||||
static deserializeBinaryFromReader(message: Skill, reader: jspb.BinaryReader): Skill;
|
||||
}
|
||||
|
||||
export namespace Skill {
|
||||
export type AsObject = {
|
||||
dots: number,
|
||||
name: string,
|
||||
untrainedPenalty: number,
|
||||
specializationsList: Array<string>,
|
||||
}
|
||||
}
|
||||
|
||||
export class Item extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getDescription(): string;
|
||||
setDescription(value: string): void;
|
||||
|
||||
getRules(): string;
|
||||
setRules(value: string): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Item.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Item): Item.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Item, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Item;
|
||||
static deserializeBinaryFromReader(message: Item, reader: jspb.BinaryReader): Item;
|
||||
}
|
||||
|
||||
export namespace Item {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
description: string,
|
||||
rules: string,
|
||||
}
|
||||
}
|
||||
|
||||
export class Attack extends jspb.Message {
|
||||
getName(): string;
|
||||
setName(value: string): void;
|
||||
|
||||
getDicePool(): number;
|
||||
setDicePool(value: number): void;
|
||||
|
||||
getDamage(): number;
|
||||
setDamage(value: number): void;
|
||||
|
||||
getRange(): number;
|
||||
setRange(value: number): void;
|
||||
|
||||
getInitiativeModifier(): number;
|
||||
setInitiativeModifier(value: number): void;
|
||||
|
||||
getSize(): number;
|
||||
setSize(value: number): void;
|
||||
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): Attack.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: Attack): Attack.AsObject;
|
||||
static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
|
||||
static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
|
||||
static serializeBinaryToWriter(message: Attack, writer: jspb.BinaryWriter): void;
|
||||
static deserializeBinary(bytes: Uint8Array): Attack;
|
||||
static deserializeBinaryFromReader(message: Attack, reader: jspb.BinaryReader): Attack;
|
||||
}
|
||||
|
||||
export namespace Attack {
|
||||
export type AsObject = {
|
||||
name: string,
|
||||
dicePool: number,
|
||||
damage: number,
|
||||
range: number,
|
||||
initiativeModifier: number,
|
||||
size: number,
|
||||
}
|
||||
}
|
||||
|
||||
export enum SystemFieldsCase {
|
||||
SYSTEM_FIELDS_NOT_SET = 0,
|
||||
CORE = 30,
|
||||
MAGE = 31,
|
||||
CHANGELING = 32,
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
// package: models.proto.cofd
|
||||
// file: cofd.proto
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
// package: models.proto.cofd
|
||||
// file: cofd.proto
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import * as jspb from "google-protobuf";
|
||||
import { ApiResult, UpdateBasicInfoRequest, UpdateAttributeRequest, UpdateSkillValueRequest } from "../_proto/cofd_api_pb";
|
||||
|
||||
const PROTOBUF_CONTENT_TYPE = { 'Content-Type': 'application/x-protobuf' };
|
||||
|
||||
function staticImplements<T>() {
|
||||
return <U extends T>(constructor: U) => { constructor };
|
||||
}
|
||||
|
||||
async function makeRequest<T extends jspb.Message>(uri: string, params: T): Promise<Uint8Array> {
|
||||
let resp = await fetch(uri, {
|
||||
method: 'POST',
|
||||
headers: { ...PROTOBUF_CONTENT_TYPE },
|
||||
body: params.serializeBinary()
|
||||
});
|
||||
|
||||
const data = await resp.arrayBuffer();
|
||||
return new Uint8Array(data);
|
||||
}
|
||||
|
||||
export async function updateSkillValue(params: UpdateSkillValueRequest): Promise<ApiResult> {
|
||||
let data = await makeRequest('/api/rpc/cofd/update_skill_value', params);
|
||||
return ApiResult.deserializeBinary(data);
|
||||
}
|
||||
|
||||
export async function updateAttributeValue(params: UpdateAttributeRequest): Promise<ApiResult> {
|
||||
let data = await makeRequest('/api/rpc/cofd/update_attribute_value', params);
|
||||
return ApiResult.deserializeBinary(data);
|
||||
}
|
||||
|
||||
export async function updateBasicInfo(params: UpdateBasicInfoRequest): Promise<ApiResult> {
|
||||
let data = await makeRequest('/api/rpc/cofd/update_basic_info', params);
|
||||
return ApiResult.deserializeBinary(data);
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
import { CharacterIdentifier, UpdateBasicInfoRequest, UpdateSkillValueRequest, UpdateAttributeRequest } from "../../_proto/cofd_api_pb";
|
||||
import * as api from "../api";
|
||||
|
||||
// This is the scripting for the edit character page, which submits
|
||||
// changes to the server as the user makes them.
|
||||
|
||||
(async () => {
|
||||
// Useful for making sure elements actually exist in event handler.
|
||||
type Option<T> = T | null | undefined;
|
||||
|
||||
const [, , USERNAME, CHARACTER_ID] = window.location.pathname.split('/');
|
||||
|
||||
function characterId(): CharacterIdentifier {
|
||||
const id = new CharacterIdentifier();
|
||||
id.setId(parseInt(CHARACTER_ID));
|
||||
id.setOwner(USERNAME);
|
||||
return id;
|
||||
}
|
||||
|
||||
const getTextValue = (selector: string) =>
|
||||
document.querySelector<HTMLInputElement>(selector)?.value ?? "";
|
||||
|
||||
const getIntValue = (selector: string) =>
|
||||
parseInt(document.querySelector<HTMLInputElement>(selector)?.value ?? "0")
|
||||
|
||||
function setupAttributes() {
|
||||
const attributeInputs = document.querySelectorAll('#attributes input[type="number"]');
|
||||
|
||||
async function attributeHandler(event: Event) {
|
||||
const input = event.target as Option<HTMLInputElement>;
|
||||
if (!input) return;
|
||||
|
||||
console.log("updating attr");
|
||||
const attribute = input.id;
|
||||
const newValue = parseInt(input.value);
|
||||
const params = new UpdateAttributeRequest();
|
||||
params.setCharacter(characterId());
|
||||
params.setAttributeName(attribute);
|
||||
params.setAttributeValue(newValue);
|
||||
let resp = await api.updateAttributeValue(params);
|
||||
console.log("got a response back", resp);
|
||||
}
|
||||
|
||||
Array.from(attributeInputs).forEach(input => {
|
||||
input.addEventListener('change', attributeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
function setupSkills() {
|
||||
const skillInputs = document.querySelectorAll('#skills input[type="number"]');
|
||||
|
||||
async function skillValueHandler(event: Event) {
|
||||
const input = event.target as Option<HTMLInputElement>;
|
||||
if (!input) return;
|
||||
|
||||
console.log("updating skill");
|
||||
const attribute = input.id;
|
||||
const newValue = parseInt(input.value);
|
||||
|
||||
const params = new UpdateSkillValueRequest();
|
||||
params.setCharacter(characterId());
|
||||
params.setSkillName(attribute);
|
||||
params.setSkillValue(newValue);
|
||||
|
||||
let resp = await api.updateSkillValue(params);
|
||||
console.log("got a response back", resp);
|
||||
}
|
||||
|
||||
Array.from(skillInputs).forEach(input => {
|
||||
input.addEventListener('change', skillValueHandler);
|
||||
});
|
||||
}
|
||||
|
||||
function setupBasicInfo() {
|
||||
async function updateInfo() {
|
||||
const params = new UpdateBasicInfoRequest();
|
||||
|
||||
params.setCharacter(characterId());
|
||||
params.setName(getTextValue("#characterName"));
|
||||
params.setAge(getIntValue("#age"));
|
||||
params.setConcept(getTextValue("#concept"));
|
||||
params.setChronicle(getTextValue("#chronicle"));
|
||||
params.setGender(getTextValue("#gender"));
|
||||
|
||||
let resp = await api.updateBasicInfo(params);
|
||||
console.log("got a response back", resp);
|
||||
}
|
||||
|
||||
const inputs = document.querySelectorAll("#basicInfo input");
|
||||
|
||||
inputs.forEach(input => {
|
||||
console.log('got an input', input);
|
||||
input.addEventListener('blur', updateInfo);
|
||||
});
|
||||
}
|
||||
|
||||
setupAttributes();
|
||||
setupSkills();
|
||||
setupBasicInfo();
|
||||
})().catch(e => {
|
||||
alert(e);
|
||||
});
|
|
@ -84,13 +84,39 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/protobufjs@6.10.2/dist/protobuf.min.js"></script>
|
||||
<script defer type="text/javascript" src="/scripts/api.js"></script>
|
||||
<script defer type="text/javascript" src="/scripts/characters/edit.js"></script>
|
||||
{# Webpack Templating for API script #}
|
||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||
|
||||
<h1>Core Sheet</h1>
|
||||
<div>
|
||||
<h1>Name: <input type="text" value="{{name}}" /></h1>
|
||||
<p>System: {{data_type}}</p>
|
||||
<div id="basicInfo">
|
||||
<h1>
|
||||
<label for="characterName">Name:</label>
|
||||
<input type="text" id="characterName" name="characterName" value="{{name}}" />
|
||||
</h1>
|
||||
<div>System: {{data_type}}</div>
|
||||
|
||||
<div>
|
||||
<label for="gender">Gender:</label>
|
||||
<input type="text" id="gender" name="gender" value="{{sheet.gender}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="age">Age:</label>
|
||||
<input type="number" id="age" name="age" min="0" value="{{sheet.age}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="concept">Concept:</label>
|
||||
<input type="text" id="concept" name="concept" value="{{sheet.concept}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="chronicle">Chronicle:</label>
|
||||
<input type="text" id="chronicle" name="chronicle" value="{{sheet.chronicle}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="attributes">
|
||||
<div class="attributes-section" id="mentalAttributes">
|
||||
{{ macros::attribute(name="Intelligence", value=sheet.intelligence) }}
|
|
@ -1,8 +1,6 @@
|
|||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/protobufjs@6.10.2/dist/protobuf.min.js"></script>
|
||||
<script type="text/javascript" src="/scripts/characters/new-character.js"></script>
|
||||
<div>
|
||||
New character page.
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
|
||||
{% import "characters/view_character_macros" as macros %}
|
||||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Liberation Sans, Arial;
|
||||
}
|
||||
|
||||
#attributes {
|
||||
padding: 4px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#attributes .attributes-section {
|
||||
border: 1px solid gray;
|
||||
border-collapse: collapse;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.attribute {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.attribute label {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
clear: left;
|
||||
width: 10em;
|
||||
text-align: right;
|
||||
vertical-align: text-bottom;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.attribute div.value {
|
||||
min-width: 1.5em;
|
||||
max-width: 4em;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
padding: 8px;
|
||||
border: none;
|
||||
background-color: lightgray;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#skills {
|
||||
padding: 4px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#skills .skills-section {
|
||||
border: 1px solid gray;
|
||||
border-collapse: collapse;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.skill {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.skill label {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
clear: left;
|
||||
width: 10em;
|
||||
text-align: right;
|
||||
vertical-align: text-bottom;
|
||||
padding: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.skill div.value {
|
||||
min-width: 1.5em;
|
||||
max-width: 4em;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
float: left;
|
||||
padding: 8px;
|
||||
border: none;
|
||||
background-color: lightgray;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>Core Sheet</h1>
|
||||
<div>
|
||||
<h1>Character {{name}}</h1>
|
||||
<h3>User: {{username}}</h3>
|
||||
<p>System: {{data_type}}</h3>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>Core Sheet</h1>
|
||||
<div>
|
||||
<h1>Name: <input type="text" value="{{name}}" /></h1>
|
||||
<p>System: {{data_type}}</p>
|
||||
<div id="attributes">
|
||||
<div class="attributes-section" id="mentalAttributes">
|
||||
{{ macros::attribute(name="Intelligence", value=sheet.intelligence) }}
|
||||
{{ macros::attribute(name="Wits", value=sheet.wits) }}
|
||||
{{ macros::attribute(name="Resolve", value=sheet.resolve) }}
|
||||
</div>
|
||||
|
||||
<div class="attributes-section" id="physicalAttributes">
|
||||
{{ macros::attribute(name="Strength", value=sheet.strength) }}
|
||||
{{ macros::attribute(name="Dexterity", value=sheet.dexterity) }}
|
||||
{{ macros::attribute(name="Stamina", value=sheet.stamina) }}
|
||||
</div>
|
||||
|
||||
<div class="attributes-section" id="socicalAttributes">
|
||||
{{ macros::attribute(name="Presence", value=sheet.presence) }}
|
||||
{{ macros::attribute(name="Manipulation", value=sheet.manipulation) }}
|
||||
{{ macros::attribute(name="Composure", value=sheet.composure) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="skills">
|
||||
<div class="skills-section" id="mentalSkills">
|
||||
{% for skill_name, skill in sheet.mentalSkills %}
|
||||
{{ macros::skill(name=skill_name, value=skill.dots) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="skills-section" id="physicalSkills">
|
||||
{% for skill_name, skill in sheet.physicalSkills %}
|
||||
{{ macros::skill(name=skill_name, value=skill.dots) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="skills-section" id="socialSkills">
|
||||
{% for skill_name, skill in sheet.socialSkills %}
|
||||
{{ macros::skill(name=skill_name, value=skill.dots) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="/characters/{{username}}/{{id}}/edit">Edit Character</a>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,13 @@
|
|||
{% macro attribute(name, value) %}
|
||||
<div class="attribute">
|
||||
<label for="{{name}}">{{name}}:</label>
|
||||
<div class="value" id="{{name}}">{{value}}</div>
|
||||
</div>
|
||||
{% endmacro attribute %}
|
||||
|
||||
{% macro skill(name, value) %}
|
||||
<div class="skill">
|
||||
<label for="{{name}}">{{name}}:</label>
|
||||
<div class="value" id="{{name}}">{{value}}</div>
|
||||
</div>
|
||||
{% endmacro skill %}
|
|
@ -1,6 +1,7 @@
|
|||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
<%= htmlWebpackPlugin.tags.bodyTags %>
|
||||
<div>
|
||||
<h1>Tenebrous: Login</h1>
|
||||
|
72
src/main.rs
72
src/main.rs
|
@ -4,14 +4,11 @@ extern crate rocket;
|
|||
#[macro_use]
|
||||
extern crate rocket_contrib;
|
||||
|
||||
// Seemingly necessary to get serde::Serialize into scope for Prost
|
||||
// code generation.
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use log::{error, info};
|
||||
use rocket_contrib::serve::StaticFiles;
|
||||
use rocket_contrib::templates::Template;
|
||||
use std::env;
|
||||
use tonic::transport::Server;
|
||||
|
||||
pub mod catchers;
|
||||
pub mod db;
|
||||
|
@ -20,6 +17,40 @@ pub mod migrator;
|
|||
pub mod models;
|
||||
pub mod routes;
|
||||
|
||||
async fn make_rocket(database: sqlx::SqlitePool) -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!("Running Rocket");
|
||||
let root_routes: Vec<rocket::Route> = {
|
||||
routes::root::routes()
|
||||
.into_iter()
|
||||
.chain(routes::auth::routes().into_iter())
|
||||
.collect()
|
||||
};
|
||||
|
||||
let api_routes = routes::api::routes();
|
||||
let character_routes = routes::characters::routes();
|
||||
let catchers = catchers::catchers();
|
||||
|
||||
rocket::ignite()
|
||||
.attach(Template::fairing())
|
||||
.manage(database)
|
||||
.mount("/", root_routes)
|
||||
.mount("/characters", character_routes)
|
||||
.mount("/api", api_routes)
|
||||
.mount(
|
||||
"/scripts",
|
||||
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/generated/scripts")),
|
||||
)
|
||||
.mount(
|
||||
"/protos",
|
||||
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/proto")),
|
||||
)
|
||||
.register(catchers)
|
||||
.launch()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[rocket::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -33,34 +64,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
migrator::migrate(db_path).await?;
|
||||
|
||||
let root_routes: Vec<rocket::Route> = {
|
||||
routes::root::routes()
|
||||
.into_iter()
|
||||
.chain(routes::auth::routes().into_iter())
|
||||
.collect()
|
||||
};
|
||||
let db = crate::db::create_pool(db_path).await?;
|
||||
|
||||
let api_routes = routes::api::routes();
|
||||
let character_routes = routes::characters::routes();
|
||||
let catchers = catchers::catchers();
|
||||
|
||||
rocket::ignite()
|
||||
.attach(Template::fairing())
|
||||
//.attach(db::TenebrousDbConn::fairing())
|
||||
.manage(crate::db::create_pool(db_path).await?)
|
||||
.mount("/", root_routes)
|
||||
.mount("/characters", character_routes)
|
||||
.mount("/api", api_routes)
|
||||
.mount(
|
||||
"/scripts",
|
||||
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/static/scripts")),
|
||||
)
|
||||
.mount(
|
||||
"/protos",
|
||||
StaticFiles::from(concat!(env!("CARGO_MANIFEST_DIR"), "/proto")),
|
||||
)
|
||||
.register(catchers)
|
||||
.launch()
|
||||
.await
|
||||
.map_err(|e| e.into())
|
||||
make_rocket(db.clone()).await
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::errors::Error;
|
|||
use crate::models::proto::cofd::*;
|
||||
use crate::models::users::User;
|
||||
use prost::bytes::BytesMut;
|
||||
use serde_derive::Serialize;
|
||||
use serde::Serialize;
|
||||
use strum::{EnumIter, EnumString};
|
||||
|
||||
/// Dynamic character data is an opaque container type that holds
|
||||
|
@ -146,8 +146,8 @@ impl Character {
|
|||
}
|
||||
|
||||
/// Update the existing character with new serialized protobuf
|
||||
/// data. Consumes the data.
|
||||
pub fn update_data<T>(&mut self, data: T) -> Result<(), Error>
|
||||
/// data.
|
||||
pub fn update_data<T>(&mut self, data: &T) -> Result<(), Error>
|
||||
where
|
||||
T: prost::Message + std::default::Default,
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::characters::CharacterDataType;
|
||||
use rocket::http::RawStr;
|
||||
use rocket::request::FromFormValue;
|
||||
use serde::Serialize;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
use crate::errors::Error;
|
||||
use prost::bytes::BytesMut;
|
||||
use rocket::data::{Data, FromData, Outcome, ToByteUnit};
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::request::Request;
|
||||
use rocket::response::status;
|
||||
use rocket::response::{self, Responder, Response};
|
||||
use std::default::Default;
|
||||
use std::ops::Deref;
|
||||
use std::io::Cursor;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub mod cofd;
|
||||
|
||||
const CRATE_NAME: &'static str = env!("CARGO_BIN_NAME");
|
||||
|
||||
/// Convert an incoming protobuf content-type to the equivalent type
|
||||
/// name produced by std::any::type_name(). Currently does NOT work
|
||||
/// with nested types due to how prost generates the module names.
|
||||
fn convert_to_rust_name(proto_type: &str) -> String {
|
||||
format!("{}::{}", CRATE_NAME, proto_type.replace(".", "::"))
|
||||
}
|
||||
|
||||
/// A struct wrapping a protobuf that allows it to be used as binary
|
||||
/// data submitted via POST using fetch API. Can automatically be
|
||||
/// dereferenced into its wrapped type.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Proto<T>(T)
|
||||
pub(crate) struct Proto<T>(pub T)
|
||||
where
|
||||
T: prost::Message + Default;
|
||||
|
||||
|
@ -40,21 +36,10 @@ where
|
|||
.map(|ct| ct.top() == "application" && ct.sub() == "x-protobuf")
|
||||
.unwrap_or(false);
|
||||
|
||||
let message_type: Option<String> = content_type.and_then(|ct| {
|
||||
ct.params()
|
||||
.find(|&(name, _)| name == "messageType")
|
||||
.map(|(_, message_type)| convert_to_rust_name(message_type))
|
||||
});
|
||||
|
||||
if !is_protobuf {
|
||||
return Outcome::Failure((Status::new(422, "invalid protobuf"), Error::InvalidInput));
|
||||
}
|
||||
|
||||
if message_type.as_ref().map(String::as_str) != Some(std::any::type_name::<T>()) {
|
||||
println!("message type is {:?}", message_type);
|
||||
return Outcome::Forward(data);
|
||||
}
|
||||
|
||||
let bytes: Vec<u8> = match data.open(2.mebibytes()).stream_to_vec().await {
|
||||
Ok(read_bytes) => read_bytes,
|
||||
Err(e) => return Outcome::Failure((Status::new(422, "invalid protobuf"), e.into())),
|
||||
|
@ -67,6 +52,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'r, T> Responder<'r, 'static> for Proto<T>
|
||||
where
|
||||
T: prost::Message + Default,
|
||||
{
|
||||
fn respond_to(self, req: &Request) -> response::Result<'static> {
|
||||
let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&self.0));
|
||||
match self.0.encode(&mut buf) {
|
||||
Ok(_) => Response::build()
|
||||
.header(ContentType::new("application", "x-protobuf"))
|
||||
.sized_body(buf.len(), Cursor::new(buf))
|
||||
.ok(),
|
||||
Err(e) => status::Custom(Status::InternalServerError, e.to_string()).respond_to(req),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable automatically calling methods on a decoded Proto instance.
|
||||
impl<T> Deref for Proto<T>
|
||||
where
|
||||
|
@ -78,3 +79,12 @@ where
|
|||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Proto<T>
|
||||
where
|
||||
T: prost::Message + Default,
|
||||
{
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,50 @@ use crate::models::characters::CharacterDataType;
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
//Add the generated protobuf code into this module.
|
||||
include!(concat!(env!("OUT_DIR"), "/models.proto.cofd.rs"));
|
||||
//include!(concat!(env!("OUT_DIR"), "/models.proto.cofd.rs"));
|
||||
tonic::include_proto!("models.proto.cofd");
|
||||
|
||||
//Add the API protobuf genreated code for the api module.
|
||||
pub mod api {
|
||||
include!(concat!(env!("OUT_DIR"), "/models.proto.cofd.api.rs"));
|
||||
//include!(concat!(env!("OUT_DIR"), "/models.proto.cofd.api.rs"));
|
||||
tonic::include_proto!("models.proto.cofd.api");
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Trait to extract values out of a CharacterIdentifier whose
|
||||
/// instance may or may not exist on an API request.
|
||||
pub trait DefaultCharacterIdentifier {
|
||||
/// Retrieve the specified owner, or a default fallback (empty
|
||||
/// string). Will not allocate if the owner is present in the
|
||||
/// request.
|
||||
fn owner(&self) -> Cow<'_, str>;
|
||||
|
||||
/// Retrieve the character ID specified, or the default i32
|
||||
/// value (0).
|
||||
fn id(&self) -> i32;
|
||||
}
|
||||
|
||||
impl DefaultCharacterIdentifier for Option<CharacterIdentifier> {
|
||||
fn owner(&self) -> Cow<'_, str> {
|
||||
self.as_ref()
|
||||
.map(|ident| Cow::from(&ident.owner))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn id(&self) -> i32 {
|
||||
self.as_ref().map(|ident| ident.id).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Helpers for the ApiResult class.
|
||||
impl ApiResult {
|
||||
pub fn success() -> Self {
|
||||
ApiResult {
|
||||
success: true,
|
||||
error: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Default mental skill names for a regular Chronicles of Darkness
|
||||
|
|
|
@ -3,7 +3,7 @@ use argon2::{self, Config, Error as ArgonError};
|
|||
use rand::Rng;
|
||||
use rocket::outcome::IntoOutcome;
|
||||
use rocket::request::{self, FromRequest, Request};
|
||||
use serde_derive::Serialize;
|
||||
use serde::Serialize;
|
||||
|
||||
pub(crate) fn hash_password(raw_password: &str) -> Result<String, ArgonError> {
|
||||
let salt = rand::thread_rng().gen::<[u8; 16]>();
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::errors::Error;
|
||||
use crate::models::characters::{Character, CharacterDataType, DynCharacterData, Visibility};
|
||||
use crate::models::proto::cofd::*;
|
||||
use crate::models::characters::Character;
|
||||
use crate::models::users::User;
|
||||
use rocket_contrib::templates::Template;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::btree_map::{Entry, OccupiedEntry};
|
||||
|
||||
mod cofd;
|
||||
|
||||
pub(crate) fn routes() -> Vec<rocket::Route> {
|
||||
routes![
|
||||
cofd::update_basic_info,
|
||||
cofd::update_attributes,
|
||||
cofd::update_attribute,
|
||||
cofd::update_skills,
|
||||
cofd::update_attribute_value,
|
||||
cofd::update_skill,
|
||||
cofd::update_skill_value,
|
||||
cofd::add_condition,
|
||||
cofd::remove_condition
|
||||
|
@ -43,169 +39,3 @@ async fn load_character(
|
|||
|
||||
Ok(character)
|
||||
}
|
||||
|
||||
/// Protobuf-based REST endpoints for editing a character.
|
||||
mod cofd {
|
||||
use super::*;
|
||||
use crate::models::proto::cofd::cofd_sheet::Skill;
|
||||
use crate::models::proto::{cofd::api::*, cofd::*, Proto};
|
||||
|
||||
fn find_skill_entry<'a>(
|
||||
sheet: &'a mut CofdSheet,
|
||||
skill_name: &'a str,
|
||||
) -> Option<OccupiedEntry<'a, String, Skill>> {
|
||||
let all_skills = vec![
|
||||
&mut sheet.mental_skills,
|
||||
&mut sheet.physical_skills,
|
||||
&mut sheet.social_skills,
|
||||
];
|
||||
|
||||
// Search all skill lists for this value using "workaround" to
|
||||
// break value from for loops.
|
||||
let skill: Option<OccupiedEntry<_, _>> = 'l: loop {
|
||||
for skill_map in all_skills {
|
||||
if let Entry::Occupied(entry) = skill_map.entry(skill_name.to_owned()) {
|
||||
break 'l Some(entry);
|
||||
}
|
||||
}
|
||||
|
||||
break None;
|
||||
};
|
||||
|
||||
skill
|
||||
}
|
||||
|
||||
fn find_skill<'a>(sheet: &'a mut CofdSheet, skill_name: &'a str) -> Option<&'a mut Skill> {
|
||||
find_skill_entry(sheet, skill_name).map(|entry| entry.into_mut())
|
||||
}
|
||||
|
||||
#[post("/cofd/<owner>/<character_id>/basic-info", data = "<info>")]
|
||||
pub(super) fn update_basic_info<'a>(
|
||||
owner: String,
|
||||
character_id: i32,
|
||||
info: Proto<BasicInfo>,
|
||||
) -> &'a str {
|
||||
"lol"
|
||||
}
|
||||
|
||||
#[post("/cofd/<owner>/<character_id>/attributes", data = "<info>")]
|
||||
pub(super) fn update_attributes<'a>(
|
||||
owner: String,
|
||||
character_id: i32,
|
||||
info: Proto<Attributes>,
|
||||
) -> &'a str {
|
||||
"lol"
|
||||
}
|
||||
|
||||
#[post("/rpc/cofd/update_attribute", data = "<req>")]
|
||||
pub(super) async fn update_attribute<'a>(
|
||||
req: Proto<UpdateAttributeRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<&'a str, Error> {
|
||||
let mut character = load_character(
|
||||
&conn,
|
||||
logged_in_user,
|
||||
&req.character_username,
|
||||
req.character_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
let value = req.attribute_value;
|
||||
match req.attribute_name.to_lowercase().as_ref() {
|
||||
"strength" => Ok(sheet.strength = value),
|
||||
"dexterity" => Ok(sheet.dexterity = value),
|
||||
"stamina" => Ok(sheet.stamina = value),
|
||||
"intelligence" => Ok(sheet.intelligence = value),
|
||||
"wits" => Ok(sheet.wits = value),
|
||||
"resolve" => Ok(sheet.resolve = value),
|
||||
"presence" => Ok(sheet.presence = value),
|
||||
"manipulation" => Ok(sheet.manipulation = value),
|
||||
"composure" => Ok(sheet.composure = value),
|
||||
_ => Err(Error::InvalidInput),
|
||||
}?;
|
||||
|
||||
character.update_data(sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok("lol")
|
||||
}
|
||||
|
||||
#[patch(
|
||||
"/cofd/<owner>/<character_id>/skills",
|
||||
data = "<skill_update>",
|
||||
rank = 1
|
||||
)]
|
||||
pub(super) async fn update_skills<'a>(
|
||||
owner: String,
|
||||
character_id: i32,
|
||||
skill_update: Proto<SkillUpdate>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<&'a str, Error> {
|
||||
let mut character = load_character(&conn, logged_in_user, &owner, character_id).await?;
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
let updated_skill: &Skill = skill_update.skill.as_ref().ok_or(Error::InvalidInput)?;
|
||||
let skill_entry = find_skill_entry(&mut sheet, &skill_update.name);
|
||||
|
||||
skill_entry
|
||||
.map(|mut entry| entry.insert(updated_skill.clone()))
|
||||
.ok_or(Error::InvalidInput)?;
|
||||
|
||||
println!(
|
||||
"updated skill {} with {:?}",
|
||||
skill_update.name, skill_update.skill
|
||||
);
|
||||
|
||||
character.update_data(sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok("lol")
|
||||
}
|
||||
|
||||
#[post("/rpc/cofd/update_skill_value", data = "<request>")]
|
||||
pub(super) async fn update_skill_value<'a>(
|
||||
request: Proto<UpdateSkillValueRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<&'a str, Error> {
|
||||
println!("{:#?}", request);
|
||||
let mut character = load_character(
|
||||
&conn,
|
||||
logged_in_user,
|
||||
&request.character_username,
|
||||
request.character_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
let skill: Option<&mut Skill> = find_skill(&mut sheet, &request.skill_name);
|
||||
|
||||
skill
|
||||
.map(|s| s.dots = request.skill_value)
|
||||
.ok_or(Error::InvalidInput)?;
|
||||
|
||||
println!("updated skill value",);
|
||||
|
||||
character.update_data(sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok("lol")
|
||||
}
|
||||
|
||||
#[put("/cofd/<owner>/<character_id>/conditions", data = "<info>")]
|
||||
pub(super) fn add_condition<'a>(
|
||||
owner: String,
|
||||
character_id: i32,
|
||||
info: Proto<Condition>,
|
||||
) -> &'a str {
|
||||
"lol"
|
||||
}
|
||||
|
||||
#[delete("/cofd/<owner>/<character_id>/conditions", data = "<info>")]
|
||||
pub(super) fn remove_condition<'a>(
|
||||
owner: String,
|
||||
character_id: i32,
|
||||
info: Proto<Condition>,
|
||||
) -> &'a str {
|
||||
"lol"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
use super::load_character;
|
||||
use crate::db::{Dao, TenebrousDbConn};
|
||||
use crate::errors::Error;
|
||||
use crate::models::characters::Character;
|
||||
use crate::models::proto::cofd::api::DefaultCharacterIdentifier;
|
||||
use crate::models::proto::cofd::cofd_sheet::Skill;
|
||||
use crate::models::proto::cofd::*;
|
||||
use crate::models::proto::{cofd::api::*, cofd::*, Proto};
|
||||
use crate::models::users::User;
|
||||
use std::collections::btree_map::{Entry, OccupiedEntry};
|
||||
|
||||
fn find_skill_entry<'a>(
|
||||
sheet: &'a mut CofdSheet,
|
||||
skill_name: &'a str,
|
||||
) -> Option<OccupiedEntry<'a, String, Skill>> {
|
||||
let all_skills = vec![
|
||||
&mut sheet.mental_skills,
|
||||
&mut sheet.physical_skills,
|
||||
&mut sheet.social_skills,
|
||||
];
|
||||
|
||||
// Search all skill lists for this value using "workaround" to
|
||||
// break value from for loops.
|
||||
let skill: Option<OccupiedEntry<_, _>> = 'l: loop {
|
||||
for skill_map in all_skills {
|
||||
if let Entry::Occupied(entry) = skill_map.entry(skill_name.to_owned()) {
|
||||
break 'l Some(entry);
|
||||
}
|
||||
}
|
||||
|
||||
break None;
|
||||
};
|
||||
|
||||
skill
|
||||
}
|
||||
|
||||
fn find_skill<'a>(sheet: &'a mut CofdSheet, skill_name: &'a str) -> Option<&'a mut Skill> {
|
||||
find_skill_entry(sheet, skill_name).map(|entry| entry.into_mut())
|
||||
}
|
||||
|
||||
#[post("/rpc/cofd/update_basic_info", data = "<req>")]
|
||||
pub(super) async fn update_basic_info<'a>(
|
||||
req: Proto<UpdateBasicInfoRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
user: Option<&User>,
|
||||
) -> Result<Proto<ApiResult>, Error> {
|
||||
let mut character =
|
||||
load_character(&conn, user, &req.character.owner(), req.character.id()).await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
|
||||
println!("name will now be {}", req.name);
|
||||
character.character_name = req.name.clone(); //Should probably remove name from the sheet?
|
||||
sheet.name = req.name.clone();
|
||||
sheet.gender = req.gender.clone();
|
||||
sheet.concept = req.concept.clone();
|
||||
sheet.chronicle = req.chronicle.clone();
|
||||
sheet.age = req.age;
|
||||
|
||||
character.update_data(&sheet)?;
|
||||
conn.update_character(&character).await?;
|
||||
println!("Updated basic info");
|
||||
Ok(Proto(ApiResult::success()))
|
||||
}
|
||||
|
||||
#[post("/rpc/cofd/update_attribute_value", data = "<req>")]
|
||||
pub(super) async fn update_attribute_value(
|
||||
req: Proto<UpdateAttributeRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<Proto<ApiResult>, Error> {
|
||||
let mut character = load_character(
|
||||
&conn,
|
||||
logged_in_user,
|
||||
&req.character.owner(),
|
||||
req.character.id(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
let value = req.attribute_value;
|
||||
match req.attribute_name.to_lowercase().as_ref() {
|
||||
"strength" => Ok(sheet.strength = value),
|
||||
"dexterity" => Ok(sheet.dexterity = value),
|
||||
"stamina" => Ok(sheet.stamina = value),
|
||||
"intelligence" => Ok(sheet.intelligence = value),
|
||||
"wits" => Ok(sheet.wits = value),
|
||||
"resolve" => Ok(sheet.resolve = value),
|
||||
"presence" => Ok(sheet.presence = value),
|
||||
"manipulation" => Ok(sheet.manipulation = value),
|
||||
"composure" => Ok(sheet.composure = value),
|
||||
_ => Err(Error::InvalidInput),
|
||||
}?;
|
||||
|
||||
character.update_data(&sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok(Proto(ApiResult {
|
||||
success: true,
|
||||
error: "".to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[patch("/rpc/cofd/update_skill", data = "<skill_update>")]
|
||||
pub(super) async fn update_skill<'a>(
|
||||
skill_update: Proto<UpdateSkillRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<&'a str, Error> {
|
||||
let mut character = load_character(
|
||||
&conn,
|
||||
logged_in_user,
|
||||
&skill_update.character.owner(),
|
||||
skill_update.character.id(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
let updated_skill: &Skill = skill_update.skill.as_ref().ok_or(Error::InvalidInput)?;
|
||||
let skill_entry = find_skill_entry(&mut sheet, &updated_skill.name);
|
||||
|
||||
skill_entry
|
||||
.map(|mut entry| entry.insert(updated_skill.clone()))
|
||||
.ok_or(Error::InvalidInput)?;
|
||||
|
||||
println!(
|
||||
"updated skill {} with {:?}",
|
||||
updated_skill.name, skill_update.skill
|
||||
);
|
||||
|
||||
character.update_data(&sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok("lol")
|
||||
}
|
||||
|
||||
#[post("/rpc/cofd/update_skill_value", data = "<req>")]
|
||||
pub(super) async fn update_skill_value<'a>(
|
||||
req: Proto<UpdateSkillValueRequest>,
|
||||
conn: TenebrousDbConn<'_>,
|
||||
logged_in_user: Option<&User>,
|
||||
) -> Result<Proto<ApiResult>, Error> {
|
||||
let mut character = load_character(
|
||||
&conn,
|
||||
logged_in_user,
|
||||
&req.character.owner(),
|
||||
req.character.id(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut sheet: CofdSheet = character.try_deserialize()?;
|
||||
|
||||
let mut skill: Option<&mut Skill> = find_skill(&mut sheet, &req.skill_name);
|
||||
if let Some(ref mut s) = skill {
|
||||
s.dots = req.skill_value;
|
||||
}
|
||||
|
||||
println!("updated skill value");
|
||||
|
||||
character.update_data(&sheet)?;
|
||||
conn.update_character_sheet(&character).await?;
|
||||
Ok(Proto(ApiResult {
|
||||
success: true,
|
||||
error: "".to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[put("/rpc/cofd/add_condition", data = "<info>")]
|
||||
pub(super) fn add_condition<'a>(info: Proto<AddConditionRequest>) -> &'a str {
|
||||
"lol"
|
||||
}
|
||||
|
||||
#[delete("/rpc/cofd/remove_condition", data = "<info>")]
|
||||
pub(super) fn remove_condition<'a>(info: Proto<RemoveConditionRequest>) -> &'a str {
|
||||
"lol"
|
||||
}
|
|
@ -3,6 +3,7 @@ use crate::errors::Error;
|
|||
use crate::models::characters::{Character, CharacterDataType, DynCharacterData, Visibility};
|
||||
use crate::models::users::User;
|
||||
use rocket_contrib::templates::Template;
|
||||
use serde::Serialize;
|
||||
|
||||
mod edit;
|
||||
mod new;
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::models::{
|
|||
};
|
||||
use rocket::{request::Form, response::Redirect};
|
||||
use rocket_contrib::templates::Template;
|
||||
use serde::Serialize;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
/// Form submission for creating a new character.
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::models::characters::Visibility;
|
|||
use crate::models::{characters::StrippedCharacter, users::User};
|
||||
use rocket::response::Redirect;
|
||||
use rocket_contrib::templates::Template;
|
||||
use serde_derive::Serialize;
|
||||
use serde::Serialize;
|
||||
|
||||
pub fn routes() -> Vec<rocket::Route> {
|
||||
routes![index, user_index, proto_test]
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
function makeAPI(root) {
|
||||
//Protobuf types
|
||||
const UpdateAttributeRequestType = 'models.proto.cofd.api.UpdateAttributeRequest';
|
||||
const UpdateAttributeRequest = root.lookupType(UpdateAttributeRequestType);
|
||||
|
||||
const UpdateSkillValueRequestType = 'models.proto.cofd.api.UpdateSkillValueRequest';
|
||||
const UpdateSkillValueRequest = root.lookupType(UpdateSkillValueRequestType);
|
||||
|
||||
//TODO rpc-ify
|
||||
const SkillSpecializationUpdateType = 'models.proto.cofd.api.SkillSpecializationsUpdate';
|
||||
const SkillSpecializationsUpdate = root.lookupType(SkillSpecializationUpdateType);
|
||||
|
||||
const protobufContentType = (messageType) =>
|
||||
({ 'Content-Type': 'application/x-protobuf; messageType="' + messageType + '"' });
|
||||
|
||||
function verifyAndCreate(protobufType, payload) {
|
||||
let err = protobufType.verify(payload);
|
||||
if (err) throw err;
|
||||
return protobufType.create(payload);
|
||||
}
|
||||
|
||||
async function updateAttribute(params) {
|
||||
const { username, characterID, attribute, newValue } = params;
|
||||
|
||||
let req = verifyAndCreate(UpdateAttributeRequest, {
|
||||
characterUsername: username,
|
||||
characterId: parseInt(characterID),
|
||||
attributeName: attribute,
|
||||
attributeValue: parseInt(newValue)
|
||||
});
|
||||
|
||||
let resp = await fetch('/api/rpc/cofd/update_attribute', {
|
||||
method: 'POST',
|
||||
headers: { ... protobufContentType(UpdateAttributeRequestType) },
|
||||
body: UpdateAttributeRequest.encode(req).finish()
|
||||
}).then(async resp => {
|
||||
console.log("resp is", await resp.text());
|
||||
}).catch(async err => {
|
||||
console.log("err is", err.text());
|
||||
});
|
||||
}
|
||||
|
||||
async function updateSkillValue(params) {
|
||||
const { username, characterID, skillName, newValue } = params;
|
||||
|
||||
let req = verifyAndCreate(UpdateSkillValueRequest, {
|
||||
characterUsername: username,
|
||||
characterId: parseInt(characterID),
|
||||
skillName: skillName,
|
||||
skillValue: parseInt(newValue)
|
||||
});
|
||||
|
||||
let resp = await fetch('/api/rpc/cofd/update_skill_value', {
|
||||
method: 'POST',
|
||||
headers: { ... protobufContentType(UpdateSkillValueRequestType) },
|
||||
body: UpdateSkillValueRequest.encode(req).finish()
|
||||
}).then(async resp => {
|
||||
console.log("resp is", await resp.text());
|
||||
}).catch(async err => {
|
||||
console.log("err is", err.text());
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
updateAttribute,
|
||||
updateSkillValue
|
||||
};
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
(async () => {
|
||||
//TODO start refactoring these into a separate script, and make API calls
|
||||
//take all necessary info (e.g. username and character ID, plus other stuff)
|
||||
//as object params.
|
||||
const root = await protobuf.load("/protos/cofd_api.proto");
|
||||
|
||||
const [, , USERNAME, CHARACTER_ID] = window.location.pathname.split('/');
|
||||
|
||||
const api = makeAPI(root);
|
||||
console.log("api is", api);
|
||||
|
||||
function setupAttributes() {
|
||||
const attributeInputs = document.querySelectorAll('#attributes input[type="number"]');
|
||||
|
||||
async function attributeHandler(event) {
|
||||
console.log("updating attr");
|
||||
const attribute = event.target.id;
|
||||
const newValue = parseInt(event.target.value);
|
||||
const params = { username: USERNAME, characterID: CHARACTER_ID, attribute, newValue };
|
||||
await api.updateAttribute(params);
|
||||
}
|
||||
|
||||
Array.from(attributeInputs).forEach(input => {
|
||||
input.addEventListener('change', attributeHandler);
|
||||
});
|
||||
}
|
||||
|
||||
function setupSkills() {
|
||||
const attributeInputs = document.querySelectorAll('#skills input[type="number"]');
|
||||
|
||||
async function skillValueHandler(event) {
|
||||
console.log("updating skill value");
|
||||
const skillName = event.target.id;
|
||||
const newValue = parseInt(event.target.value);
|
||||
const params = { username: USERNAME, characterID: CHARACTER_ID, skillName, newValue };
|
||||
await api.updateSkillValue(params);
|
||||
}
|
||||
|
||||
Array.from(attributeInputs).forEach(input => {
|
||||
input.addEventListener('change', skillValueHandler);
|
||||
});
|
||||
}
|
||||
|
||||
setupAttributes();
|
||||
setupSkills();
|
||||
})().catch(e => {
|
||||
alert(e);
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
document.addEventListener('DOMContentLoaded', event => {
|
||||
protobuf.load("/protos/cofd.proto").then(function(root) {
|
||||
console.log("root is", root);
|
||||
let CofdSheet = root.lookupType("models.proto.cofd.CofdSheet");
|
||||
let sheet = CofdSheet.fromObject({ name: 'lol', strength: 100 });
|
||||
let buffer = CofdSheet.encode(sheet).finish();
|
||||
|
||||
fetch('/proto-test', {
|
||||
method: 'POST',
|
||||
body: buffer
|
||||
}).then(async resp => {
|
||||
console.log("resp is", await resp.text());
|
||||
}).catch(async err => {
|
||||
console.log("err is", err.text());
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
{% extends "base" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Core Sheet</h1>
|
||||
<div>
|
||||
<h1>Character {{name}}</h1>
|
||||
<h3>User: {{username}}</h3>
|
||||
<p>System: {{data_type}}</h3>
|
||||
<p>Strength: {{sheet.strength}}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="/characters/{{username}}/{{id}}/edit">Edit Character</a>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"alwaysStrict": true,
|
||||
"sourceMap": true,
|
||||
"target": "es5",
|
||||
"removeComments": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"stripInternal": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noEmitOnError": true,
|
||||
"lib": [
|
||||
"dom"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
const webpack = require('webpack');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
const root = path.resolve(__dirname, '.');
|
||||
|
||||
function packPage(page, chunks) {
|
||||
if (!chunks) chunks = [];
|
||||
return new HtmlWebpackPlugin({
|
||||
template: `${root}/src/frontend/templates/${page}`,
|
||||
filename: `${root}/generated/templates/${page}`,
|
||||
publicPath: '/',
|
||||
scriptLoading: 'defer',
|
||||
chunks: chunks,
|
||||
inject: false,
|
||||
minify: false
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
edit_character: "./src/frontend/scripts/characters/edit.ts",
|
||||
},
|
||||
optimization: {
|
||||
runtimeChunk: "single",
|
||||
splitChunks: {
|
||||
chunks: 'all',
|
||||
},
|
||||
},
|
||||
mode: "development",
|
||||
output: {
|
||||
path: `${root}/generated`,
|
||||
filename: 'scripts/dist/[name].bundle.js'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
include: /src|_proto/,
|
||||
exclude: /node_modules/,
|
||||
loader: "ts-loader"
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".ts", ".js"]
|
||||
},
|
||||
plugins: [
|
||||
new CleanWebpackPlugin({
|
||||
cleanOnceBeforeBuildPatterns: ['!templates/**/*'],
|
||||
cleanAfterEveryBuildPatterns: ['!templates/**/*'],
|
||||
dry: false
|
||||
}),
|
||||
packPage('login.html.tera'),
|
||||
packPage('base.html.tera'),
|
||||
packPage('error.html.tera'),
|
||||
packPage('index.html.tera'),
|
||||
packPage('registration.html.tera'),
|
||||
packPage('characters/edit_character.html.tera', ['edit_character']),
|
||||
packPage('characters/edit_character_macros.html.tera'),
|
||||
packPage('characters/new_character.html.tera'),
|
||||
packPage('characters/view_character.html.tera'),
|
||||
packPage('characters/view_character_macros.html.tera'),
|
||||
]
|
||||
};
|
Loading…
Reference in New Issue