tangled
alpha
login
or
join now
eldridge.cam
/
cartography
0
fork
atom
Trading card city builder game?
0
fork
atom
overview
issues
pulls
pipelines
connect custom websocket from client side
eldridge.cam
1 month ago
bf288b4b
77e541ee
0/0
Waiting for spindle ...
+508
-112
14 changed files
expand all
collapse all
unified
split
Cargo.lock
Cargo.toml
Justfile
src
actor
player_socket
mod.rs
server
authenticate.rs
player_socket.rs
api
ws.rs
api.rs
app
hooks
mod.rs
use_custom_websocket.rs
menu.rs
mod.rs
play.rs
main.rs
+214
-23
Cargo.lock
···
149
149
"bytes",
150
150
"form_urlencoded",
151
151
"futures-util",
152
152
-
"http",
152
152
+
"http 1.4.0",
153
153
"http-body",
154
154
"http-body-util",
155
155
"hyper",
···
183
183
dependencies = [
184
184
"bytes",
185
185
"futures-core",
186
186
-
"http",
186
186
+
"http 1.4.0",
187
187
"http-body",
188
188
"http-body-util",
189
189
"mime",
···
205
205
"bytes",
206
206
"futures-util",
207
207
"headers",
208
208
-
"http",
208
208
+
"http 1.4.0",
209
209
"http-body",
210
210
"http-body-util",
211
211
"mime",
···
245
245
version = "1.8.3"
246
246
source = "registry+https://github.com/rust-lang/crates.io-index"
247
247
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
248
248
+
249
249
+
[[package]]
250
250
+
name = "bincode"
251
251
+
version = "1.3.3"
252
252
+
source = "registry+https://github.com/rust-lang/crates.io-index"
253
253
+
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
254
254
+
dependencies = [
255
255
+
"serde",
256
256
+
]
248
257
249
258
[[package]]
250
259
name = "bitflags"
···
335
344
name = "cartography"
336
345
version = "0.1.0"
337
346
dependencies = [
347
347
+
"anyhow",
338
348
"axum",
339
349
"derive_more 2.1.1",
340
350
"dioxus",
341
351
"futures",
342
352
"futures-rx",
353
353
+
"gloo",
343
354
"kameo",
344
355
"rmp-serde",
345
356
"serde",
···
947
958
checksum = "c0161af1d3cfc8ff31503ff1b7ee0068c97771fc38d0cc6566e23483142ddf4f"
948
959
dependencies = [
949
960
"dioxus-cli-config",
950
950
-
"http",
961
961
+
"http 1.4.0",
951
962
"infer",
952
963
"jni",
953
964
"js-sys",
···
1163
1174
"futures",
1164
1175
"futures-channel",
1165
1176
"futures-util",
1166
1166
-
"gloo-net",
1177
1177
+
"gloo-net 0.6.0",
1167
1178
"headers",
1168
1168
-
"http",
1179
1179
+
"http 1.4.0",
1169
1180
"http-body",
1170
1181
"http-body-util",
1171
1182
"inventory",
···
1215
1226
"futures-channel",
1216
1227
"futures-util",
1217
1228
"generational-box",
1218
1218
-
"http",
1229
1229
+
"http 1.4.0",
1219
1230
"inventory",
1220
1231
"parking_lot",
1221
1232
"serde",
···
1446
1457
"futures-channel",
1447
1458
"futures-util",
1448
1459
"generational-box",
1449
1449
-
"http",
1460
1460
+
"http 1.4.0",
1450
1461
"http-body-util",
1451
1462
"hyper",
1452
1463
"hyper-util",
···
2272
2283
]
2273
2284
2274
2285
[[package]]
2286
2286
+
name = "gloo"
2287
2287
+
version = "0.11.0"
2288
2288
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2289
2289
+
checksum = "d15282ece24eaf4bd338d73ef580c6714c8615155c4190c781290ee3fa0fd372"
2290
2290
+
dependencies = [
2291
2291
+
"gloo-console",
2292
2292
+
"gloo-dialogs",
2293
2293
+
"gloo-events",
2294
2294
+
"gloo-file",
2295
2295
+
"gloo-history",
2296
2296
+
"gloo-net 0.5.0",
2297
2297
+
"gloo-render",
2298
2298
+
"gloo-storage",
2299
2299
+
"gloo-timers",
2300
2300
+
"gloo-utils",
2301
2301
+
"gloo-worker",
2302
2302
+
]
2303
2303
+
2304
2304
+
[[package]]
2305
2305
+
name = "gloo-console"
2306
2306
+
version = "0.3.0"
2307
2307
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2308
2308
+
checksum = "2a17868f56b4a24f677b17c8cb69958385102fa879418052d60b50bc1727e261"
2309
2309
+
dependencies = [
2310
2310
+
"gloo-utils",
2311
2311
+
"js-sys",
2312
2312
+
"serde",
2313
2313
+
"wasm-bindgen",
2314
2314
+
"web-sys",
2315
2315
+
]
2316
2316
+
2317
2317
+
[[package]]
2318
2318
+
name = "gloo-dialogs"
2319
2319
+
version = "0.2.0"
2320
2320
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2321
2321
+
checksum = "bf4748e10122b01435750ff530095b1217cf6546173459448b83913ebe7815df"
2322
2322
+
dependencies = [
2323
2323
+
"wasm-bindgen",
2324
2324
+
"web-sys",
2325
2325
+
]
2326
2326
+
2327
2327
+
[[package]]
2328
2328
+
name = "gloo-events"
2329
2329
+
version = "0.2.0"
2330
2330
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2331
2331
+
checksum = "27c26fb45f7c385ba980f5fa87ac677e363949e065a083722697ef1b2cc91e41"
2332
2332
+
dependencies = [
2333
2333
+
"wasm-bindgen",
2334
2334
+
"web-sys",
2335
2335
+
]
2336
2336
+
2337
2337
+
[[package]]
2338
2338
+
name = "gloo-file"
2339
2339
+
version = "0.3.0"
2340
2340
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2341
2341
+
checksum = "97563d71863fb2824b2e974e754a81d19c4a7ec47b09ced8a0e6656b6d54bd1f"
2342
2342
+
dependencies = [
2343
2343
+
"gloo-events",
2344
2344
+
"js-sys",
2345
2345
+
"wasm-bindgen",
2346
2346
+
"web-sys",
2347
2347
+
]
2348
2348
+
2349
2349
+
[[package]]
2350
2350
+
name = "gloo-history"
2351
2351
+
version = "0.2.2"
2352
2352
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2353
2353
+
checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6"
2354
2354
+
dependencies = [
2355
2355
+
"getrandom 0.2.17",
2356
2356
+
"gloo-events",
2357
2357
+
"gloo-utils",
2358
2358
+
"serde",
2359
2359
+
"serde-wasm-bindgen",
2360
2360
+
"serde_urlencoded",
2361
2361
+
"thiserror 1.0.69",
2362
2362
+
"wasm-bindgen",
2363
2363
+
"web-sys",
2364
2364
+
]
2365
2365
+
2366
2366
+
[[package]]
2367
2367
+
name = "gloo-net"
2368
2368
+
version = "0.5.0"
2369
2369
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2370
2370
+
checksum = "43aaa242d1239a8822c15c645f02166398da4f8b5c4bae795c1f5b44e9eee173"
2371
2371
+
dependencies = [
2372
2372
+
"futures-channel",
2373
2373
+
"futures-core",
2374
2374
+
"futures-sink",
2375
2375
+
"gloo-utils",
2376
2376
+
"http 0.2.12",
2377
2377
+
"js-sys",
2378
2378
+
"pin-project",
2379
2379
+
"serde",
2380
2380
+
"serde_json",
2381
2381
+
"thiserror 1.0.69",
2382
2382
+
"wasm-bindgen",
2383
2383
+
"wasm-bindgen-futures",
2384
2384
+
"web-sys",
2385
2385
+
]
2386
2386
+
2387
2387
+
[[package]]
2275
2388
name = "gloo-net"
2276
2389
version = "0.6.0"
2277
2390
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2281
2394
"futures-core",
2282
2395
"futures-sink",
2283
2396
"gloo-utils",
2284
2284
-
"http",
2397
2397
+
"http 1.4.0",
2285
2398
"js-sys",
2286
2399
"pin-project",
2287
2400
"serde",
···
2293
2406
]
2294
2407
2295
2408
[[package]]
2409
2409
+
name = "gloo-render"
2410
2410
+
version = "0.2.0"
2411
2411
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2412
2412
+
checksum = "56008b6744713a8e8d98ac3dcb7d06543d5662358c9c805b4ce2167ad4649833"
2413
2413
+
dependencies = [
2414
2414
+
"wasm-bindgen",
2415
2415
+
"web-sys",
2416
2416
+
]
2417
2417
+
2418
2418
+
[[package]]
2419
2419
+
name = "gloo-storage"
2420
2420
+
version = "0.3.0"
2421
2421
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2422
2422
+
checksum = "fbc8031e8c92758af912f9bc08fbbadd3c6f3cfcbf6b64cdf3d6a81f0139277a"
2423
2423
+
dependencies = [
2424
2424
+
"gloo-utils",
2425
2425
+
"js-sys",
2426
2426
+
"serde",
2427
2427
+
"serde_json",
2428
2428
+
"thiserror 1.0.69",
2429
2429
+
"wasm-bindgen",
2430
2430
+
"web-sys",
2431
2431
+
]
2432
2432
+
2433
2433
+
[[package]]
2296
2434
name = "gloo-timers"
2297
2435
version = "0.3.0"
2298
2436
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2318
2456
]
2319
2457
2320
2458
[[package]]
2459
2459
+
name = "gloo-worker"
2460
2460
+
version = "0.5.0"
2461
2461
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2462
2462
+
checksum = "085f262d7604911c8150162529cefab3782e91adb20202e8658f7275d2aefe5d"
2463
2463
+
dependencies = [
2464
2464
+
"bincode",
2465
2465
+
"futures",
2466
2466
+
"gloo-utils",
2467
2467
+
"gloo-worker-macros",
2468
2468
+
"js-sys",
2469
2469
+
"pinned",
2470
2470
+
"serde",
2471
2471
+
"thiserror 1.0.69",
2472
2472
+
"wasm-bindgen",
2473
2473
+
"wasm-bindgen-futures",
2474
2474
+
"web-sys",
2475
2475
+
]
2476
2476
+
2477
2477
+
[[package]]
2478
2478
+
name = "gloo-worker-macros"
2479
2479
+
version = "0.1.0"
2480
2480
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2481
2481
+
checksum = "956caa58d4857bc9941749d55e4bd3000032d8212762586fa5705632967140e7"
2482
2482
+
dependencies = [
2483
2483
+
"proc-macro-crate 1.3.1",
2484
2484
+
"proc-macro2",
2485
2485
+
"quote",
2486
2486
+
"syn 2.0.114",
2487
2487
+
]
2488
2488
+
2489
2489
+
[[package]]
2321
2490
name = "gobject-sys"
2322
2491
version = "0.18.0"
2323
2492
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2391
2560
"fnv",
2392
2561
"futures-core",
2393
2562
"futures-sink",
2394
2394
-
"http",
2563
2563
+
"http 1.4.0",
2395
2564
"indexmap",
2396
2565
"slab",
2397
2566
"tokio",
···
2456
2625
"base64",
2457
2626
"bytes",
2458
2627
"headers-core",
2459
2459
-
"http",
2628
2628
+
"http 1.4.0",
2460
2629
"httpdate",
2461
2630
"mime",
2462
2631
"sha1",
···
2468
2637
source = "registry+https://github.com/rust-lang/crates.io-index"
2469
2638
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
2470
2639
dependencies = [
2471
2471
-
"http",
2640
2640
+
"http 1.4.0",
2472
2641
]
2473
2642
2474
2643
[[package]]
···
2530
2699
2531
2700
[[package]]
2532
2701
name = "http"
2702
2702
+
version = "0.2.12"
2703
2703
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2704
2704
+
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
2705
2705
+
dependencies = [
2706
2706
+
"bytes",
2707
2707
+
"fnv",
2708
2708
+
"itoa",
2709
2709
+
]
2710
2710
+
2711
2711
+
[[package]]
2712
2712
+
name = "http"
2533
2713
version = "1.4.0"
2534
2714
source = "registry+https://github.com/rust-lang/crates.io-index"
2535
2715
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
···
2545
2725
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
2546
2726
dependencies = [
2547
2727
"bytes",
2548
2548
-
"http",
2728
2728
+
"http 1.4.0",
2549
2729
]
2550
2730
2551
2731
[[package]]
···
2556
2736
dependencies = [
2557
2737
"bytes",
2558
2738
"futures-core",
2559
2559
-
"http",
2739
2739
+
"http 1.4.0",
2560
2740
"http-body",
2561
2741
"pin-project-lite",
2562
2742
]
···
2590
2770
"futures-channel",
2591
2771
"futures-core",
2592
2772
"h2",
2593
2593
-
"http",
2773
2773
+
"http 1.4.0",
2594
2774
"http-body",
2595
2775
"httparse",
2596
2776
"httpdate",
···
2608
2788
source = "registry+https://github.com/rust-lang/crates.io-index"
2609
2789
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
2610
2790
dependencies = [
2611
2611
-
"http",
2791
2791
+
"http 1.4.0",
2612
2792
"hyper",
2613
2793
"hyper-util",
2614
2794
"rustls",
···
2629
2809
"bytes",
2630
2810
"futures-channel",
2631
2811
"futures-util",
2632
2632
-
"http",
2812
2812
+
"http 1.4.0",
2633
2813
"http-body",
2634
2814
"hyper",
2635
2815
"ipnet",
···
3319
3499
"bytes",
3320
3500
"encoding_rs",
3321
3501
"futures-util",
3322
3322
-
"http",
3502
3502
+
"http 1.4.0",
3323
3503
"httparse",
3324
3504
"memchr",
3325
3505
"mime",
···
3859
4039
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
3860
4040
3861
4041
[[package]]
4042
4042
+
name = "pinned"
4043
4043
+
version = "0.1.0"
4044
4044
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4045
4045
+
checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
4046
4046
+
dependencies = [
4047
4047
+
"futures",
4048
4048
+
"rustversion",
4049
4049
+
"thiserror 1.0.69",
4050
4050
+
]
4051
4051
+
4052
4052
+
[[package]]
3862
4053
name = "pkcs1"
3863
4054
version = "0.7.5"
3864
4055
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4280
4471
"cookie_store",
4281
4472
"futures-core",
4282
4473
"futures-util",
4283
4283
-
"http",
4474
4474
+
"http 1.4.0",
4284
4475
"http-body",
4285
4476
"http-body-util",
4286
4477
"hyper",
···
5578
5769
"bytes",
5579
5770
"futures-core",
5580
5771
"futures-util",
5581
5581
-
"http",
5772
5772
+
"http 1.4.0",
5582
5773
"http-body",
5583
5774
"http-body-util",
5584
5775
"http-range-header",
···
5711
5902
dependencies = [
5712
5903
"bytes",
5713
5904
"data-encoding",
5714
5714
-
"http",
5905
5905
+
"http 1.4.0",
5715
5906
"httparse",
5716
5907
"log",
5717
5908
"native-tls",
···
5730
5921
dependencies = [
5731
5922
"bytes",
5732
5923
"data-encoding",
5733
5733
-
"http",
5924
5924
+
"http 1.4.0",
5734
5925
"httparse",
5735
5926
"log",
5736
5927
"rand 0.9.2",
···
6666
6857
"dunce",
6667
6858
"gtk",
6668
6859
"html5ever",
6669
6669
-
"http",
6860
6860
+
"http 1.4.0",
6670
6861
"javascriptcore-rs",
6671
6862
"jni",
6672
6863
"kuchikiki",
+5
-2
Cargo.toml
···
7
7
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8
8
9
9
[dependencies]
10
10
+
anyhow = "1.0.101"
10
11
axum = { version = "0.8.8", features = ["ws"], optional = true }
11
11
-
derive_more = "2.1.1"
12
12
+
derive_more = { version = "2.1.1", features = ["error"] }
12
13
dioxus = { version = "0.7.1", features = ["router", "fullstack"] }
13
14
futures = "0.3.31"
14
15
futures-rx = "0.2.1"
16
16
+
gloo = { version = "0.11.0", optional = true }
15
17
kameo = "0.19.2"
16
18
rmp-serde = "1.3.1"
17
19
serde = { version = "1.0.228", features = ["derive"] }
···
24
26
25
27
[features]
26
28
default = ["web", "server"]
27
27
-
web = ["dioxus/web", "tokio/rt", "uuid/js"]
29
29
+
web = ["dioxus/web", "tokio/rt", "uuid/js", "dep:gloo"]
28
30
desktop = ["dioxus/desktop", "tokio/rt", "uuid/js"]
29
31
mobile = ["dioxus/mobile", "tokio/rt", "uuid/js"]
30
32
server = ["dioxus/server", "tokio/rt-multi-thread", "dep:sqlx", "dep:axum"]
33
33
+
gloo = ["dep:gloo"]
+2
-2
Justfile
···
19
19
shadow_database_name := if SHADOW_DATABASE_URL != "" { file_stem(SHADOW_DATABASE_URL) } else { "" }
20
20
21
21
[group: "run"]
22
22
-
dev: up
23
23
-
dx serve
22
22
+
dev +args="--web": up
23
23
+
dx serve {{args}}
24
24
25
25
[group: "dev"]
26
26
init:
-61
src/actor/player_socket.rs
···
1
1
-
use futures::Stream;
2
2
-
use serde::{Deserialize, Serialize};
3
3
-
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
4
4
-
use tokio_stream::wrappers::UnboundedReceiverStream;
5
5
-
6
6
-
#[cfg(feature = "server")]
7
7
-
use kameo::prelude::*;
8
8
-
9
9
-
#[cfg(feature = "server")]
10
10
-
#[derive(Actor, Default)]
11
11
-
pub struct PlayerSocket {
12
12
-
account_id: Option<String>,
13
13
-
}
14
14
-
15
15
-
#[cfg(feature = "server")]
16
16
-
impl PlayerSocket {
17
17
-
pub async fn push(
18
18
-
actor: ActorRef<Self>,
19
19
-
request: Request,
20
20
-
) -> Result<impl Stream<Item = Response>, SendError<PlayerSocketMessage>> {
21
21
-
let (tx, rx) = unbounded_channel();
22
22
-
actor.tell(PlayerSocketMessage { tx, request }).await?;
23
23
-
Ok(UnboundedReceiverStream::new(rx))
24
24
-
}
25
25
-
}
26
26
-
27
27
-
#[cfg(feature = "server")]
28
28
-
pub struct PlayerSocketMessage {
29
29
-
tx: UnboundedSender<Response>,
30
30
-
request: Request,
31
31
-
}
32
32
-
33
33
-
#[derive(Serialize, Deserialize, Clone, Debug)]
34
34
-
#[serde(tag = "type", content = "data")]
35
35
-
pub enum Request {
36
36
-
Authenticate(String),
37
37
-
}
38
38
-
39
39
-
#[derive(Serialize, Deserialize, Clone, Debug)]
40
40
-
#[serde(tag = "type", content = "data")]
41
41
-
pub enum Response {
42
42
-
Authenticated(String),
43
43
-
}
44
44
-
45
45
-
#[cfg(feature = "server")]
46
46
-
impl Message<PlayerSocketMessage> for PlayerSocket {
47
47
-
type Reply = ();
48
48
-
49
49
-
async fn handle(
50
50
-
&mut self,
51
51
-
PlayerSocketMessage { tx, request }: PlayerSocketMessage,
52
52
-
_ctx: &mut Context<Self, Self::Reply>,
53
53
-
) -> Self::Reply {
54
54
-
match request {
55
55
-
Request::Authenticate(name) => {
56
56
-
self.account_id = Some(name.clone());
57
57
-
let _ = tx.send(Response::Authenticated(name));
58
58
-
}
59
59
-
}
60
60
-
}
61
61
-
}
+75
src/actor/player_socket/mod.rs
···
1
1
+
use serde::{Deserialize, Serialize};
2
2
+
3
3
+
#[derive(Serialize, Deserialize, Clone, Debug)]
4
4
+
#[serde(tag = "type", content = "data")]
5
5
+
pub enum Request {
6
6
+
Authenticate(String),
7
7
+
}
8
8
+
9
9
+
#[derive(Serialize, Deserialize, Clone, Debug)]
10
10
+
#[serde(tag = "type", content = "data")]
11
11
+
pub enum Response {
12
12
+
Authenticated(String),
13
13
+
}
14
14
+
15
15
+
#[cfg(feature = "server")]
16
16
+
pub use server::PlayerSocket;
17
17
+
18
18
+
#[cfg(feature = "server")]
19
19
+
mod server {
20
20
+
mod authenticate;
21
21
+
22
22
+
use super::{Request, Response};
23
23
+
use futures::Stream;
24
24
+
use kameo::prelude::*;
25
25
+
use sqlx::PgPool;
26
26
+
use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
27
27
+
use tokio_stream::wrappers::UnboundedReceiverStream;
28
28
+
29
29
+
#[derive(Actor)]
30
30
+
pub struct PlayerSocket {
31
31
+
pub(super) db: PgPool,
32
32
+
pub(super) account_id: Option<String>,
33
33
+
}
34
34
+
35
35
+
impl PlayerSocket {
36
36
+
pub fn build(db: PgPool) -> Self {
37
37
+
Self {
38
38
+
db,
39
39
+
account_id: None,
40
40
+
}
41
41
+
}
42
42
+
43
43
+
pub async fn push(
44
44
+
actor: ActorRef<Self>,
45
45
+
request: Request,
46
46
+
) -> Result<impl Stream<Item = Response>, SendError<PlayerSocketMessage>> {
47
47
+
let (tx, rx) = unbounded_channel();
48
48
+
actor.tell(PlayerSocketMessage { tx, request }).await?;
49
49
+
Ok(UnboundedReceiverStream::new(rx))
50
50
+
}
51
51
+
}
52
52
+
53
53
+
pub struct PlayerSocketMessage {
54
54
+
tx: UnboundedSender<Response>,
55
55
+
request: Request,
56
56
+
}
57
57
+
58
58
+
impl Message<PlayerSocketMessage> for PlayerSocket {
59
59
+
type Reply = ();
60
60
+
61
61
+
async fn handle(
62
62
+
&mut self,
63
63
+
PlayerSocketMessage { tx, request }: PlayerSocketMessage,
64
64
+
ctx: &mut Context<Self, Self::Reply>,
65
65
+
) -> Self::Reply {
66
66
+
let result = match request {
67
67
+
Request::Authenticate(account_id) => self.authenticate(tx, account_id).await,
68
68
+
};
69
69
+
if let Err(error) = result {
70
70
+
tracing::error!("error handling player socket message: {}", error);
71
71
+
ctx.stop()
72
72
+
}
73
73
+
}
74
74
+
}
75
75
+
}
+34
src/actor/player_socket/server/authenticate.rs
···
1
1
+
use super::{PlayerSocket, Response};
2
2
+
use tokio::sync::mpsc::UnboundedSender;
3
3
+
4
4
+
impl PlayerSocket {
5
5
+
pub(super) async fn authenticate(
6
6
+
&mut self,
7
7
+
tx: UnboundedSender<Response>,
8
8
+
account_id: String,
9
9
+
) -> anyhow::Result<()> {
10
10
+
struct Account {
11
11
+
id: String,
12
12
+
}
13
13
+
let mut conn = self.db.begin().await?;
14
14
+
let account = sqlx::query_as!(Account, "SELECT id FROM accounts WHERE id = $1", account_id)
15
15
+
.fetch_optional(&mut *conn)
16
16
+
.await?;
17
17
+
let account = match account {
18
18
+
Some(account) => account,
19
19
+
None => {
20
20
+
sqlx::query_as!(
21
21
+
Account,
22
22
+
"INSERT INTO accounts (id) VALUES ($1) RETURNING id",
23
23
+
account_id
24
24
+
)
25
25
+
.fetch_one(&mut *conn)
26
26
+
.await?
27
27
+
}
28
28
+
};
29
29
+
conn.commit().await?;
30
30
+
self.account_id = Some(account.id.clone());
31
31
+
tx.send(Response::Authenticated(account.id))?;
32
32
+
Ok(())
33
33
+
}
34
34
+
}
-1
src/api.rs
···
1
1
use dioxus::prelude::*;
2
2
3
3
-
#[cfg(feature = "server")]
4
3
pub mod ws;
5
4
6
5
/// Echo the user input on the server.
+20
-19
src/api/ws.rs
···
1
1
+
use serde::{Deserialize, Serialize};
2
2
+
use uuid::Uuid;
3
3
+
4
4
+
#[cfg(feature = "server")]
1
5
use axum::extract::ws::{CloseFrame, Message, WebSocket, WebSocketUpgrade};
6
6
+
#[cfg(feature = "server")]
7
7
+
use axum::Extension;
8
8
+
#[cfg(feature = "server")]
2
9
use futures::StreamExt;
10
10
+
#[cfg(feature = "server")]
3
11
use kameo::prelude::*;
4
4
-
use serde::{Deserialize, Serialize};
12
12
+
#[cfg(feature = "server")]
13
13
+
use sqlx::PgPool;
14
14
+
#[cfg(feature = "server")]
5
15
use tracing::Instrument;
6
6
-
use uuid::Uuid;
7
7
-
16
16
+
#[cfg(feature = "server")]
8
17
use crate::actor::player_socket::{PlayerSocket, Request, Response};
9
18
10
19
#[derive(Serialize, Deserialize, Clone, Debug)]
···
14
23
data: T,
15
24
}
16
25
17
17
-
const JSON_PROTOCOL: &str = "v1-json.cartography.app";
18
18
-
const MESSAGEPACK_PROTOCOL: &str = "v1-messagepack.cartography.app";
26
26
+
pub const JSON_PROTOCOL: &str = "v1-json.cartography.app";
27
27
+
pub const MESSAGEPACK_PROTOCOL: &str = "v1-messagepack.cartography.app";
19
28
20
20
-
impl std::error::Error for ProtocolV1Error {
21
21
-
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
22
22
-
match self {
23
23
-
Self::InvalidJson(error) => Some(error),
24
24
-
Self::InvalidMessagepack(error) => Some(error),
25
25
-
_ => None,
26
26
-
}
27
27
-
}
28
28
-
}
29
29
-
30
30
-
#[derive(Debug, derive_more::Display)]
29
29
+
#[cfg(feature = "server")]
30
30
+
#[derive(Debug, derive_more::Error, derive_more::Display)]
31
31
enum ProtocolV1Error {
32
32
#[display("invalid JSON payload: {_0}")]
33
33
InvalidJson(serde_json::Error),
···
38
38
#[display("client disconnected")]
39
39
Disconnected,
40
40
#[display("client closed connection: {_0:?}")]
41
41
-
Closed(CloseFrame),
41
41
+
Closed(#[error(not(source))] CloseFrame),
42
42
}
43
43
44
44
-
pub async fn v1(ws: WebSocketUpgrade) -> axum::response::Response {
44
44
+
#[cfg(feature = "server")]
45
45
+
pub async fn v1(ws: WebSocketUpgrade, db: Extension<PgPool>) -> axum::response::Response {
45
46
let ws = ws.protocols([JSON_PROTOCOL, MESSAGEPACK_PROTOCOL]);
46
47
let protocol = ws
47
48
.selected_protocol()
···
54
55
let (ws_sender, ws_receiver) = socket.split();
55
56
futures::pin_mut!(ws_sender);
56
57
57
57
-
let actor = PlayerSocket::spawn_default();
58
58
+
let actor = PlayerSocket::spawn(PlayerSocket::build((*db).clone()));
58
59
let result = ws_receiver
59
60
.filter_map(|msg| async move { msg.ok() })
60
61
.map(|msg| match msg {
+1
src/app.rs
src/app/mod.rs
···
1
1
use dioxus::prelude::*;
2
2
3
3
+
mod hooks;
3
4
mod menu;
4
5
mod play;
5
6
+1
src/app/hooks/mod.rs
···
1
1
+
pub mod use_custom_websocket;
+142
src/app/hooks/use_custom_websocket.rs
···
1
1
+
use crate::actor::player_socket::{Request, Response};
2
2
+
use crate::api::{self, ws::ProtocolV1Message};
3
3
+
use dioxus::fullstack::{get_server_url, WebsocketState};
4
4
+
use dioxus::prelude::*;
5
5
+
use futures::stream::{SplitSink, SplitStream};
6
6
+
use futures::{SinkExt, StreamExt};
7
7
+
#[cfg(feature = "web")]
8
8
+
use gloo::net::websocket::futures::WebSocket;
9
9
+
#[cfg(feature = "web")]
10
10
+
use gloo::net::websocket::Message;
11
11
+
use tokio::sync::Mutex;
12
12
+
13
13
+
#[cfg(feature = "web")]
14
14
+
pub struct CustomWebSocket {
15
15
+
protocol: String,
16
16
+
sender: Mutex<SplitSink<WebSocket, Message>>,
17
17
+
receiver: Mutex<SplitStream<WebSocket>>,
18
18
+
}
19
19
+
20
20
+
#[derive(Copy, Clone)]
21
21
+
pub struct UseCustomWebsocket {
22
22
+
waker: UseWaker<()>,
23
23
+
#[cfg(feature = "web")]
24
24
+
connection: Resource<anyhow::Result<CustomWebSocket>>,
25
25
+
status: Signal<WebsocketState>,
26
26
+
status_read: ReadSignal<WebsocketState>,
27
27
+
}
28
28
+
29
29
+
pub fn use_custom_websocket(path: &'static str) -> UseCustomWebsocket {
30
30
+
let mut waker = use_waker();
31
31
+
#[cfg(feature = "web")]
32
32
+
let mut status = use_signal(|| WebsocketState::Connecting);
33
33
+
#[cfg(not(feature = "web"))]
34
34
+
let mut status = use_signal(|| WebsocketState::FailedToConnect);
35
35
+
let status_read = use_hook(|| ReadSignal::new(status));
36
36
+
37
37
+
#[cfg(feature = "web")]
38
38
+
let connection = use_resource(move || async move {
39
39
+
let socket = match gloo::net::websocket::futures::WebSocket::open_with_protocols(
40
40
+
&format!("{}/{}", get_server_url(), path),
41
41
+
&[api::ws::JSON_PROTOCOL, api::ws::MESSAGEPACK_PROTOCOL],
42
42
+
) {
43
43
+
Ok(socket) => {
44
44
+
status.set(WebsocketState::Open);
45
45
+
socket
46
46
+
}
47
47
+
Err(error) => {
48
48
+
status.set(WebsocketState::FailedToConnect);
49
49
+
return Err(error.into());
50
50
+
}
51
51
+
};
52
52
+
53
53
+
let protocol = socket.protocol();
54
54
+
let (sender, receiver) = socket.split();
55
55
+
56
56
+
waker.wake(());
57
57
+
58
58
+
Ok(CustomWebSocket {
59
59
+
protocol,
60
60
+
sender: Mutex::new(sender),
61
61
+
receiver: Mutex::new(receiver),
62
62
+
})
63
63
+
});
64
64
+
65
65
+
UseCustomWebsocket {
66
66
+
waker,
67
67
+
#[cfg(feature = "web")]
68
68
+
connection,
69
69
+
status,
70
70
+
status_read,
71
71
+
}
72
72
+
}
73
73
+
74
74
+
impl UseCustomWebsocket {
75
75
+
#[cfg(not(feature = "web"))]
76
76
+
pub async fn connect(&self) -> WebsocketState {
77
77
+
WebsocketState::FailedToConnect
78
78
+
}
79
79
+
80
80
+
#[cfg(feature = "web")]
81
81
+
pub async fn connect(&self) -> WebsocketState {
82
82
+
while !self.connection.finished() {
83
83
+
_ = self.waker.wait().await;
84
84
+
}
85
85
+
self.status.cloned()
86
86
+
}
87
87
+
88
88
+
pub fn status(&self) -> ReadSignal<WebsocketState> {
89
89
+
self.status_read
90
90
+
}
91
91
+
92
92
+
#[cfg(not(feature = "web"))]
93
93
+
pub async fn send(&self, msg: ProtocolV1Message<Request>) -> anyhow::Result<()> {
94
94
+
Err(anyhow::anyhow!("not implemented on this platform"))
95
95
+
}
96
96
+
97
97
+
#[cfg(not(feature = "web"))]
98
98
+
pub async fn recv(&self) -> anyhow::Result<ProtocolV1Message<Response>> {
99
99
+
Err(anyhow::anyhow!("not implemented on this platform"))
100
100
+
}
101
101
+
102
102
+
#[cfg(feature = "web")]
103
103
+
pub async fn send(&self, msg: ProtocolV1Message<Request>) -> anyhow::Result<()> {
104
104
+
self.connect().await;
105
105
+
106
106
+
let connection = self.connection.as_ref();
107
107
+
let connection = connection
108
108
+
.as_deref()
109
109
+
.ok_or_else(|| anyhow::anyhow!("websocket closed away"))?
110
110
+
.as_ref()
111
111
+
.map_err(|err| anyhow::anyhow!("{err}"))?;
112
112
+
113
113
+
let msg = match connection.protocol.as_str() {
114
114
+
api::ws::MESSAGEPACK_PROTOCOL => rmp_serde::to_vec(&msg).map(Message::Bytes)?,
115
115
+
_ => serde_json::to_string(&msg).map(Message::Text)?,
116
116
+
};
117
117
+
118
118
+
connection.sender.lock().await.send(msg).await?;
119
119
+
120
120
+
Ok(())
121
121
+
}
122
122
+
123
123
+
#[cfg(feature = "web")]
124
124
+
pub async fn recv(&self) -> anyhow::Result<ProtocolV1Message<Response>> {
125
125
+
self.connect().await;
126
126
+
127
127
+
let connection = self.connection.as_ref();
128
128
+
let connection = connection
129
129
+
.as_deref()
130
130
+
.ok_or_else(|| anyhow::anyhow!("websocket closed away"))?
131
131
+
.as_ref()
132
132
+
.map_err(|err| anyhow::anyhow!("{err}"))?;
133
133
+
134
134
+
let mut recv = connection.receiver.lock().await;
135
135
+
match recv.next().await {
136
136
+
Some(Ok(Message::Text(text))) => Ok(serde_json::from_str(&text)?),
137
137
+
Some(Ok(Message::Bytes(bytes))) => Ok(rmp_serde::from_read(&*bytes)?),
138
138
+
Some(Err(e)) => Err(e.into()),
139
139
+
None => anyhow::bail!("closed away"),
140
140
+
}
141
141
+
}
142
142
+
}
+4
-3
src/app/menu.rs
···
1
1
+
use crate::app::Route;
1
2
use dioxus::prelude::*;
2
3
3
4
#[component]
···
13
14
gap: "1rem",
14
15
15
16
h1 { font_size: "2rem", "This game?" }
16
16
-
a { href: "/cards", "See the cards" }
17
17
-
a { href: "/play", "Just Play" }
18
18
-
a { href: "/demo", "Try Demo" }
17
17
+
Link { to: Route::Menu {}, "See the cards" }
18
18
+
Link { to: Route::Play {}, "Just Play" }
19
19
+
Link { to: Route::Menu {}, "Try Demo" }
19
20
}
20
21
}
21
22
}
+9
src/app/play.rs
···
1
1
+
use crate::app::hooks::use_custom_websocket::use_custom_websocket;
1
2
use dioxus::prelude::*;
2
3
3
4
#[component]
4
5
pub fn Play() -> Element {
6
6
+
let socket = use_custom_websocket("api/ws");
7
7
+
8
8
+
use_future(move || async move {
9
9
+
while let Ok(msg) = socket.recv().await {
10
10
+
dbg!(msg);
11
11
+
}
12
12
+
});
13
13
+
5
14
rsx! {
6
15
main { display: "flex",
7
16
div {
+1
-1
src/main.rs
···
6
6
7
7
fn main() {
8
8
#[cfg(not(feature = "server"))]
9
9
-
dioxus::fullstack::set_server_url("");
9
9
+
dioxus::fullstack::set_server_url("http://localhost:8080");
10
10
11
11
#[cfg(not(feature = "server"))]
12
12
dioxus::launch(App);