Compare commits
19 commits
Author | SHA1 | Date | |
---|---|---|---|
b316e5f0ac | |||
4e7e7bbba6 | |||
90179f1001 | |||
46e57fe2dc | |||
432c68ff74 | |||
be69d00e2c | |||
44e7ec1527 | |||
af97b37e51 | |||
3c9e3dc907 | |||
d909cf1aa3 | |||
c6b66eacb4 | |||
85fbc933ba | |||
395f249207 | |||
087991cb85 | |||
1d5321c805 | |||
d93c61cc92 | |||
73060a426d | |||
96d1994fe2 | |||
e5892ed9e5 |
116 changed files with 7394452 additions and 186845 deletions
12
.dockerignore
Normal file
12
.dockerignore
Normal file
|
@ -0,0 +1,12 @@
|
|||
.git
|
||||
assets
|
||||
postgres
|
||||
**/target
|
||||
.gitignore
|
||||
docker-compose.yml
|
||||
Dockerfile-builder
|
||||
Dockerfile-service
|
||||
LICENSE
|
||||
*.md
|
||||
*.zip
|
||||
*.png
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
.idea
|
||||
/target
|
||||
/hotpatch.toml
|
||||
/configserver.toml
|
||||
|
|
106
Cargo.lock
generated
106
Cargo.lock
generated
|
@ -402,7 +402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
|
@ -416,7 +416,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
|
@ -724,13 +724,19 @@ dependencies = [
|
|||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -825,6 +831,12 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range-header"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08a397c49fec283e3d6211adbe480be95aae5f304cfb923e9970e08956d5168a"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.9.4"
|
||||
|
@ -884,12 +896,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.4.0"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
|
||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
"hashbrown 0.15.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1006,6 +1018,15 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
|
@ -1043,6 +1064,16 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mime_guess"
|
||||
version = "2.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
||||
dependencies = [
|
||||
"mime",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
@ -1462,8 +1493,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-automata 0.4.7",
|
||||
"regex-syntax 0.8.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1474,9 +1514,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.8.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.4"
|
||||
|
@ -1725,6 +1771,7 @@ dependencies = [
|
|||
"rbase64",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -1745,10 +1792,14 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"byteorder",
|
||||
"crc32fast",
|
||||
"prettyplease",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"shorekeeper-protocol-derive",
|
||||
"syn",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
@ -1882,7 +1933,7 @@ dependencies = [
|
|||
"futures-intrusive",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"hashbrown",
|
||||
"hashbrown 0.14.5",
|
||||
"hashlink",
|
||||
"hex",
|
||||
"indexmap",
|
||||
|
@ -2269,6 +2320,31 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"http-range-header",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.3"
|
||||
|
@ -2331,10 +2407,14 @@ version = "0.3.18"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
@ -2345,6 +2425,12 @@ version = "1.17.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -8,6 +8,7 @@ version = "0.1.0"
|
|||
[workspace.dependencies]
|
||||
# Framework
|
||||
tokio = { version = "1.39.3", features = ["full"] }
|
||||
tower-http = { version = "0.6.1", features = ["fs", "trace"] }
|
||||
axum = "0.7.5"
|
||||
axum-server = "0.7.1"
|
||||
zeromq = { version = "0.4.0", default-features = false, features = ["tokio-runtime", "tcp-transport"] }
|
||||
|
@ -41,18 +42,18 @@ crc32fast = "1.4.2"
|
|||
|
||||
# Tracing
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = "0.3.18"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
# Internal
|
||||
kcp = { path = "gateway-server/kcp" }
|
||||
common = { path = "common/" }
|
||||
shorekeeper-http = { path = "shorekeeper-http/" }
|
||||
shorekeeper-data = { path = "shorekeeper-data/" }
|
||||
shorekeeper-database = { path = "shorekeeper-database/" }
|
||||
shorekeeper-network = { path = "shorekeeper-network/" }
|
||||
shorekeeper-protocol = { path = "shorekeeper-protocol/" }
|
||||
common = { path = "common" }
|
||||
shorekeeper-http = { path = "shorekeeper-http" }
|
||||
shorekeeper-data = { path = "shorekeeper-data" }
|
||||
shorekeeper-database = { path = "shorekeeper-database" }
|
||||
shorekeeper-network = { path = "shorekeeper-network" }
|
||||
shorekeeper-protocol = { path = "shorekeeper-protocol" }
|
||||
shorekeeper-protocol-derive = { path = "shorekeeper-protocol/shorekeeper-protocol-derive" }
|
||||
shorekeeper-protokey = { path = "shorekeeper-protokey/" }
|
||||
shorekeeper-protokey = { path = "shorekeeper-protokey" }
|
||||
|
||||
[profile.release]
|
||||
strip = true # Automatically strip symbols from the binary.
|
||||
|
|
6
Dockerfile-builder
Normal file
6
Dockerfile-builder
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM rust:1.82-alpine3.20
|
||||
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
# No need to manually strip symbols(strip target/release/$MICROSERVICE) since workspace its already prepared for that
|
||||
RUN apk add musl-dev protoc && cargo build --release
|
6
Dockerfile-service
Normal file
6
Dockerfile-service
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM alpine:3.20
|
||||
ARG MICROSERVICE
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=wicked-waifus-builder:1.4.0-SNAPSHOT /app/target/release/$MICROSERVICE ./service
|
||||
CMD ["./service"]
|
17
README.md
17
README.md
|
@ -24,7 +24,22 @@ cargo run --bin gateway-server
|
|||
cargo run --bin game-server
|
||||
```
|
||||
|
||||
##### b) using pre-built binaries
|
||||
##### b) building from sources(docker edition)
|
||||
If you are to wheelchair'd for option A, you can fallback to option b.
|
||||
In this case you will need [Docker Desktop](https://www.docker.com/products/docker-desktop/)
|
||||
|
||||
Once installed, to build the images, run:
|
||||
```sh
|
||||
# or builder.bat if you run it on windows
|
||||
./builder.sh
|
||||
```
|
||||
|
||||
And to run the containers:
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
##### c) using pre-built binaries
|
||||
Navigate to the [Releases](https://git.xeondev.com/Shorekeeper/Shorekeeper/releases)
|
||||
page and download the latest release for your platform.<br>
|
||||
Launch all servers: `config-server`, `hotpatch-server`, `login-server`, `gateway-server`, `game-server`
|
||||
|
|
64
assets/config/e66lKLx7RnUf9QlkMCGaW0jdyjRV1weo/index.json
Normal file
64
assets/config/e66lKLx7RnUf9QlkMCGaW0jdyjRV1weo/index.json
Normal file
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"default": {
|
||||
"CdnUrl": [
|
||||
{
|
||||
"url": "https://cdn-huoshan-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "0"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qiniu-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "4181"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qcloud-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "2937"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-aliyun-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "0"
|
||||
}
|
||||
],
|
||||
"SecondaryUrl": [],
|
||||
"PriceRatio": 1,
|
||||
"SpeedRatio": 1,
|
||||
"GachaUrl": {
|
||||
"GachaRecord": "https://aki-gm-resources.aki-game.com",
|
||||
"GachaPoolDetail": "https://aki-gm-resources.aki-game.com"
|
||||
},
|
||||
"LogReport": {
|
||||
"name": "aki-upload-log-1319073642",
|
||||
"region": "ap-shanghai"
|
||||
},
|
||||
"PackageUpdateDescUrl": {
|
||||
"MainUrl": "https://aki-gm-resources-back.aki-game.com/force_update/UpdateDesc.html",
|
||||
"SubUrl": "https://aki-gm-resources-back-qiniu.aki-game.com/force_update/UpdateDesc.html"
|
||||
},
|
||||
"PackageUpdateUrl": {
|
||||
"MainUrl": "https://aki-gm-resources-back.aki-game.com/force_update/UpdateJs.html",
|
||||
"SubUrl": "https://aki-gm-resources-back-qiniu.aki-game.com/force_update/UpdateJs.html"
|
||||
},
|
||||
"TDCfg": {
|
||||
"AppID": "7be70c9c181f4fa8bc013e9b89d2b63b",
|
||||
"URL": "http://cn-datareceiver.aki-game.com"
|
||||
},
|
||||
"GmOpen": false,
|
||||
"IosAuditFirstDownloadTip": false,
|
||||
"NoticeUrl": "https://aki-gm-resources-back.aki-game.com",
|
||||
"MixUri": "e66lKLx7RnUf9QlkMCGaW0jdyjRV1weo",
|
||||
"ResUri": "e66lKLx7RnUf9QlkMCGaW0jdyjRV1weo",
|
||||
"LoginServers": [
|
||||
{
|
||||
"id": "76402e5b20be2c39f095a152090afddc",
|
||||
"ip": "https://login-sh.aki-game.com",
|
||||
"name": "鸣潮"
|
||||
}
|
||||
],
|
||||
"PrivateServers": {
|
||||
"enable": false,
|
||||
"serverUrl": ""
|
||||
}
|
||||
},
|
||||
"p1": {
|
||||
"IosAuditFirstDownloadTip": false
|
||||
}
|
||||
}
|
60
assets/config/fcQrWOPC2RkghpzsAE2kyGWD37QTm2mR/index.json
Normal file
60
assets/config/fcQrWOPC2RkghpzsAE2kyGWD37QTm2mR/index.json
Normal file
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"default": {
|
||||
"CdnUrl": [
|
||||
{
|
||||
"url": "https://cdn-huoshan-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "2323"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qiniu-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "1"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qcloud-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "5443"
|
||||
}
|
||||
],
|
||||
"SecondaryUrl": [],
|
||||
"PriceRatio": 1,
|
||||
"SpeedRatio": 1,
|
||||
"GachaUrl": {
|
||||
"GachaRecord": "http://127.0.0.1:10001/gacha/record",
|
||||
"GachaPoolDetail": "http://127.0.0.1:10001/gacha/detail"
|
||||
},
|
||||
"LogReport": {
|
||||
"name": "pioneer-upload-log-1319073642",
|
||||
"region": "dev-reversedrooms"
|
||||
},
|
||||
"PackageUpdateDescUrl": {
|
||||
"MainUrl": "http://127.0.0.1:10001/force_update/UpdateDesc.html",
|
||||
"SubUrl": "http://127.0.0.1:10001/force_update/UpdateDesc.html"
|
||||
},
|
||||
"PackageUpdateUrl": {
|
||||
"MainUrl": "http://127.0.0.1:10001/force_update/UpdateJs.html",
|
||||
"SubUrl": "http://127.0.0.1:10001/force_update/UpdateJs.html"
|
||||
},
|
||||
"TDCfg": {
|
||||
"AppID": "3e2e647670b7498fa645eb9574f78c2c",
|
||||
"URL": "http://127.0.0.1:10001/TDCfg"
|
||||
},
|
||||
"GmOpen": false,
|
||||
"IosAuditFirstDownloadTip": false,
|
||||
"NoticeUrl": "http://127.0.0.1:10001/notice",
|
||||
"MixUri": "fcQrWOPC2RkghpzsAE2kyGWD37QTm2mR",
|
||||
"ResUri": "fcQrWOPC2RkghpzsAE2kyGWD37QTm2mR",
|
||||
"LoginServers": [
|
||||
{
|
||||
"id": "f9e0fc655c1931bc03ad976e9fc14473",
|
||||
"ip": "http://127.0.0.1:5500",
|
||||
"name": "Camellya"
|
||||
}
|
||||
],
|
||||
"PrivateServers": {
|
||||
"enable": false,
|
||||
"serverUrl": ""
|
||||
}
|
||||
},
|
||||
"p1": {
|
||||
"IosAuditFirstDownloadTip": false
|
||||
}
|
||||
}
|
|
@ -2,8 +2,20 @@
|
|||
"default": {
|
||||
"CdnUrl": [
|
||||
{
|
||||
"url": "http://127.0.0.1:10002/prod/client/",
|
||||
"url": "https://cdn-huoshan-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "2323"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qiniu-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "0"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qcloud-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "5443"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-aliyun-cn-mc.aki-game.com/prod/client/",
|
||||
"weight": "0"
|
||||
}
|
||||
],
|
||||
"SecondaryUrl": [],
|
115
assets/config/u4S3uXaif7gNKnNGnbZ6WnW67vWFmQgd/index.json
Normal file
115
assets/config/u4S3uXaif7gNKnNGnbZ6WnW67vWFmQgd/index.json
Normal file
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"default": {
|
||||
"CdnUrl": [
|
||||
{
|
||||
"url": "https://cdn-aws-hw-mc.aki-game.net/prod/client/",
|
||||
"weight": "829"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-qcloud-hw-mc.aki-game.net/prod/client/",
|
||||
"weight": "0"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-akamai-hw-mc.aki-game.net/prod/client/",
|
||||
"weight": "262"
|
||||
},
|
||||
{
|
||||
"url": "https://cdn-huoshan-hw-mc.aki-game.net/prod/client/",
|
||||
"weight": "829"
|
||||
}
|
||||
],
|
||||
"SecondaryUrl": [],
|
||||
"PriceRatio": 1,
|
||||
"SpeedRatio": 1,
|
||||
"GachaUrl": {
|
||||
"GachaRecord": "https://aki-gm-resources-oversea.aki-game.net",
|
||||
"GachaPoolDetail": "https://aki-gm-resources-oversea.aki-game.net"
|
||||
},
|
||||
"LogReport": {
|
||||
"name": "hw-aki-upload-log-1319073642",
|
||||
"region": "ap-singapore"
|
||||
},
|
||||
"PackageUpdateDescUrl": {
|
||||
"MainUrl": "https://aki-gm-resources-back.aki-game.net/force_update/UpdateDesc.html",
|
||||
"SubUrl": "https://aki-gm-resources-back-aws.aki-game.net/force_update/UpdateDesc.html"
|
||||
},
|
||||
"PackageUpdateUrl": {
|
||||
"MainUrl": "https://aki-gm-resources-back.aki-game.net/force_update/UpdateJs.html",
|
||||
"SubUrl": "https://aki-gm-resources-back-aws.aki-game.net/force_update/UpdateJs.html"
|
||||
},
|
||||
"GARUrl": "https://gar-service.aki-game.net",
|
||||
"TDCfg": {
|
||||
"URL": "http://us-datareceiver.aki-game.net",
|
||||
"AppID": "bf3f44edc6cf43c582e347ba660876c0"
|
||||
},
|
||||
"GmOpen": false,
|
||||
"IosAuditFirstDownloadTip": false,
|
||||
"NoticeUrl": "https://aki-gm-resources-back.aki-game.net",
|
||||
"MixUri": "u4S3uXaif7gNKnNGnbZ6WnW67vWFmQgd",
|
||||
"ResUri": "u4S3uXaif7gNKnNGnbZ6WnW67vWFmQgd",
|
||||
"LoginServers": [
|
||||
{
|
||||
"PingUrl": "49.51.79.239",
|
||||
"Region": "America",
|
||||
"TDCfg": {
|
||||
"AppID": "12c7c870b57541caa2093e275212f083",
|
||||
"URL": "http://us-datareceiver.aki-game.net"
|
||||
},
|
||||
"id": "591d6af3a3090d8ea00d8f86cf6d7501",
|
||||
"ip": "https://login-us.aki-game.net",
|
||||
"name": "America"
|
||||
},
|
||||
{
|
||||
"PingUrl": "124.156.236.245",
|
||||
"Region": "Asia",
|
||||
"TDCfg": {
|
||||
"AppID": "dc2e8c02609546e89d78c2eb2974becb",
|
||||
"URL": "http://jp-datareceiver.aki-game.net"
|
||||
},
|
||||
"id": "86d52186155b148b5c138ceb41be9650",
|
||||
"ip": "https://login-jp.aki-game.net",
|
||||
"name": "Asia"
|
||||
},
|
||||
{
|
||||
"PingUrl": "49.51.129.88",
|
||||
"Region": "Europe",
|
||||
"TDCfg": {
|
||||
"AppID": "0fb77e4801db41a7a1fb1a3431625fbd",
|
||||
"URL": "http://eu-datareceiver.aki-game.net"
|
||||
},
|
||||
"id": "6eb2a235b30d05efd77bedb5cf60999e",
|
||||
"ip": "https://login-eu.aki-game.net",
|
||||
"name": "Europe"
|
||||
},
|
||||
{
|
||||
"PingUrl": "43.129.150.38",
|
||||
"Region": "HMT",
|
||||
"TDCfg": {
|
||||
"AppID": "5ccd297c928d405a9e6b18a122f4d48d",
|
||||
"URL": "http://hk-datareceiver.aki-game.net"
|
||||
},
|
||||
"id": "919752ae5ea09c1ced910dd668a63ffb",
|
||||
"ip": "https://login-hk.aki-game.net",
|
||||
"name": "HMT(HK, MO, TW)"
|
||||
},
|
||||
{
|
||||
"PingUrl": "101.33.100.22",
|
||||
"Region": "SEA",
|
||||
"TDCfg": {
|
||||
"AppID": "dffd1143291b4877a8866fb24c623038",
|
||||
"URL": "http://as-datareceiver.aki-game.net"
|
||||
},
|
||||
"id": "10cd7254d57e58ae560b15d51e34b4c8",
|
||||
"ip": "https://login-sg.aki-game.net",
|
||||
"name": "SEA"
|
||||
}
|
||||
],
|
||||
"PrivateServers": {
|
||||
"enable": false,
|
||||
"serverUrl": ""
|
||||
}
|
||||
},
|
||||
"p1": {
|
||||
"IosAuditFirstDownloadTip": false
|
||||
}
|
||||
}
|
|
@ -1,60 +1,44 @@
|
|||
{
|
||||
"PackageVersion": "1.3.0",
|
||||
"LauncherVersion": "1.3.9",
|
||||
"ResourceVersion": "1.3.9",
|
||||
"LauncherIndexSha1": {
|
||||
"1.3.1": "90FDF17EA0B4015D43C344CB7229E76AB32549DD",
|
||||
"1.3.2": "C9A587AB1FA6CA57CD23E0FB3F0103BFDCAA8E37",
|
||||
"1.3.3": "1C7AF02F13DBE69637DB43039E2FFB8C9AD9A04B",
|
||||
"1.3.4": "DA50F315041E216568A7713074C6475F6AB4530E",
|
||||
"1.3.5": "EA9C6F6D5E920F47F96D8F8BC366A4CED62A0346",
|
||||
"1.3.6": "8CA7E6573A52B16CFAA29E996D389918B6829E7A",
|
||||
"1.3.7": "FCAAED58E5983027A82F52C350418CCE7BD531D2",
|
||||
"1.3.8": "91D6231B3F4C9A6605B79E23D0C02F9790DD6BCF",
|
||||
"1.3.9": "B36BD648AB2A637A4E087B7B115A6CCBDAEBDF9A"
|
||||
},
|
||||
"PackageVersion": "1.4.0",
|
||||
"LauncherVersion": "1.4.0",
|
||||
"ResourceVersion": "1.4.3",
|
||||
"LauncherIndexSha1": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||
"ResourceIndexSha1": {
|
||||
"1.3.1": "2D635E549EB6F99659571D72741B62249473A77A",
|
||||
"1.3.2": "C5814A80EA3E7D80D4CFBCD884D1FD158BF0AD9D",
|
||||
"1.3.3": "1E0F05333B09A9215B4AA5C437BFC7DC4014E348",
|
||||
"1.3.4": "6155D492540A99ECF0DA06D2B7EEBFE36231FBC2",
|
||||
"1.3.5": "1E60C8F60CA1AAA9955441B4F4265C8288B95F33",
|
||||
"1.3.6": "AA10A8DD1025D5033E291060C686B816513ADCAD",
|
||||
"1.3.7": "A9881305EBD3DC5A6892D49BDAF540F56EE56232",
|
||||
"1.3.8": "261CA25DAD6877DF3C57DA39947130867FCC09CE",
|
||||
"1.3.9": "88A9E40631FC1C11A91A61CB3F4BE8C13C5E2BD3"
|
||||
"1.4.1": "0C747E444EC6DBA11D0C57B34E2638DE5182029F",
|
||||
"1.4.2": "41751A94DBD406653DB52262E85BB716E45D9864",
|
||||
"1.4.3": "424EDFD97BA8699CBC2CB9E27D3FCBB6F7C70806"
|
||||
},
|
||||
"ChangeList": "2333675",
|
||||
"ChangeList": "2585778",
|
||||
"CompatibleChangeLists": [],
|
||||
"Versions": [
|
||||
{
|
||||
"Name": "en",
|
||||
"Version": "1.3.0",
|
||||
"Version": "1.4.0",
|
||||
"IndexSha1": {
|
||||
"1.3.0": "6FB5B66EF8B3EECBBBEBE74A82BC23E3FC35450B"
|
||||
"1.4.0": "34F9AC326B3E81E972B93094E43751967A6A3396"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "ja",
|
||||
"Version": "1.3.0",
|
||||
"Version": "1.4.0",
|
||||
"IndexSha1": {
|
||||
"1.3.0": "E4DA1960DB36CE8166C042AD8B9AF98C1A9119F3"
|
||||
"1.4.0": "3233223428D586FBC0A2EC817F4E94DDD10C646F"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "ko",
|
||||
"Version": "1.3.0",
|
||||
"Version": "1.4.0",
|
||||
"IndexSha1": {
|
||||
"1.3.0": "498B379E95FC617385CCD832B8C359FA5AC220CE"
|
||||
"1.4.0": "3CC0312BDFFF5FA4D3F5256E2618D0EF6AA84912"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "zh",
|
||||
"Version": "1.3.0",
|
||||
"Version": "1.4.0",
|
||||
"IndexSha1": {
|
||||
"1.3.0": "CC58C357A80E7B3846264918197FC3ECAA1FE190"
|
||||
"1.4.0": "2F3AD1412C6991FE0E146C5E2F6F86D8D9F44A5D"
|
||||
}
|
||||
}
|
||||
],
|
||||
"UpdateTime": 1725869509
|
||||
}
|
||||
"UpdateTime": 1729243476
|
||||
}
|
150539
assets/logic/BinData/BaseProperty.json
Normal file
150539
assets/logic/BinData/BaseProperty.json
Normal file
File diff suppressed because it is too large
Load diff
246
assets/logic/BinData/ExploreTools.json
Normal file
246
assets/logic/BinData/ExploreTools.json
Normal file
|
@ -0,0 +1,246 @@
|
|||
[
|
||||
{
|
||||
"PhantomSkillId": 1004,
|
||||
"Name": "ExploreTools_1004_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1004_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT3.SP_IconT3",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT3.SP_IconT3",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT3.SP_IconT3",
|
||||
"SortId": 3,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1001,
|
||||
"Name": "ExploreTools_1001_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1001_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT6.SP_IconT6",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT6.SP_IconT6",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT6.SP_IconT6",
|
||||
"SortId": 1,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1003,
|
||||
"Name": "ExploreTools_1003_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1003_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT1.SP_IconT1",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT1.SP_IconT1",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT1.SP_IconT1",
|
||||
"SortId": 4,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1005,
|
||||
"Name": "ExploreTools_1005_Name",
|
||||
"SkillType": 2,
|
||||
"CurrentSkillInfo": "ExploreTools_1005_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT2.SP_IconT2",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT2.SP_IconT2",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT2.SP_IconT2",
|
||||
"SortId": 5,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1006,
|
||||
"Name": "ExploreTools_1006_Name",
|
||||
"SkillType": 2,
|
||||
"CurrentSkillInfo": "ExploreTools_1006_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT5.SP_IconT5",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT5.SP_IconT5",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT5.SP_IconT5",
|
||||
"SortId": 6,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1007,
|
||||
"Name": "ExploreTools_1007_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1007_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT29.SP_IconT29",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT29.SP_IconT29",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT29.SP_IconT29",
|
||||
"SortId": 7,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1009,
|
||||
"Name": "ExploreTools_1009_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1009_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"SortId": 9,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": true,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 3001,
|
||||
"Name": "ExploreTools_1009_Name",
|
||||
"SkillType": 3,
|
||||
"CurrentSkillInfo": "ExploreTools_1009_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"SortId": 10,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 3002,
|
||||
"Name": "ExploreTools_1009_Name",
|
||||
"SkillType": 3,
|
||||
"CurrentSkillInfo": "ExploreTools_1009_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT9.SP_IconT9",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/UiSkillswitching/Atlas/SP_SkillswitchingItemNone.SP_SkillswitchingItemNone",
|
||||
"SortId": 10,
|
||||
"AutoFill": false,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1010,
|
||||
"Name": "ExploreTools_1010_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1010_CurrentSkillInfo",
|
||||
"HelpId": 30,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT30.SP_IconT30",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT30.SP_IconT30",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT30.SP_IconT30",
|
||||
"SortId": 11,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {
|
||||
"10808": 1
|
||||
},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1011,
|
||||
"Name": "ExploreTools_1011_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1011_CurrentSkillInfo",
|
||||
"HelpId": 28,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT32.SP_IconT32",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT32.SP_IconT32",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT32.SP_IconT32",
|
||||
"SortId": 12,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {
|
||||
"10807": 1
|
||||
},
|
||||
"Authorization": {
|
||||
"1": 10805,
|
||||
"900": 10805
|
||||
},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1012,
|
||||
"Name": "ExploreTools_1012_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1012_CurrentSkillInfo",
|
||||
"HelpId": 29,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT31.SP_IconT31",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT31.SP_IconT31",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT31.SP_IconT31",
|
||||
"SortId": 13,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {
|
||||
"1": 10806,
|
||||
"900": 10806
|
||||
},
|
||||
"SummonConfigId": 0
|
||||
},
|
||||
{
|
||||
"PhantomSkillId": 1013,
|
||||
"Name": "ExploreTools_1013_Name",
|
||||
"SkillType": 1,
|
||||
"CurrentSkillInfo": "ExploreTools_1013_CurrentSkillInfo",
|
||||
"HelpId": 0,
|
||||
"Icon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT34.SP_IconT34",
|
||||
"BackGround": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT34.SP_IconT34",
|
||||
"BattleViewIcon": "/Game/Aki/UI/UIResources/Common/Atlas/SkillIcon/SkillIconNor/SP_IconT34.SP_IconT34",
|
||||
"SortId": 14,
|
||||
"AutoFill": true,
|
||||
"ShowUnlock": false,
|
||||
"SkillGroupId": 1,
|
||||
"IsUseInPhantomTeam": false,
|
||||
"Cost": {},
|
||||
"Authorization": {},
|
||||
"SummonConfigId": 24000022
|
||||
}
|
||||
]
|
1058
assets/logic/BinData/FunctionCondition.json
Normal file
1058
assets/logic/BinData/FunctionCondition.json
Normal file
File diff suppressed because it is too large
Load diff
53394
assets/logic/BinData/InstanceDungeon.json
Normal file
53394
assets/logic/BinData/InstanceDungeon.json
Normal file
File diff suppressed because it is too large
Load diff
2681165
assets/logic/BinData/LevelEntityConfig.json
Normal file
2681165
assets/logic/BinData/LevelEntityConfig.json
Normal file
File diff suppressed because it is too large
Load diff
4044
assets/logic/BinData/RoleInfo.json
Normal file
4044
assets/logic/BinData/RoleInfo.json
Normal file
File diff suppressed because it is too large
Load diff
6192
assets/logic/BinData/WeaponConf.json
Normal file
6192
assets/logic/BinData/WeaponConf.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/de/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/de/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/en/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/en/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/es/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/es/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/fr/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/fr/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/id/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/id/multi_text/MultiText.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/ja/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/ja/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/ko/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/ko/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/pt/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/pt/multi_text/MultiText.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/ru/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/ru/multi_text/MultiText.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/th/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/th/multi_text/MultiText.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/vi/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/vi/multi_text/MultiText.json
Normal file
File diff suppressed because it is too large
Load diff
345078
assets/logic/Textmaps/zh-Hans/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/zh-Hans/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
345078
assets/logic/Textmaps/zh-Hant/multi_text/MultiText.json
Normal file
345078
assets/logic/Textmaps/zh-Hant/multi_text/MultiText.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
12
builder.bat
Normal file
12
builder.bat
Normal file
|
@ -0,0 +1,12 @@
|
|||
docker build -t wicked-waifus-builder:1.4.0-SNAPSHOT -f Dockerfile-builder .
|
||||
|
||||
docker build -t wicked-waifus-config-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=config-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-hotpatch-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=hotpatch-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-login-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=login-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-gateway-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=gateway-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-game-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=game-server -f Dockerfile-service .
|
||||
|
||||
docker rmi wicked-waifus-builder:1.4.0-SNAPSHOT
|
||||
|
||||
: Persistence for the application
|
||||
: docker volume create wicked-waifus-postgres-vol
|
12
builder.sh
Normal file
12
builder.sh
Normal file
|
@ -0,0 +1,12 @@
|
|||
docker build -t wicked-waifus-builder:1.4.0-SNAPSHOT -f Dockerfile-builder .
|
||||
|
||||
docker build -t wicked-waifus-config-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=config-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-hotpatch-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=hotpatch-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-login-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=login-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-gateway-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=gateway-server -f Dockerfile-service .
|
||||
docker build -t wicked-waifus-game-server:1.4.0-SNAPSHOT --build-arg MICROSERVICE=game-server -f Dockerfile-service .
|
||||
|
||||
docker rmi wicked-waifus-builder:1.4.0-SNAPSHOT
|
||||
|
||||
# Persistence for the application
|
||||
# docker volume create wicked-waifus-postgres-vol
|
|
@ -4,7 +4,7 @@ pub trait TomlConfig: DeserializeOwned {
|
|||
const DEFAULT_TOML: &str;
|
||||
}
|
||||
|
||||
pub fn load_or_create<'a, C>(path: &str) -> C
|
||||
pub fn load_or_create<C>(path: &str) -> C
|
||||
where
|
||||
C: DeserializeOwned + TomlConfig,
|
||||
{
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use tracing::Level;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
pub fn init(max_level: Level) {
|
||||
tracing_subscriber::fmt()
|
||||
|
@ -6,3 +8,21 @@ pub fn init(max_level: Level) {
|
|||
.with_target(false)
|
||||
.init();
|
||||
}
|
||||
|
||||
pub fn init_axum(max_level: Level) {
|
||||
tracing_subscriber::registry()
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||
// axum logs rejections from built-in extractors with the `axum::rejection`
|
||||
// target, at `TRACE` level. `axum::rejection=trace` enables showing those events
|
||||
format!(
|
||||
"{}={},tower_http={},axum::rejection=trace",
|
||||
env!("CARGO_CRATE_NAME"),
|
||||
max_level.as_str(),
|
||||
max_level.as_str()
|
||||
).into()
|
||||
}),
|
||||
)
|
||||
.with(tracing_subscriber::fmt::layer().with_target(false))
|
||||
.init();
|
||||
}
|
|
@ -1,3 +1,10 @@
|
|||
pub fn print_splash() {
|
||||
println!(" _____ __ __ \n / ___// /_ ____ ________ / /_____ ___ ____ ___ _____\n \\__ \\/ __ \\/ __ \\/ ___/ _ \\/ //_/ _ \\/ _ \\/ __ \\/ _ \\/ ___/\n ___/ / / / / /_/ / / / __/ ,< / __/ __/ /_/ / __/ / \n/____/_/ /_/\\____/_/ \\___/_/|_|\\___/\\___/ .___/\\___/_/ \n /_/ ");
|
||||
println!("
|
||||
██╗ ██╗██╗ ██████╗██╗ ██╗███████╗██████╗ ██╗ ██╗ █████╗ ██╗███████╗██╗ ██╗███████╗ ██████╗ ███████╗
|
||||
██║ ██║██║██╔════╝██║ ██╔╝██╔════╝██╔══██╗ ██║ ██║██╔══██╗██║██╔════╝██║ ██║██╔════╝ ██╔══██╗██╔════╝
|
||||
██║ █╗ ██║██║██║ █████╔╝ █████╗ ██║ ██║ ██║ █╗ ██║███████║██║█████╗ ██║ ██║███████╗ ██████╔╝███████╗
|
||||
██║███╗██║██║██║ ██╔═██╗ ██╔══╝ ██║ ██║ ██║███╗██║██╔══██║██║██╔══╝ ██║ ██║╚════██║ ██╔═══╝ ╚════██║
|
||||
╚███╔███╔╝██║╚██████╗██║ ██╗███████╗██████╔╝ ╚███╔███╔╝██║ ██║██║██║ ╚██████╔╝███████║ ██║ ███████║
|
||||
╚══╝╚══╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝
|
||||
");
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ pub fn unix_timestamp() -> u64 {
|
|||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs() as u64
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
pub fn unix_timestamp_ms() -> u64 {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::fs;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use anyhow::Result;
|
||||
|
@ -26,20 +25,14 @@ async fn main() -> Result<()> {
|
|||
LazyLock::new(|| config_util::load_or_create("configserver.toml"));
|
||||
|
||||
::common::splash::print_splash();
|
||||
::common::logging::init(::tracing::Level::DEBUG);
|
||||
::common::logging::init_axum(::tracing::Level::DEBUG);
|
||||
|
||||
Application::new()
|
||||
.get("/index.json", get_index)
|
||||
.serve_dir("/", "assets/config")
|
||||
.with_encryption(&CONFIG.encryption)
|
||||
.with_logger()
|
||||
.serve(&CONFIG.network)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_index() -> &'static str {
|
||||
static INDEX: LazyLock<String> =
|
||||
LazyLock::new(|| fs::read_to_string("assets/config/index.json").unwrap());
|
||||
|
||||
&*INDEX
|
||||
}
|
||||
|
|
13
docker-compose-hotpatch.yml
Normal file
13
docker-compose-hotpatch.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
name: wicked-waifus-ps
|
||||
|
||||
services:
|
||||
wicked-waifus-hotpatch-server:
|
||||
image: wicked-waifus-hotpatch-server:1.4.0-SNAPSHOT
|
||||
depends_on:
|
||||
wicked-waifus-postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- '10002:10002'
|
||||
volumes:
|
||||
- "./docker/hotpatch.toml:/app/hotpatch.toml"
|
||||
- "./assets/hotpatch:/app/assets/hotpatch"
|
63
docker-compose.yml
Normal file
63
docker-compose.yml
Normal file
|
@ -0,0 +1,63 @@
|
|||
name: wicked-waifus-ps
|
||||
|
||||
services:
|
||||
wicked-waifus-config-server:
|
||||
image: wicked-waifus-config-server:1.4.0-SNAPSHOT
|
||||
depends_on:
|
||||
wicked-waifus-postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- '10001:10001'
|
||||
volumes:
|
||||
- "./docker/configserver.toml:/app/configserver.toml"
|
||||
- "./assets/config:/app/assets/config"
|
||||
wicked-waifus-login-server:
|
||||
image: wicked-waifus-login-server:1.4.0-SNAPSHOT
|
||||
depends_on:
|
||||
wicked-waifus-postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- '5500:5500'
|
||||
volumes:
|
||||
- "./docker/loginserver.toml:/app/loginserver.toml"
|
||||
wicked-waifus-gateway-server:
|
||||
image: wicked-waifus-gateway-server:1.4.0-SNAPSHOT
|
||||
depends_on:
|
||||
wicked-waifus-postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
# Uncomment this if you want to have manual access
|
||||
# - '10003:10003'
|
||||
- '7777:7777/udp'
|
||||
volumes:
|
||||
- "./docker/gateway.toml:/app/gateway.toml"
|
||||
wicked-waifus-game-server:
|
||||
image: wicked-waifus-game-server:1.4.0-SNAPSHOT
|
||||
depends_on:
|
||||
wicked-waifus-postgres:
|
||||
condition: service_healthy
|
||||
# Uncomment this if you want to have manual access
|
||||
# ports:
|
||||
# - '10004:10004'
|
||||
volumes:
|
||||
- "./docker/gameserver.toml:/app/gameserver.toml"
|
||||
- "./assets/logic:/app/assets/logic"
|
||||
wicked-waifus-postgres:
|
||||
image: postgres:16.4-alpine3.20
|
||||
user: postgres
|
||||
# Uncomment this if you want to have manual access
|
||||
ports:
|
||||
- '5432:5432'
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
environment:
|
||||
- "POSTGRES_PASSWORD=toor"
|
||||
volumes:
|
||||
- "./docker/postgres/scripts:/docker-entrypoint-initdb.d"
|
||||
- wicked-waifus-postgres-vol:/var/lib/postgresql/data
|
||||
volumes:
|
||||
wicked-waifus-postgres-vol:
|
||||
external: true
|
6
docker/configserver.toml
Normal file
6
docker/configserver.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[network]
|
||||
http_addr = "0.0.0.0:10001"
|
||||
|
||||
[encryption]
|
||||
key = "t+AEu5SGdpz06tomonajLMau9AJgmyTvVhz9VtGf1+0="
|
||||
iv = "fprc5lBWADQB7tim0R2JxQ=="
|
13
docker/gameserver.toml
Normal file
13
docker/gameserver.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
service_id = 2
|
||||
|
||||
[database]
|
||||
host = "wicked-waifus-postgres:5432"
|
||||
user_name = "wicked_waifus_user"
|
||||
password = "wicked_waifus_pass"
|
||||
db_name = "wicked_waifus_db"
|
||||
|
||||
[service_end_point]
|
||||
addr = "tcp://0.0.0.0:10004"
|
||||
|
||||
[gateway_end_point]
|
||||
addr = "tcp://wicked-waifus-gateway-server:10003"
|
20
docker/gateway.toml
Normal file
20
docker/gateway.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
service_id = 1
|
||||
|
||||
[network]
|
||||
kcp_port = 7777
|
||||
|
||||
[protokey]
|
||||
builtin_encryption_msg_id = [111, 112]
|
||||
use_client_key = true
|
||||
|
||||
[service_end_point]
|
||||
addr = "tcp://0.0.0.0:10003"
|
||||
|
||||
[game_server_end_point]
|
||||
addr = "tcp://wicked-waifus-game-server:10004"
|
||||
|
||||
[database]
|
||||
host = "wicked-waifus-postgres:5432"
|
||||
user_name = "wicked_waifus_user"
|
||||
password = "wicked_waifus_pass"
|
||||
db_name = "wicked_waifus_db"
|
6
docker/hotpatch.toml
Normal file
6
docker/hotpatch.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[network]
|
||||
http_addr = "0.0.0.0:10002"
|
||||
|
||||
[encryption]
|
||||
key = "t+AEu5SGdpz06tomonajLMau9AJgmyTvVhz9VtGf1+0="
|
||||
iv = "fprc5lBWADQB7tim0R2JxQ=="
|
12
docker/loginserver.toml
Normal file
12
docker/loginserver.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[network]
|
||||
http_addr = "0.0.0.0:5500"
|
||||
|
||||
[gateway]
|
||||
host = "host.docker.internal"
|
||||
port = 7777
|
||||
|
||||
[database]
|
||||
host = "wicked-waifus-postgres:5432"
|
||||
user_name = "wicked_waifus_user"
|
||||
password = "wicked_waifus_pass"
|
||||
db_name = "wicked_waifus_db"
|
3
docker/postgres/scripts/0_INIT.sql
Normal file
3
docker/postgres/scripts/0_INIT.sql
Normal file
|
@ -0,0 +1,3 @@
|
|||
CREATE DATABASE wicked_waifus_db;
|
||||
CREATE USER wicked_waifus_user WITH encrypted password 'wicked_waifus_pass';
|
||||
GRANT ALL PRIVILEGES ON DATABASE wicked_waifus_db to wicked_waifus_user;
|
2
docker/postgres/scripts/1_PERMS.sql
Normal file
2
docker/postgres/scripts/1_PERMS.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
\c wicked_waifus_db;
|
||||
GRANT ALL ON SCHEMA public TO wicked_waifus_user;
|
12
game-server/censorshipfix.js
Normal file
12
game-server/censorshipfix.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const UE = require("ue");
|
||||
const { CharacterDitherEffectController } = require("../NewWorld/Character/Common/Component/Effect/CharacterDitherEffectController");
|
||||
|
||||
CharacterDitherEffectController.prototype.SetDitherEffect = function(t, i = 3, s = true) { };
|
||||
|
||||
CharacterDitherEffectController.prototype.EnterAppearEffect = function(t = 1, i = 3, s = true) {
|
||||
this.SetHiddenInGame(false, true);
|
||||
};
|
||||
|
||||
CharacterDitherEffectController.prototype.EnterDisappearEffect = function(t = 1, i = 3, s = true) {
|
||||
this.SetHiddenInGame(true, true);
|
||||
};
|
|
@ -1,52 +1,31 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use shorekeeper_data::BasePropertyData;
|
||||
use shorekeeper_protocol::{
|
||||
entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType,
|
||||
EntityComponentPb, LivingStatus,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::ecs::component::Component;
|
||||
use crate::logic::utils::load_role_info::attribute_from_data;
|
||||
|
||||
pub struct Attribute {
|
||||
pub attr_map: HashMap<EAttributeType, (i32, i32)>,
|
||||
}
|
||||
|
||||
macro_rules! impl_from_data {
|
||||
($($name:ident),*) => {
|
||||
pub fn from_data(base_property: &BasePropertyData) -> Self {
|
||||
Self {
|
||||
attr_map: HashMap::from([$(
|
||||
::paste::paste!((EAttributeType::[<$name:camel>], (base_property.$name, 0))),
|
||||
)*]),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Component for Attribute {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.living_status = self
|
||||
.is_alive()
|
||||
.then_some(LivingStatus::Alive)
|
||||
.unwrap_or(LivingStatus::Dead)
|
||||
.into();
|
||||
pb.living_status = (if self.is_alive() {
|
||||
LivingStatus::Alive
|
||||
} else {
|
||||
LivingStatus::Dead
|
||||
})
|
||||
.into();
|
||||
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::AttributeComponent(AttributeComponentPb {
|
||||
attr_data: self
|
||||
.attr_map
|
||||
.iter()
|
||||
.map(|(ty, (base, incr))| AttrData {
|
||||
attribute_type: (*ty).into(),
|
||||
base_value: *base,
|
||||
increment: *incr,
|
||||
})
|
||||
.collect(),
|
||||
hardness_mode_id: 0,
|
||||
rage_mode_id: 0,
|
||||
})),
|
||||
})
|
||||
component_pb: Some(ComponentPb::AttributeComponent(
|
||||
self.build_entity_attribute(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,142 +39,27 @@ impl Attribute {
|
|||
> 0
|
||||
}
|
||||
|
||||
impl_from_data!(
|
||||
lv,
|
||||
life_max,
|
||||
life,
|
||||
sheild,
|
||||
sheild_damage_change,
|
||||
sheild_damage_reduce,
|
||||
atk,
|
||||
crit,
|
||||
crit_damage,
|
||||
def,
|
||||
energy_efficiency,
|
||||
cd_reduse,
|
||||
reaction_efficiency,
|
||||
damage_change_normal_skill,
|
||||
damage_change,
|
||||
damage_reduce,
|
||||
damage_change_auto,
|
||||
damage_change_cast,
|
||||
damage_change_ultra,
|
||||
damage_change_qte,
|
||||
damage_change_phys,
|
||||
damage_change_element1,
|
||||
damage_change_element2,
|
||||
damage_change_element3,
|
||||
damage_change_element4,
|
||||
damage_change_element5,
|
||||
damage_change_element6,
|
||||
damage_resistance_phys,
|
||||
damage_resistance_element1,
|
||||
damage_resistance_element2,
|
||||
damage_resistance_element3,
|
||||
damage_resistance_element4,
|
||||
damage_resistance_element5,
|
||||
damage_resistance_element6,
|
||||
heal_change,
|
||||
healed_change,
|
||||
damage_reduce_phys,
|
||||
damage_reduce_element1,
|
||||
damage_reduce_element2,
|
||||
damage_reduce_element3,
|
||||
damage_reduce_element4,
|
||||
damage_reduce_element5,
|
||||
damage_reduce_element6,
|
||||
reaction_change1,
|
||||
reaction_change2,
|
||||
reaction_change3,
|
||||
reaction_change4,
|
||||
reaction_change5,
|
||||
reaction_change6,
|
||||
reaction_change7,
|
||||
reaction_change8,
|
||||
reaction_change9,
|
||||
reaction_change10,
|
||||
reaction_change11,
|
||||
reaction_change12,
|
||||
reaction_change13,
|
||||
reaction_change14,
|
||||
reaction_change15,
|
||||
energy_max,
|
||||
energy,
|
||||
special_energy_1_max,
|
||||
special_energy_1,
|
||||
special_energy_2_max,
|
||||
special_energy_2,
|
||||
special_energy_3_max,
|
||||
special_energy_3,
|
||||
special_energy_4_max,
|
||||
special_energy_4,
|
||||
strength_max,
|
||||
strength,
|
||||
strength_recover,
|
||||
strength_punish_time,
|
||||
strength_run,
|
||||
strength_swim,
|
||||
strength_fast_swim,
|
||||
hardness_max,
|
||||
hardness,
|
||||
hardness_recover,
|
||||
hardness_punish_time,
|
||||
hardness_change,
|
||||
hardness_reduce,
|
||||
rage_max,
|
||||
rage,
|
||||
rage_recover,
|
||||
rage_punish_time,
|
||||
rage_change,
|
||||
rage_reduce,
|
||||
tough_max,
|
||||
tough,
|
||||
tough_recover,
|
||||
tough_change,
|
||||
tough_reduce,
|
||||
tough_recover_delay_time,
|
||||
element_power1,
|
||||
element_power2,
|
||||
element_power3,
|
||||
element_power4,
|
||||
element_power5,
|
||||
element_power6,
|
||||
special_damage_change,
|
||||
strength_fast_climb_cost,
|
||||
element_property_type,
|
||||
weak_time,
|
||||
ignore_def_rate,
|
||||
ignore_damage_resistance_phys,
|
||||
ignore_damage_resistance_element1,
|
||||
ignore_damage_resistance_element2,
|
||||
ignore_damage_resistance_element3,
|
||||
ignore_damage_resistance_element4,
|
||||
ignore_damage_resistance_element5,
|
||||
ignore_damage_resistance_element6,
|
||||
skill_tough_ratio,
|
||||
strength_climb_jump,
|
||||
strength_gliding,
|
||||
mass,
|
||||
braking_friction_factor,
|
||||
gravity_scale,
|
||||
speed_ratio,
|
||||
damage_change_phantom,
|
||||
auto_attack_speed,
|
||||
cast_attack_speed,
|
||||
status_build_up_1_max,
|
||||
status_build_up_1,
|
||||
status_build_up_2_max,
|
||||
status_build_up_2,
|
||||
status_build_up_3_max,
|
||||
status_build_up_3,
|
||||
status_build_up_4_max,
|
||||
status_build_up_4,
|
||||
status_build_up_5_max,
|
||||
status_build_up_5,
|
||||
paralysis_time_max,
|
||||
paralysis_time,
|
||||
paralysis_time_recover,
|
||||
element_energy_max,
|
||||
element_energy
|
||||
);
|
||||
#[inline(always)]
|
||||
pub fn from_data(base_property: &BasePropertyData) -> Self {
|
||||
Self {
|
||||
attr_map: attribute_from_data(base_property),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn build_entity_attribute(&self) -> AttributeComponentPb {
|
||||
AttributeComponentPb {
|
||||
attr_data: self
|
||||
.attr_map
|
||||
.iter()
|
||||
.map(|(ty, (base, incr))| AttrData {
|
||||
attribute_type: (*ty).into(),
|
||||
base_value: *base,
|
||||
increment: *incr,
|
||||
})
|
||||
.collect(),
|
||||
hardness_mode_id: 0,
|
||||
rage_mode_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
use shorekeeper_protocol::EntityConfigType;
|
||||
use shorekeeper_protocol::{EEntityType, EntityConfigType, EntityState};
|
||||
|
||||
use crate::logic::ecs::component::Component;
|
||||
|
||||
pub struct EntityConfig {
|
||||
pub config_id: i32,
|
||||
pub config_type: EntityConfigType,
|
||||
pub entity_type: EEntityType,
|
||||
pub entity_state: EntityState
|
||||
}
|
||||
|
||||
impl Component for EntityConfig {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.config_id = self.config_id;
|
||||
pb.config_type = self.config_type.into();
|
||||
pb.entity_type = self.entity_type.into();
|
||||
pb.entity_state = self.entity_state.into();
|
||||
}
|
||||
}
|
||||
|
|
19
game-server/src/logic/components/equip.rs
Normal file
19
game-server/src/logic/components/equip.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use crate::logic::ecs::component::Component;
|
||||
use shorekeeper_protocol::entity_component_pb::ComponentPb;
|
||||
use shorekeeper_protocol::{EntityComponentPb, EquipComponentPb};
|
||||
|
||||
pub struct Equip {
|
||||
pub weapon_id: i32,
|
||||
pub weapon_breach_level: i32,
|
||||
}
|
||||
|
||||
impl Component for Equip {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::EquipComponent(EquipComponentPb {
|
||||
weapon_id: self.weapon_id,
|
||||
weapon_breach_level: self.weapon_breach_level,
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
25
game-server/src/logic/components/fsm.rs
Normal file
25
game-server/src/logic/components/fsm.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate::logic::ecs::component::Component;
|
||||
use shorekeeper_protocol::entity_component_pb::ComponentPb;
|
||||
use shorekeeper_protocol::{DFsm, DFsmBlackBoard, EntityComponentPb, EntityFsmComponentPb, FsmCustomBlackboardDatas};
|
||||
|
||||
pub struct Fsm {
|
||||
pub fsms: Vec<DFsm>,
|
||||
pub hash_code: i32,
|
||||
pub common_hash_code: i32,
|
||||
pub black_board: Vec<DFsmBlackBoard>,
|
||||
pub fsm_custom_blackboard_datas: Option<FsmCustomBlackboardDatas>,
|
||||
}
|
||||
|
||||
impl Component for Fsm {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::EntityFsmComponentPb(EntityFsmComponentPb {
|
||||
fsms: self.fsms.clone(),
|
||||
hash_code: self.hash_code,
|
||||
common_hash_code: self.common_hash_code,
|
||||
black_board: self.black_board.clone(),
|
||||
fsm_custom_blackboard_datas: self.fsm_custom_blackboard_datas.clone(),
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,15 +1,23 @@
|
|||
mod attribute;
|
||||
mod entity_config;
|
||||
mod equip;
|
||||
mod movement;
|
||||
mod owner_player;
|
||||
mod player_entity_marker;
|
||||
mod position;
|
||||
mod visibility;
|
||||
mod vision_skill;
|
||||
mod monster_ai;
|
||||
mod fsm;
|
||||
|
||||
pub use attribute::Attribute;
|
||||
pub use entity_config::EntityConfig;
|
||||
pub use equip::Equip;
|
||||
pub use movement::Movement;
|
||||
pub use owner_player::OwnerPlayer;
|
||||
pub use player_entity_marker::PlayerEntityMarker;
|
||||
pub use position::Position;
|
||||
pub use visibility::Visibility;
|
||||
pub use vision_skill::VisionSkill;
|
||||
pub use monster_ai::MonsterAi;
|
||||
pub use fsm::Fsm;
|
||||
|
|
23
game-server/src/logic/components/monster_ai.rs
Normal file
23
game-server/src/logic/components/monster_ai.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use crate::logic::ecs::component::Component;
|
||||
use shorekeeper_protocol::entity_component_pb::ComponentPb;
|
||||
use shorekeeper_protocol::{EntityComponentPb, MonsterAiComponentPb};
|
||||
|
||||
pub struct MonsterAi {
|
||||
pub weapon_id: i32,
|
||||
pub hatred_group_id: i64,
|
||||
pub ai_team_init_id: i32,
|
||||
pub combat_message_id: i64,
|
||||
}
|
||||
|
||||
impl Component for MonsterAi {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::MonsterAiComponentPb(MonsterAiComponentPb {
|
||||
weapon_id: self.weapon_id,
|
||||
hatred_group_id: self.hatred_group_id,
|
||||
ai_team_init_id: self.ai_team_init_id,
|
||||
combat_message_id: self.combat_message_id,
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
23
game-server/src/logic/components/vision_skill.rs
Normal file
23
game-server/src/logic/components/vision_skill.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use shorekeeper_protocol::{
|
||||
entity_component_pb::ComponentPb, EntityComponentPb, VisionSkillComponentPb,
|
||||
VisionSkillInformation,
|
||||
};
|
||||
|
||||
use crate::logic::ecs::component::Component;
|
||||
|
||||
pub struct VisionSkill {
|
||||
pub skill_id: i32,
|
||||
}
|
||||
|
||||
impl Component for VisionSkill {
|
||||
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
|
||||
pb.component_pbs.push(EntityComponentPb {
|
||||
component_pb: Some(ComponentPb::VisionSkillComponent(VisionSkillComponentPb {
|
||||
vision_skill_infos: vec![VisionSkillInformation {
|
||||
skill_id: self.skill_id,
|
||||
..Default::default()
|
||||
}],
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -30,6 +30,10 @@ impl_component_container! {
|
|||
Attribute;
|
||||
PlayerEntityMarker;
|
||||
Movement;
|
||||
Equip;
|
||||
VisionSkill;
|
||||
MonsterAi;
|
||||
Fsm;
|
||||
}
|
||||
|
||||
pub trait Component {
|
||||
|
|
|
@ -1,34 +1,118 @@
|
|||
use std::{cell::RefCell, collections::HashSet};
|
||||
|
||||
use super::component::ComponentContainer;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct Entity(i64);
|
||||
pub struct Entity {
|
||||
pub entity_id: i32,
|
||||
pub entity_type: i32,
|
||||
pub map_id: i32,
|
||||
}
|
||||
|
||||
pub struct EntityBuilder<'comp>(Entity, &'comp mut Vec<RefCell<ComponentContainer>>);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EntityManager {
|
||||
entity_id_counter: i64,
|
||||
active_entity_set: HashSet<Entity>,
|
||||
active_entity_set: HashMap<i32, Vec<Entity>>,
|
||||
next_id: AtomicI32,
|
||||
recycled_ids: HashMap<i32, VecDeque<i32>>,
|
||||
}
|
||||
|
||||
impl EntityManager {
|
||||
pub fn create(&mut self) -> Entity {
|
||||
self.entity_id_counter += 1;
|
||||
let entity = Entity(self.entity_id_counter);
|
||||
pub fn create(&mut self, config_id: i32, entity_type: i32, map_id: i32) -> Entity {
|
||||
let entity_id = self
|
||||
.recycled_ids
|
||||
.get_mut(&config_id)
|
||||
.and_then(|ids| ids.pop_front())
|
||||
.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
|
||||
|
||||
let entity = Entity {
|
||||
entity_id,
|
||||
entity_type,
|
||||
map_id,
|
||||
};
|
||||
|
||||
self.active_entity_set
|
||||
.entry(config_id)
|
||||
.or_default()
|
||||
.push(entity);
|
||||
|
||||
self.active_entity_set.insert(entity);
|
||||
entity
|
||||
}
|
||||
|
||||
pub fn get(&self, id: i64) -> Option<Entity> {
|
||||
self.active_entity_set.get(&Entity(id)).copied()
|
||||
pub fn get_entity_id(&self, config_id: i32) -> i32 {
|
||||
self.active_entity_set
|
||||
.get(&config_id)
|
||||
.and_then(|entities| entities.first())
|
||||
.map(|entity| entity.entity_id)
|
||||
.unwrap_or_else(|| {
|
||||
tracing::error!("Entity Configuration ID {} not found.", config_id);
|
||||
-1
|
||||
})
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn remove(&mut self, entity: Entity) -> bool {
|
||||
self.active_entity_set.remove(&entity)
|
||||
pub fn get_config_id(&self, entity_id: i32) -> i32 {
|
||||
self.active_entity_set
|
||||
.iter()
|
||||
.find_map(|(config_id, entities)| {
|
||||
entities
|
||||
.iter()
|
||||
.any(|e| e.entity_id == entity_id)
|
||||
.then_some(*config_id)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
tracing::error!("Entity ID {} not found.", entity_id);
|
||||
-1
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, config_id: i32) -> Entity {
|
||||
self.active_entity_set
|
||||
.get(&config_id)
|
||||
.and_then(|entities| entities.first())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
tracing::error!("Entity Configuration ID {} not found.", config_id);
|
||||
Entity::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_all_entity_id(&self) -> Vec<i32> {
|
||||
self.active_entity_set
|
||||
.iter()
|
||||
.flat_map(|(_, entities)| entities.iter().map(|e| e.entity_id))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn active_entity_empty(&self) -> bool {
|
||||
self.active_entity_set.is_empty()
|
||||
}
|
||||
|
||||
pub fn get_entity_ids_by_map(&self, map_id: i32) -> Vec<i32> {
|
||||
self.active_entity_set
|
||||
.iter()
|
||||
.flat_map(|(_, entities)| {
|
||||
entities
|
||||
.iter()
|
||||
.filter(|e| e.map_id == map_id)
|
||||
.map(|e| e.entity_id)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn remove(&mut self, entity_id: i32) -> bool {
|
||||
for (config_id, entities) in self.active_entity_set.iter_mut() {
|
||||
if let Some(index) = entities.iter().position(|e| e.entity_id == entity_id) {
|
||||
let entity = entities.remove(index);
|
||||
self.recycled_ids
|
||||
.entry(*config_id)
|
||||
.or_default()
|
||||
.push_back(entity.entity_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,6 +136,26 @@ impl<'comp> EntityBuilder<'comp> {
|
|||
|
||||
impl From<Entity> for i64 {
|
||||
fn from(value: Entity) -> Self {
|
||||
value.0
|
||||
value.entity_id as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EntityManager {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
active_entity_set: HashMap::new(),
|
||||
next_id: AtomicI32::new(1),
|
||||
recycled_ids: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Entity {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entity_id: -1,
|
||||
entity_type: -1,
|
||||
map_id: 8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,28 +2,56 @@ pub mod component;
|
|||
pub mod entity;
|
||||
pub mod world;
|
||||
|
||||
// Query specified components from all entities
|
||||
#[macro_export]
|
||||
macro_rules! find_component {
|
||||
($comps:expr, $comp:ident) => {
|
||||
$comps.iter().find_map(|comp| {
|
||||
let r = comp.try_borrow_mut().ok()?;
|
||||
if matches!(&*r, ComponentContainer::$comp(_)) {
|
||||
Some(::std::cell::RefMut::map(r, |r| {
|
||||
let ComponentContainer::$comp(comp_inner) = r else {
|
||||
unreachable!()
|
||||
};
|
||||
comp_inner
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// Query specified components from all entities (and)
|
||||
#[macro_export]
|
||||
macro_rules! query_with {
|
||||
($world:expr, $($comp:ident),*) => {
|
||||
$world.components().iter().filter(|(_, comps)| {
|
||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )* true
|
||||
($world_entitys:expr, $($comp:ident),*) => {
|
||||
$world_entitys.components().iter().filter(|(_, comps)| {
|
||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) && )
|
||||
* true
|
||||
})
|
||||
.map(|(e, comps)| {
|
||||
(*e,
|
||||
$(
|
||||
comps.iter().find_map(|comp| {
|
||||
let r = comp.try_borrow_mut().ok()?;
|
||||
if matches!(&*r, ComponentContainer::$comp(_)) {
|
||||
Some(::std::cell::RefMut::map(r, |r| {
|
||||
let ComponentContainer::$comp(comp_inner) = r else { unreachable!() };
|
||||
comp_inner
|
||||
}))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}).unwrap(),
|
||||
$crate::find_component!(comps, $comp).unwrap(),
|
||||
)*
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
}
|
||||
|
||||
// Query specified components from all entities (or)
|
||||
#[macro_export]
|
||||
macro_rules! query_hn_with {
|
||||
($world_entitys:expr, $($comp:ident),*) => {
|
||||
$world_entitys.components().iter().filter(|(_, comps)| {
|
||||
$(comps.iter().any(|comp| matches!(&*comp.borrow(), ComponentContainer::$comp(_))) || )
|
||||
* false
|
||||
})
|
||||
.map(|(e, comps)| {
|
||||
(*e,
|
||||
$(
|
||||
$crate::find_component!(comps, $comp),
|
||||
)*
|
||||
)
|
||||
})
|
||||
|
@ -41,24 +69,24 @@ macro_rules! ident_as_none {
|
|||
// Query components of specified entity
|
||||
#[macro_export]
|
||||
macro_rules! query_components {
|
||||
($world:expr, $entity_id:expr, $($comp:ident),*) => {
|
||||
$world.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
||||
($world_entitys:expr, $entity_id:expr, $($comp:ident),*) => {
|
||||
$world_entitys.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
|
||||
.map(|(_, comps)| {
|
||||
($(
|
||||
comps.iter().find_map(|comp| {
|
||||
let r = comp.try_borrow_mut().ok()?;
|
||||
if matches!(&*r, ComponentContainer::$comp(_)) {
|
||||
Some(::std::cell::RefMut::map(r, |r| {
|
||||
let ComponentContainer::$comp(comp_inner) = r else { unreachable!() };
|
||||
comp_inner
|
||||
}))
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
$crate::find_component!(comps, $comp),
|
||||
)*)
|
||||
})
|
||||
.unwrap_or_else(|| ($( crate::ident_as_none!($comp), )*))
|
||||
.unwrap_or_else(|| ($( $crate::ident_as_none!($comp), )*))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! modify_component {
|
||||
($comps:expr, $comp:ident, $modifier:expr) => {
|
||||
$comps.iter_mut().for_each(|comp| {
|
||||
if let ComponentContainer::$comp(ref mut inner_comp) = &mut **comp {
|
||||
$modifier(inner_comp);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,48 +1,30 @@
|
|||
use super::component::ComponentContainer;
|
||||
use super::entity::{Entity, EntityBuilder, EntityManager};
|
||||
use crate::logic::player::InWorldPlayer;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::hash_map::{Keys, Values};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::player::InWorldPlayer;
|
||||
|
||||
use super::component::ComponentContainer;
|
||||
use super::entity::{Entity, EntityBuilder, EntityManager};
|
||||
pub struct WorldEntity {
|
||||
components: HashMap<i32, Vec<RefCell<ComponentContainer>>>,
|
||||
entity_manager: EntityManager,
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
components: HashMap<Entity, Vec<RefCell<ComponentContainer>>>,
|
||||
entity_manager: EntityManager,
|
||||
in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
||||
pub player_cur_map_id: i32,
|
||||
pub world_entitys: HashMap<i32, WorldEntity>, // i32 -> map_id
|
||||
pub in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
components: HashMap::new(),
|
||||
entity_manager: EntityManager::default(),
|
||||
player_cur_map_id: 8,
|
||||
world_entitys: HashMap::new(),
|
||||
in_world_players: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_entity(&mut self) -> EntityBuilder {
|
||||
let entity = self.entity_manager.create();
|
||||
EntityBuilder::builder(entity, self.components.entry(entity).or_insert(Vec::new()))
|
||||
}
|
||||
|
||||
pub fn is_in_world(&self, entity_id: i64) -> bool {
|
||||
self.entity_manager.get(entity_id).is_some()
|
||||
}
|
||||
|
||||
pub fn components(&self) -> &HashMap<Entity, Vec<RefCell<ComponentContainer>>> {
|
||||
&self.components
|
||||
}
|
||||
|
||||
pub fn get_entity_components(&self, entity: Entity) -> Vec<RefMut<ComponentContainer>> {
|
||||
let Some(components) = self.components.get(&entity) else {
|
||||
return Vec::with_capacity(0);
|
||||
};
|
||||
|
||||
components.iter().map(|rc| rc.borrow_mut()).collect()
|
||||
}
|
||||
|
||||
pub fn player_ids(&self) -> Keys<'_, i32, InWorldPlayer> {
|
||||
self.in_world_players.keys()
|
||||
}
|
||||
|
@ -55,4 +37,84 @@ impl World {
|
|||
self.in_world_players
|
||||
.insert(in_world_player.player_id, in_world_player);
|
||||
}
|
||||
|
||||
pub fn get_mut_world_entity(&mut self) -> &mut WorldEntity {
|
||||
self.world_entitys
|
||||
.get_mut(&self.player_cur_map_id)
|
||||
.unwrap_or_else(|| panic!("Failed to get cur map data: {}", self.player_cur_map_id))
|
||||
}
|
||||
|
||||
pub fn get_world_entity(&self) -> &WorldEntity {
|
||||
self.world_entitys
|
||||
.get(&self.player_cur_map_id)
|
||||
.unwrap_or_else(|| panic!("Failed to get cur map data: {}", self.player_cur_map_id))
|
||||
}
|
||||
}
|
||||
|
||||
impl WorldEntity {
|
||||
pub fn create_entity(
|
||||
&mut self,
|
||||
config_id: i32,
|
||||
entity_type: i32,
|
||||
map_id: i32,
|
||||
) -> EntityBuilder {
|
||||
let entity = self.entity_manager.create(config_id, entity_type, map_id);
|
||||
EntityBuilder::builder(
|
||||
entity,
|
||||
self.components
|
||||
.entry(entity.entity_id)
|
||||
.or_insert(Vec::new()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_in_all_world_map(&self, entity_id: i32) -> bool {
|
||||
self.entity_manager.get_all_entity_id().contains(&entity_id)
|
||||
}
|
||||
|
||||
pub fn is_in_world_map(&self, entity_id: i32, map_id: i32) -> bool {
|
||||
self.entity_manager
|
||||
.get_entity_ids_by_map(map_id)
|
||||
.contains(&entity_id)
|
||||
}
|
||||
|
||||
pub fn get_entity_id(&self, config_id: i32) -> i64 {
|
||||
self.entity_manager.get_entity_id(config_id) as i64
|
||||
}
|
||||
|
||||
pub fn get_config_id(&self, entity_id: i32) -> i32 {
|
||||
self.entity_manager.get_config_id(entity_id)
|
||||
}
|
||||
|
||||
pub fn get_entity(&self, config_id: i32) -> Entity {
|
||||
self.entity_manager.get(config_id)
|
||||
}
|
||||
|
||||
pub fn components(&self) -> &HashMap<i32, Vec<RefCell<ComponentContainer>>> {
|
||||
&self.components
|
||||
}
|
||||
|
||||
pub fn get_entity_components(&self, entity_id: i32) -> Vec<RefMut<ComponentContainer>> {
|
||||
if let Some(components) = self.components.get(&entity_id) {
|
||||
components.iter().map(|rc| rc.borrow_mut()).collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_entity(&mut self, entity_id: i32) -> bool {
|
||||
self.components.remove(&entity_id).is_some() && self.entity_manager.remove(entity_id)
|
||||
}
|
||||
|
||||
pub fn active_entity_empty(&self) -> bool {
|
||||
self.entity_manager.active_entity_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WorldEntity {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
components: HashMap::new(),
|
||||
entity_manager: EntityManager::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
73
game-server/src/logic/handler/combat.rs
Normal file
73
game-server/src/logic/handler/combat.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use crate::logic::player::Player;
|
||||
use shorekeeper_protocol::combat_message::{
|
||||
combat_receive_data, combat_request_data, combat_response_data, combat_send_data,
|
||||
CombatReceiveData, CombatRequestData, CombatResponseData, CombatSendPackRequest,
|
||||
CombatSendPackResponse,
|
||||
};
|
||||
use shorekeeper_protocol::{ErrorCode, SwitchRoleRequest, SwitchRoleResponse};
|
||||
|
||||
#[inline(always)]
|
||||
fn create_combat_response(
|
||||
combat_request: &CombatRequestData,
|
||||
message: combat_response_data::Message,
|
||||
) -> CombatReceiveData {
|
||||
CombatReceiveData {
|
||||
message: Some(combat_receive_data::Message::CombatResponseData(
|
||||
CombatResponseData {
|
||||
combat_common: combat_request.combat_common,
|
||||
request_id: combat_request.request_id,
|
||||
message: Some(message),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_combat_message_combat_send_pack_request(
|
||||
player: &mut Player,
|
||||
request: CombatSendPackRequest,
|
||||
response: &mut CombatSendPackResponse,
|
||||
) {
|
||||
for data in request.data.iter() {
|
||||
if let Some(combat_send_data::Message::Request(ref request_data)) = data.message {
|
||||
if let Some(ref request_message) = request_data.message {
|
||||
match request_message {
|
||||
combat_request_data::Message::SwitchRoleRequest(ref request) => {
|
||||
handle_switch_role_request(player, request_data, request, response);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
fn handle_switch_role_request(
|
||||
player: &mut Player,
|
||||
combat_request: &CombatRequestData,
|
||||
request: &SwitchRoleRequest,
|
||||
response: &mut CombatSendPackResponse,
|
||||
) {
|
||||
// Find current formation and update current role
|
||||
if let Some(formation) = player.formation_list.values_mut().find(|f| f.is_current) {
|
||||
formation.cur_role = request.role_id;
|
||||
|
||||
let receive_pack = response
|
||||
.receive_pack_notify
|
||||
.get_or_insert_with(Default::default);
|
||||
|
||||
receive_pack.data.push(create_combat_response(
|
||||
combat_request,
|
||||
combat_response_data::Message::SwitchRoleResponse(SwitchRoleResponse {
|
||||
error_code: ErrorCode::Success.into(),
|
||||
role_id: request.role_id,
|
||||
}),
|
||||
));
|
||||
} else {
|
||||
tracing::error!("Role with id {} not found", request.role_id);
|
||||
response.error_code = ErrorCode::ErrSwitchRoleEntityNotExist.into();
|
||||
return;
|
||||
}
|
||||
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
139
game-server/src/logic/handler/entity.rs
Normal file
139
game-server/src/logic/handler/entity.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use shorekeeper_protocol::{EntityActiveRequest, EntityActiveResponse, EntityLoadCompleteRequest,
|
||||
EntityLoadCompleteResponse, EntityOnLandedRequest,
|
||||
EntityOnLandedResponse, EntityPb, EntityPositionRequest,
|
||||
EntityPositionResponse, ErrorCode, MovePackagePush};
|
||||
|
||||
use crate::{logic, logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
|
||||
|
||||
pub fn on_entity_active_request(
|
||||
player: &Player,
|
||||
request: EntityActiveRequest,
|
||||
response: &mut EntityActiveResponse,
|
||||
) {
|
||||
let world_ref = player.world.borrow();
|
||||
let world = world_ref.get_world_entity();
|
||||
|
||||
if !world.is_in_all_world_map(request.entity_id as i32) {
|
||||
tracing::debug!(
|
||||
"EntityActiveRequest: entity with id {} doesn't exist, player_id: {}",
|
||||
request.entity_id,
|
||||
player.basic_info.id
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let component_pbs = {
|
||||
let mut pb = EntityPb {
|
||||
id: request.entity_id,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
world.get_entity_components(request.entity_id as i32)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
pb.component_pbs
|
||||
};
|
||||
|
||||
// TODO: Remove attribute
|
||||
if let (Some(position), Some(_attribute)) =
|
||||
query_components!(world, request.entity_id, Position, Attribute)
|
||||
{
|
||||
response.is_visible = true;
|
||||
response.pos = Some(position.0.get_position_protobuf());
|
||||
response.rot = Some(position.0.get_rotation_protobuf());
|
||||
|
||||
response.component_pbs.extend_from_slice(&component_pbs);
|
||||
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
} else {
|
||||
tracing::error!(
|
||||
"EntityActiveRequest: entity with id {} not found",
|
||||
request.entity_id
|
||||
);
|
||||
response.error_code = ErrorCode::ErrEntityNotFound.into(); // TODO: replace with appropriate error code
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn on_entity_on_landed_request(
|
||||
_: &Player,
|
||||
request: EntityOnLandedRequest,
|
||||
_: &mut EntityOnLandedResponse,
|
||||
) {
|
||||
// TODO: More implementation?
|
||||
tracing::debug!(
|
||||
"EntityOnLandedRequest: entity with id {} landed",
|
||||
request.entity_id
|
||||
);
|
||||
}
|
||||
|
||||
pub fn on_entity_position_request(
|
||||
_: &Player,
|
||||
request: EntityPositionRequest,
|
||||
_: &mut EntityPositionResponse,
|
||||
) {
|
||||
// TODO: Implement this
|
||||
tracing::debug!(
|
||||
"EntityPositionRequest: config with id {} for map {} position requested",
|
||||
request.config_id,
|
||||
request.map_id
|
||||
);
|
||||
}
|
||||
|
||||
pub fn on_entity_load_complete_request(
|
||||
_: &Player,
|
||||
request: EntityLoadCompleteRequest,
|
||||
_: &mut EntityLoadCompleteResponse,
|
||||
) {
|
||||
// TODO: Implement this
|
||||
tracing::debug!(
|
||||
"EntityLoadCompleteRequest: for ids {:?}",
|
||||
request.entity_ids
|
||||
);
|
||||
}
|
||||
|
||||
pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) {
|
||||
for moving_entity in push.moving_entities {
|
||||
// Query components borrows world component so lets wrap it
|
||||
{
|
||||
let world_ref = player.world.borrow();
|
||||
let world = world_ref.get_world_entity();
|
||||
|
||||
if !world.is_in_all_world_map(moving_entity.entity_id as i32) {
|
||||
tracing::debug!(
|
||||
"MovePackage: entity with id {} doesn't exist",
|
||||
moving_entity.entity_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(mut movement) = query_components!(world, moving_entity.entity_id, Movement).0
|
||||
else {
|
||||
tracing::warn!(
|
||||
"MovePackage: entity {} doesn't have movement component",
|
||||
moving_entity.entity_id
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
movement
|
||||
.pending_movement_vec
|
||||
.extend(moving_entity.move_infos);
|
||||
}
|
||||
|
||||
// TODO: review instance id vs map id in world
|
||||
let map = logic::utils::quadrant_util::get_map(player.location.instance_id);
|
||||
let quadrant_id = map.get_quadrant_id(
|
||||
player.location.position.position.x * 100.0,
|
||||
player.location.position.position.y * 100.0,
|
||||
);
|
||||
|
||||
// TODO: This may require some changes for Co-Op
|
||||
if quadrant_id != player.quadrant_id {
|
||||
let (entities_to_remove, entities_to_add) = map.get_update_entities(player.quadrant_id, quadrant_id);
|
||||
player.quadrant_id = quadrant_id;
|
||||
logic::utils::world_util::remove_entities(player, &entities_to_remove);
|
||||
logic::utils::world_util::add_entities(player, &entities_to_add);
|
||||
}
|
||||
}
|
||||
}
|
12
game-server/src/logic/handler/guide.rs
Normal file
12
game-server/src/logic/handler/guide.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use shorekeeper_protocol::{GuideInfoRequest, GuideInfoResponse};
|
||||
|
||||
use crate::logic::player::Player;
|
||||
|
||||
pub fn on_guide_info_request(
|
||||
_player: &Player,
|
||||
_request: GuideInfoRequest,
|
||||
response: &mut GuideInfoResponse,
|
||||
) {
|
||||
// TODO: Implement this
|
||||
response.guide_group_finish_list = Vec::new();
|
||||
}
|
16
game-server/src/logic/handler/mail.rs
Normal file
16
game-server/src/logic/handler/mail.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use shorekeeper_protocol::{MailBind, MailBindInfoRequest, MailBindInfoResponse};
|
||||
|
||||
use crate::logic::player::Player;
|
||||
|
||||
pub fn on_mail_bind_info_request(
|
||||
_: &Player,
|
||||
_: MailBindInfoRequest,
|
||||
response: &mut MailBindInfoResponse,
|
||||
) {
|
||||
// TODO: Implement this
|
||||
response.mail_bind = Some(MailBind {
|
||||
is_bind: true,
|
||||
is_reward: true,
|
||||
close_time: -1,
|
||||
});
|
||||
}
|
49
game-server/src/logic/handler/misc.rs
Normal file
49
game-server/src/logic/handler/misc.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use shorekeeper_protocol::{
|
||||
ErrorCode, Hih, InputSettingRequest, InputSettingResponse, InputSettingUpdateRequest,
|
||||
InputSettingUpdateResponse, LanguageSettingUpdateRequest, LanguageSettingUpdateResponse,
|
||||
ServerPlayStationPlayOnlyStateRequest, ServerPlayStationPlayOnlyStateResponse, VersionInfoPush,
|
||||
};
|
||||
|
||||
use crate::logic::player::Player;
|
||||
|
||||
pub fn on_input_setting_request(
|
||||
_: &Player,
|
||||
_: InputSettingRequest,
|
||||
response: &mut InputSettingResponse,
|
||||
) {
|
||||
response.hih = Some(Hih::default());
|
||||
}
|
||||
|
||||
pub fn on_input_setting_update_request(
|
||||
_: &Player,
|
||||
_: InputSettingUpdateRequest,
|
||||
response: &mut InputSettingUpdateResponse,
|
||||
) {
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_language_setting_update_request(
|
||||
_: &Player,
|
||||
_: LanguageSettingUpdateRequest,
|
||||
response: &mut LanguageSettingUpdateResponse,
|
||||
) {
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_server_play_station_play_only_state_request(
|
||||
_: &Player,
|
||||
_: ServerPlayStationPlayOnlyStateRequest,
|
||||
response: &mut ServerPlayStationPlayOnlyStateResponse,
|
||||
) {
|
||||
response.play_station_play_only_state = false;
|
||||
}
|
||||
|
||||
pub fn on_version_info_push(_player: &Player, push: VersionInfoPush) {
|
||||
// TODO: Shall we do safety check and ensure we have compatible versions?
|
||||
tracing::debug!(
|
||||
"Client versions: launcher: {}, app: {}, resources: {}",
|
||||
push.launcher_version,
|
||||
push.app_version,
|
||||
push.resource_version
|
||||
);
|
||||
}
|
|
@ -1,31 +1,53 @@
|
|||
mod combat;
|
||||
mod entity;
|
||||
mod guide;
|
||||
mod mail;
|
||||
mod misc;
|
||||
mod role;
|
||||
mod scene;
|
||||
mod skill;
|
||||
|
||||
pub use combat::*;
|
||||
pub use entity::*;
|
||||
pub use guide::*;
|
||||
pub use mail::*;
|
||||
pub use misc::*;
|
||||
pub use role::*;
|
||||
pub use scene::*;
|
||||
pub use skill::*;
|
||||
|
||||
use shorekeeper_protocol::message::Message;
|
||||
|
||||
macro_rules! handle_request {
|
||||
($($name:ident;)*) => {
|
||||
($($name:ident $(, $inner_package:ident)?;)*) => {
|
||||
fn handle_request(player: &mut super::player::Player, mut msg: Message) {
|
||||
use ::shorekeeper_protocol::{MessageID, Protobuf};
|
||||
|
||||
::paste::paste! {
|
||||
match msg.get_message_id() {
|
||||
$(
|
||||
::shorekeeper_protocol::[<$name Request>]::MESSAGE_ID => {
|
||||
let Ok(request) = ::shorekeeper_protocol::[<$name Request>]::decode(&*msg.remove_payload()) else {
|
||||
tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Request>]), player.basic_info.id);
|
||||
::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::MESSAGE_ID => {
|
||||
let Ok(request) = ::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::decode(&*msg.remove_payload()) else {
|
||||
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Request>]), player.basic_info.id);
|
||||
return;
|
||||
};
|
||||
|
||||
tracing::debug!("logic: processing request {}", stringify!([<$name Request>]));
|
||||
tracing::debug!("logic: processing request {}", stringify!($($inner_package::)?[<$name Request>]));
|
||||
|
||||
let mut response = ::shorekeeper_protocol::[<$name Response>]::default();
|
||||
[<on_ $name:snake _request>](player, request, &mut response);
|
||||
let mut response = ::shorekeeper_protocol::$($inner_package::)?[<$name Response>]::default();
|
||||
[<on_ $($inner_package:snake _)? $name:snake _request>](player, request, &mut response);
|
||||
|
||||
player.respond(response, msg.get_rpc_id());
|
||||
},
|
||||
)*
|
||||
unhandled => ::tracing::warn!("can't find handler for request with message_id={unhandled}")
|
||||
unhandled => {
|
||||
::tracing::warn!("can't find handler for request with message_id={unhandled}");
|
||||
let tmp = &*msg.remove_payload();
|
||||
let (name, value) = shorekeeper_protocol::proto_dumper::get_debug_info(
|
||||
unhandled, tmp,
|
||||
).unwrap_or_else(|err| ("Error", err.to_string()));
|
||||
tracing::debug!("trying to log unhandled data for message {name} with:\n{value}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,23 +55,32 @@ macro_rules! handle_request {
|
|||
}
|
||||
|
||||
macro_rules! handle_push {
|
||||
($($name:ident;)*) => {
|
||||
($($name:ident $(, $inner_package:ident)?;)*) => {
|
||||
fn handle_push(player: &mut super::player::Player, mut msg: Message) {
|
||||
use ::shorekeeper_protocol::{MessageID, Protobuf};
|
||||
|
||||
::paste::paste! {
|
||||
match msg.get_message_id() {
|
||||
$(
|
||||
::shorekeeper_protocol::[<$name Push>]::MESSAGE_ID => {
|
||||
let Ok(push) = ::shorekeeper_protocol::[<$name Push>]::decode(&*msg.remove_payload()) else {
|
||||
tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Push>]), player.basic_info.id);
|
||||
::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::MESSAGE_ID => {
|
||||
let Ok(push) = ::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::decode(&*msg.remove_payload()) else {
|
||||
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Push>]), player.basic_info.id);
|
||||
return;
|
||||
};
|
||||
|
||||
[<on_ $name:snake _push>](player, push);
|
||||
tracing::debug!("logic: processing push {}", stringify!($($inner_package::)?[<$name Push>]));
|
||||
|
||||
[<on_ $($inner_package:snake _)? $name:snake _push>](player, push);
|
||||
},
|
||||
)*
|
||||
unhandled => ::tracing::warn!("can't find handler for push with message_id={unhandled}")
|
||||
unhandled => {
|
||||
::tracing::warn!("can't find handler for push with message_id={unhandled}");
|
||||
let tmp = &*msg.remove_payload();
|
||||
let (name, value) = shorekeeper_protocol::proto_dumper::get_debug_info(
|
||||
unhandled, tmp,
|
||||
).unwrap_or_else(|err| ("Error", err.to_string()));
|
||||
tracing::debug!("trying to log unhandled data for message {name} with:\n{value}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,13 +88,49 @@ macro_rules! handle_push {
|
|||
}
|
||||
|
||||
handle_request! {
|
||||
UpdateSceneDate;
|
||||
// Combat
|
||||
CombatSendPack, combat_message;
|
||||
|
||||
// Role
|
||||
RoleShowListUpdate;
|
||||
ClientCurrentRoleReport;
|
||||
RoleFavorList;
|
||||
FormationAttr;
|
||||
UpdateFormation;
|
||||
|
||||
// Entity
|
||||
EntityActive;
|
||||
EntityOnLanded;
|
||||
EntityPosition;
|
||||
EntityLoadComplete;
|
||||
|
||||
// Guide
|
||||
GuideInfo;
|
||||
|
||||
// Mail
|
||||
MailBindInfo;
|
||||
|
||||
// Misc
|
||||
InputSetting;
|
||||
InputSettingUpdate;
|
||||
LanguageSettingUpdate;
|
||||
ServerPlayStationPlayOnlyState;
|
||||
|
||||
// Scene
|
||||
SceneTrace;
|
||||
SceneLoadingFinish;
|
||||
UpdateSceneDate;
|
||||
|
||||
// Skill
|
||||
VisionExploreSkillSet;
|
||||
}
|
||||
|
||||
handle_push! {
|
||||
// Entity
|
||||
MovePackage;
|
||||
|
||||
// Misc
|
||||
VersionInfo;
|
||||
}
|
||||
|
||||
pub fn handle_logic_message(player: &mut super::player::Player, msg: Message) {
|
||||
|
|
141
game-server/src/logic/handler/role.rs
Normal file
141
game-server/src/logic/handler/role.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
use crate::logic::player::Player;
|
||||
use crate::logic::role::{Role, RoleFormation};
|
||||
use shorekeeper_protocol::{
|
||||
ClientCurrentRoleReportRequest, ClientCurrentRoleReportResponse, ERemoveEntityType, ErrorCode,
|
||||
FormationAttrRequest, FormationAttrResponse, RoleFavorListRequest, RoleFavorListResponse,
|
||||
RoleShowListUpdateRequest, RoleShowListUpdateResponse, UpdateFormationRequest,
|
||||
UpdateFormationResponse,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn on_role_show_list_update_request(
|
||||
player: &mut Player,
|
||||
request: RoleShowListUpdateRequest,
|
||||
response: &mut RoleShowListUpdateResponse,
|
||||
) {
|
||||
let role_ids: HashSet<i32> = player.role_list.keys().cloned().collect();
|
||||
let all_exist = request.role_list.iter().all(|id| role_ids.contains(id));
|
||||
|
||||
if all_exist {
|
||||
player.basic_info.role_show_list = request.role_list;
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
} else {
|
||||
response.error_code = ErrorCode::InvalidRequest.into(); // TODO: replace with appropriate error code
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_client_current_role_report_request(
|
||||
_player: &Player,
|
||||
request: ClientCurrentRoleReportRequest,
|
||||
response: &mut ClientCurrentRoleReportResponse,
|
||||
) {
|
||||
response.current_entity_id = request.current_entity_id;
|
||||
response.player_id = request.player_id;
|
||||
}
|
||||
|
||||
pub fn on_role_favor_list_request(
|
||||
_player: &Player,
|
||||
_request: RoleFavorListRequest,
|
||||
response: &mut RoleFavorListResponse,
|
||||
) {
|
||||
response.favor_list = vec![]; // TODO: add favor
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_formation_attr_request(
|
||||
_player: &Player,
|
||||
_request: FormationAttrRequest,
|
||||
response: &mut FormationAttrResponse,
|
||||
) {
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_update_formation_request(
|
||||
player: &mut Player,
|
||||
request: UpdateFormationRequest,
|
||||
response: &mut UpdateFormationResponse,
|
||||
) {
|
||||
let mut world_ref = player.world.borrow_mut();
|
||||
let world = world_ref.get_mut_world_entity();
|
||||
|
||||
for formation in request.formations {
|
||||
let formation_id = formation.formation_id;
|
||||
let cur_role = formation.cur_role;
|
||||
let is_current = formation.is_current;
|
||||
|
||||
if is_current {
|
||||
// update player current formation id
|
||||
player.cur_formation_id = formation_id;
|
||||
|
||||
// search old formation id and set real_formation_id, set is_current to false
|
||||
let mut real_formation_id = formation_id;
|
||||
if let Some(rf) = player
|
||||
.formation_list
|
||||
.values_mut()
|
||||
.find(|rf| rf.is_current && rf.id != formation_id)
|
||||
{
|
||||
real_formation_id = rf.id;
|
||||
rf.is_current = false;
|
||||
}
|
||||
|
||||
if let Some(old_formation) = player.formation_list.get(&real_formation_id) {
|
||||
let removed_entities: Vec<i64> = old_formation
|
||||
.role_ids
|
||||
.iter()
|
||||
.map(|&role_id| world.get_entity_id(role_id))
|
||||
.collect();
|
||||
removed_entities.iter().for_each(|&entity_id| {
|
||||
world.remove_entity(entity_id as i32);
|
||||
});
|
||||
player.notify(player.build_player_entity_remove_notify(
|
||||
removed_entities,
|
||||
ERemoveEntityType::RemoveTypeNormal,
|
||||
));
|
||||
}
|
||||
|
||||
let added_roles: Vec<Role> = formation
|
||||
.role_ids
|
||||
.iter()
|
||||
.map(|&role_id| Role::new(role_id))
|
||||
.collect();
|
||||
|
||||
if !added_roles.is_empty() {
|
||||
// add new roles
|
||||
player.notify(player.build_player_entity_add_notify(added_roles, world));
|
||||
}
|
||||
|
||||
// send update group formation notify
|
||||
player.notify(player.build_update_group_formation_notify(
|
||||
RoleFormation {
|
||||
id: formation_id,
|
||||
cur_role,
|
||||
role_ids: formation.role_ids.clone(),
|
||||
is_current,
|
||||
},
|
||||
world,
|
||||
));
|
||||
|
||||
response.formation = Some(formation.clone());
|
||||
}
|
||||
|
||||
// update all formation and check formation_list
|
||||
player
|
||||
.formation_list
|
||||
.entry(formation_id)
|
||||
.and_modify(|r| {
|
||||
r.cur_role = formation.cur_role;
|
||||
r.role_ids = formation.role_ids.clone();
|
||||
r.is_current = is_current;
|
||||
})
|
||||
.or_insert(RoleFormation {
|
||||
id: formation_id,
|
||||
cur_role: formation.cur_role,
|
||||
role_ids: formation.role_ids,
|
||||
is_current,
|
||||
});
|
||||
}
|
||||
|
||||
player.notify(player.build_update_formation_notify());
|
||||
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
|
@ -1,9 +1,26 @@
|
|||
use shorekeeper_protocol::{
|
||||
EntityActiveRequest, EntityActiveResponse, EntityOnLandedRequest, EntityOnLandedResponse,
|
||||
ErrorCode, MovePackagePush, UpdateSceneDateRequest, UpdateSceneDateResponse,
|
||||
ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse, SceneTraceRequest,
|
||||
SceneTraceResponse, UpdateSceneDateRequest, UpdateSceneDateResponse,
|
||||
};
|
||||
|
||||
use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
|
||||
use crate::logic::player::Player;
|
||||
|
||||
pub fn on_scene_trace_request(
|
||||
_player: &Player,
|
||||
request: SceneTraceRequest,
|
||||
_: &mut SceneTraceResponse,
|
||||
) {
|
||||
tracing::debug!("SceneTraceRequest: trace id {}", request.scene_trace_id);
|
||||
}
|
||||
|
||||
pub fn on_scene_loading_finish_request(
|
||||
_player: &Player,
|
||||
_request: SceneLoadingFinishRequest,
|
||||
response: &mut SceneLoadingFinishResponse,
|
||||
) {
|
||||
// TODO: Implement this if needed
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_update_scene_date_request(
|
||||
_player: &Player,
|
||||
|
@ -12,66 +29,3 @@ pub fn on_update_scene_date_request(
|
|||
) {
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_entity_active_request(
|
||||
player: &Player,
|
||||
request: EntityActiveRequest,
|
||||
response: &mut EntityActiveResponse,
|
||||
) {
|
||||
let world = player.world.borrow();
|
||||
|
||||
if !world.is_in_world(request.entity_id) {
|
||||
tracing::debug!(
|
||||
"EntityActiveRequest: entity with id {} doesn't exist, player_id: {}",
|
||||
request.entity_id,
|
||||
player.basic_info.id
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(position) = query_components!(world, request.entity_id, Position).0 {
|
||||
// TODO: proper entity "activation" logic
|
||||
response.pos = Some(position.0.get_position_protobuf());
|
||||
response.rot = Some(position.0.get_rotation_protobuf());
|
||||
}
|
||||
response.component_pbs = Vec::new(); // not implemented
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
}
|
||||
|
||||
pub fn on_entity_on_landed_request(
|
||||
_: &Player,
|
||||
request: EntityOnLandedRequest,
|
||||
_: &mut EntityOnLandedResponse,
|
||||
) {
|
||||
tracing::debug!(
|
||||
"EntityOnLandedRequest: entity with id {} landed",
|
||||
request.entity_id
|
||||
);
|
||||
}
|
||||
|
||||
pub fn on_move_package_push(player: &mut Player, push: MovePackagePush) {
|
||||
let world = player.world.borrow();
|
||||
|
||||
for moving_entity in push.moving_entities {
|
||||
if !world.is_in_world(moving_entity.entity_id) {
|
||||
tracing::debug!(
|
||||
"MovePackage: entity with id {} doesn't exist",
|
||||
moving_entity.entity_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(mut movement) = query_components!(world, moving_entity.entity_id, Movement).0
|
||||
else {
|
||||
tracing::warn!(
|
||||
"MovePackage: entity {} doesn't have movement component",
|
||||
moving_entity.entity_id
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
movement
|
||||
.pending_movement_vec
|
||||
.extend(moving_entity.move_infos);
|
||||
}
|
||||
}
|
||||
|
|
35
game-server/src/logic/handler/skill.rs
Normal file
35
game-server/src/logic/handler/skill.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
use crate::logic::ecs::component::ComponentContainer;
|
||||
use shorekeeper_protocol::{
|
||||
VisionExploreSkillSetRequest, VisionExploreSkillSetResponse, VisionSkillChangeNotify,
|
||||
VisionSkillInformation,
|
||||
};
|
||||
|
||||
use crate::{logic::player::Player, query_with};
|
||||
|
||||
pub fn on_vision_explore_skill_set_request(
|
||||
player: &mut Player,
|
||||
request: VisionExploreSkillSetRequest,
|
||||
response: &mut VisionExploreSkillSetResponse,
|
||||
) {
|
||||
player.explore_tools.active_explore_skill = request.skill_id;
|
||||
|
||||
for (entity, owner, mut vision_skill) in query_with!(
|
||||
player.world.borrow().get_world_entity(),
|
||||
OwnerPlayer,
|
||||
VisionSkill
|
||||
) {
|
||||
if owner.0 == player.basic_info.id {
|
||||
vision_skill.skill_id = request.skill_id;
|
||||
player.notify(VisionSkillChangeNotify {
|
||||
entity_id: entity.into(),
|
||||
vision_skill_infos: vec![VisionSkillInformation {
|
||||
skill_id: request.skill_id,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
response.skill_id = request.skill_id;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use shorekeeper_data::RawVectorData;
|
||||
use shorekeeper_protocol::{Rotator, TransformData};
|
||||
|
||||
use super::Vector3f;
|
||||
|
@ -47,3 +48,12 @@ impl Transform {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[RawVectorData]> for Transform {
|
||||
fn from(transform: &[RawVectorData]) -> Self {
|
||||
Self {
|
||||
position: Vector3f::from(&transform[0]),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use shorekeeper_data::RawVectorData;
|
||||
use shorekeeper_protocol::{Vector, VectorData};
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Debug)]
|
||||
|
@ -40,3 +41,13 @@ impl Vector3f {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&RawVectorData> for Vector3f {
|
||||
fn from(transform: &RawVectorData) -> Self {
|
||||
Self {
|
||||
x: transform.x / 100.0,
|
||||
y: transform.y / 100.0,
|
||||
z: transform.z / 100.0,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ pub struct PlayerBasicInfo {
|
|||
pub exp: i32,
|
||||
pub head_photo: i32,
|
||||
pub head_frame: i32,
|
||||
pub cur_map_id: i32,
|
||||
pub role_show_list: Vec<i32>,
|
||||
}
|
||||
|
||||
impl PlayerBasicInfo {
|
||||
|
@ -37,6 +39,8 @@ impl PlayerBasicInfo {
|
|||
exp: data.exp,
|
||||
head_photo: data.head_photo,
|
||||
head_frame: data.head_frame,
|
||||
cur_map_id: data.cur_map_id,
|
||||
role_show_list: data.role_show_list,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +53,8 @@ impl PlayerBasicInfo {
|
|||
exp: self.exp,
|
||||
head_photo: self.head_photo,
|
||||
head_frame: self.head_frame,
|
||||
cur_map_id: self.cur_map_id,
|
||||
role_show_list: self.role_show_list.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
76
game-server/src/logic/player/explore_tools.rs
Normal file
76
game-server/src/logic/player/explore_tools.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use shorekeeper_data::explore_tools_data;
|
||||
use shorekeeper_protocol::{
|
||||
ExploreSkillRoulette, ExploreSkillRouletteUpdateNotify, ExploreToolAllNotify,
|
||||
PlayerExploreToolsData,
|
||||
};
|
||||
|
||||
type Roulette = [i32; 8];
|
||||
|
||||
pub struct ExploreTools {
|
||||
pub unlocked_explore_skills: HashSet<i32>,
|
||||
pub active_explore_skill: i32,
|
||||
pub roulette: Roulette,
|
||||
}
|
||||
|
||||
impl ExploreTools {
|
||||
pub fn build_save_data(&self) -> PlayerExploreToolsData {
|
||||
PlayerExploreToolsData {
|
||||
unlocked_skill_list: self.unlocked_explore_skills.iter().cloned().collect(),
|
||||
active_skill_id: self.active_explore_skill,
|
||||
roulette: self.roulette.iter().cloned().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_save(data: PlayerExploreToolsData) -> Self {
|
||||
Self {
|
||||
unlocked_explore_skills: data.unlocked_skill_list.into_iter().collect(),
|
||||
active_explore_skill: data.active_skill_id,
|
||||
roulette: data
|
||||
.roulette
|
||||
.try_into()
|
||||
.unwrap_or_else(|_| Self::default_roulette()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_explore_tool_all_notify(&self) -> ExploreToolAllNotify {
|
||||
ExploreToolAllNotify {
|
||||
skill_list: self.unlocked_explore_skills.iter().cloned().collect(),
|
||||
explore_skill: self.active_explore_skill,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_roulette_update_notify(&self) -> ExploreSkillRouletteUpdateNotify {
|
||||
ExploreSkillRouletteUpdateNotify {
|
||||
roulette_info: vec![ExploreSkillRoulette {
|
||||
skill_ids: self.roulette.iter().cloned().collect(),
|
||||
extra_item_id: 0,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
fn default_roulette() -> Roulette {
|
||||
let mut roulette = [0i32; 8];
|
||||
explore_tools_data::iter()
|
||||
.take(3)
|
||||
.enumerate()
|
||||
.for_each(|(i, e)| roulette[i] = e.phantom_skill_id);
|
||||
|
||||
roulette
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ExploreTools {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unlocked_explore_skills: explore_tools_data::iter()
|
||||
.filter(|e| e.authorization.is_empty())
|
||||
.map(|e| e.phantom_skill_id)
|
||||
.collect(),
|
||||
active_explore_skill: 1001,
|
||||
roulette: Self::default_roulette(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,23 @@
|
|||
use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
|
||||
|
||||
use basic_info::PlayerBasicInfo;
|
||||
use common::time_util;
|
||||
use location::PlayerLocation;
|
||||
use player_func::PlayerFunc;
|
||||
use shorekeeper_protocol::{
|
||||
message::Message, PbGetRoleListNotify, PlayerBasicData, PlayerRoleData, PlayerSaveData,
|
||||
ProtocolUnit,
|
||||
EEntityType, EntityState, ERemoveEntityType, EntityAddNotify, EntityConfigType, EntityPb, EntityRemoveInfo,
|
||||
EntityRemoveNotify, FightFormationNotifyInfo, FightRoleInfo, FightRoleInfos, FormationRoleInfo,
|
||||
GroupFormation, ItemPkgOpenNotify, LivingStatus, PbGetRoleListNotify, PlayerBasicData,
|
||||
PlayerFightFormations, PlayerRoleData, PlayerSaveData, ProtocolUnit, UpdateFormationNotify,
|
||||
UpdateGroupFormationNotify,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::logic::{
|
||||
components::{
|
||||
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
||||
Visibility, VisionSkill,
|
||||
},
|
||||
ecs::component::ComponentContainer,
|
||||
};
|
||||
use crate::session::Session;
|
||||
|
||||
use super::{
|
||||
|
@ -17,94 +26,145 @@ use super::{
|
|||
};
|
||||
|
||||
mod basic_info;
|
||||
mod explore_tools;
|
||||
mod in_world_player;
|
||||
mod location;
|
||||
mod player_func;
|
||||
|
||||
use crate::create_player_entity_pb;
|
||||
use crate::logic::ecs::world::WorldEntity;
|
||||
use crate::logic::player::basic_info::PlayerBasicInfo;
|
||||
use crate::logic::player::explore_tools::ExploreTools;
|
||||
use crate::logic::player::location::PlayerLocation;
|
||||
use crate::logic::player::player_func::PlayerFunc;
|
||||
pub use in_world_player::InWorldPlayer;
|
||||
use shorekeeper_data::base_property_data;
|
||||
use shorekeeper_data::role_info_data;
|
||||
use shorekeeper_protocol::message::Message;
|
||||
|
||||
pub struct Player {
|
||||
session: Option<Arc<Session>>,
|
||||
// Persistent
|
||||
pub basic_info: PlayerBasicInfo,
|
||||
pub role_list: Vec<Role>,
|
||||
pub formation_list: Vec<RoleFormation>,
|
||||
pub role_list: HashMap<i32, Role>,
|
||||
pub formation_list: HashMap<i32, RoleFormation>,
|
||||
pub cur_formation_id: i32,
|
||||
pub location: PlayerLocation,
|
||||
pub func: PlayerFunc,
|
||||
pub explore_tools: ExploreTools,
|
||||
// Runtime
|
||||
pub world: Rc<RefCell<World>>,
|
||||
pub last_save_time: u64,
|
||||
pub quadrant_id: u64,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn init(&mut self) {
|
||||
if self.role_list.is_empty() {
|
||||
self.on_first_enter();
|
||||
if self.role_list.is_empty() || self.formation_list.is_empty() {
|
||||
self.init_role_and_formation();
|
||||
}
|
||||
|
||||
// we need shorekeeper
|
||||
// TODO: remove this part after implementing team switch
|
||||
if !self.role_list.iter().any(|r| r.role_id == 1505) {
|
||||
self.role_list.push(Role::new(1505));
|
||||
}
|
||||
|
||||
self.formation_list.clear();
|
||||
self.formation_list.push(RoleFormation {
|
||||
id: 1,
|
||||
cur_role: 1505,
|
||||
role_id_set: HashSet::from([1505]),
|
||||
is_current: true,
|
||||
});
|
||||
// End shorekeeper hardcode part
|
||||
|
||||
self.ensure_current_formation();
|
||||
self.ensure_basic_unlock_func();
|
||||
}
|
||||
|
||||
pub fn notify_general_data(&self) {
|
||||
self.notify(self.basic_info.build_notify());
|
||||
self.notify(self.func.build_func_open_notify());
|
||||
self.notify(self.build_role_list_notify());
|
||||
}
|
||||
self.notify(self.explore_tools.build_explore_tool_all_notify());
|
||||
self.notify(self.explore_tools.build_roulette_update_notify());
|
||||
|
||||
fn on_first_enter(&mut self) {
|
||||
self.role_list.push(Self::create_main_character_role(
|
||||
self.basic_info.name.clone(),
|
||||
self.basic_info.sex,
|
||||
));
|
||||
|
||||
let role = &self.role_list[0];
|
||||
|
||||
self.formation_list.push(RoleFormation {
|
||||
id: 1,
|
||||
cur_role: role.role_id,
|
||||
role_id_set: HashSet::from([role.role_id]),
|
||||
is_current: true,
|
||||
self.notify(ItemPkgOpenNotify {
|
||||
open_pkg: (0..8).collect(),
|
||||
});
|
||||
|
||||
self.location = PlayerLocation::default();
|
||||
self.notify(self.build_update_formation_notify());
|
||||
}
|
||||
|
||||
fn init_role_and_formation(&mut self) {
|
||||
self.role_list.clear();
|
||||
let mut role = match self.basic_info.sex {
|
||||
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
|
||||
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
role.name = self.basic_info.name.clone();
|
||||
|
||||
self.role_list.insert(role.role_id, role);
|
||||
|
||||
let required_role_ids: Vec<i32> = role_info_data::iter()
|
||||
.filter(|role_info| role_info.role_type == 1)
|
||||
.map(|role_info| role_info.id)
|
||||
.collect();
|
||||
let formation = vec![1603, 1504, 1505];
|
||||
|
||||
required_role_ids.iter().for_each(|&role_id| {
|
||||
if !self.role_list.keys().any(|&k| k == role_id) {
|
||||
self.role_list.insert(role_id, Role::new(role_id));
|
||||
}
|
||||
});
|
||||
|
||||
self.formation_list.insert(
|
||||
1,
|
||||
RoleFormation {
|
||||
id: 1,
|
||||
cur_role: *formation.iter().next().unwrap(),
|
||||
role_ids: formation,
|
||||
is_current: true,
|
||||
},
|
||||
);
|
||||
self.cur_formation_id = 1;
|
||||
|
||||
self.formation_list.values_mut().for_each(|formation| {
|
||||
if formation.is_current && formation.id != 1 {
|
||||
formation.is_current = false;
|
||||
}
|
||||
});
|
||||
|
||||
self.ensure_current_formation();
|
||||
}
|
||||
|
||||
// Ensure basic functionality is unlocked
|
||||
// Should be handled by quest progression,
|
||||
// but as of right now, just unlock what we need
|
||||
fn ensure_basic_unlock_func(&mut self) {
|
||||
self.func.unlock(10026); // explore tools
|
||||
}
|
||||
|
||||
fn ensure_current_formation(&mut self) {
|
||||
// If the list off formation is empty, add a default formation
|
||||
if self.formation_list.is_empty() {
|
||||
let role = &self.role_list[0];
|
||||
let mut role_list_clone = self.role_list.iter().clone();
|
||||
|
||||
self.formation_list.push(RoleFormation {
|
||||
id: 1,
|
||||
cur_role: role.role_id,
|
||||
role_id_set: HashSet::from([role.role_id]),
|
||||
is_current: true,
|
||||
});
|
||||
self.formation_list.insert(
|
||||
1,
|
||||
RoleFormation {
|
||||
id: 1,
|
||||
cur_role: role_list_clone.next().unwrap().1.role_id,
|
||||
role_ids: role_list_clone
|
||||
.take(3)
|
||||
.map(|(&role_id, _)| role_id)
|
||||
.collect(),
|
||||
is_current: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if !self.formation_list.iter().any(|rf| rf.is_current) {
|
||||
self.formation_list[0].is_current = true;
|
||||
// If there is no current formation, set the first formation as the current formation
|
||||
if !self.formation_list.values().any(|rf| rf.is_current) {
|
||||
self.formation_list.get_mut(&1).unwrap().is_current = true;
|
||||
}
|
||||
|
||||
if let Some(rf) = self.formation_list.iter_mut().find(|rf| rf.is_current) {
|
||||
if rf.role_id_set.is_empty() {
|
||||
rf.role_id_set.insert(self.role_list[0].role_id);
|
||||
// Ensure that the set of character IDs for the current formation is not empty and that the current character ID is in the set
|
||||
if let Some(rf) = self.formation_list.values_mut().find(|rf| rf.is_current) {
|
||||
if rf.role_ids.is_empty() {
|
||||
rf.role_ids
|
||||
.push(self.role_list.iter().next().unwrap().1.role_id);
|
||||
}
|
||||
|
||||
if !rf.role_id_set.contains(&rf.cur_role) {
|
||||
rf.cur_role = *rf.role_id_set.iter().nth(0).unwrap();
|
||||
if !rf.role_ids.contains(&rf.cur_role) {
|
||||
rf.cur_role = *rf.role_ids.iter().nth(0).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,24 +179,105 @@ impl Player {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_current_formation_role_list(&self) -> Vec<&Role> {
|
||||
self.formation_list
|
||||
.iter()
|
||||
.find(|rf| rf.is_current)
|
||||
.unwrap()
|
||||
.role_id_set
|
||||
.iter()
|
||||
.map(|id| self.role_list.iter().find(|r| r.role_id == *id))
|
||||
.flatten()
|
||||
.collect()
|
||||
pub fn build_player_entity_add_notify(
|
||||
&self,
|
||||
role_list: Vec<Role>,
|
||||
world: &mut WorldEntity,
|
||||
) -> EntityAddNotify {
|
||||
create_player_entity_pb!(
|
||||
role_list,
|
||||
self.basic_info.cur_map_id,
|
||||
world,
|
||||
self.basic_info.id,
|
||||
self.location.position.clone(),
|
||||
self.explore_tools
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_cur_role_id(&self) -> i32 {
|
||||
self.formation_list
|
||||
.iter()
|
||||
.find(|rf| rf.is_current)
|
||||
.unwrap()
|
||||
.cur_role
|
||||
pub fn build_player_entity_remove_notify(
|
||||
&self,
|
||||
entities: Vec<i64>,
|
||||
remove_type: ERemoveEntityType,
|
||||
) -> EntityRemoveNotify {
|
||||
EntityRemoveNotify {
|
||||
remove_infos: entities
|
||||
.iter()
|
||||
.map(|&entity_id| EntityRemoveInfo {
|
||||
entity_id,
|
||||
r#type: remove_type.into(),
|
||||
})
|
||||
.collect(),
|
||||
is_remove: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_update_group_formation_notify(
|
||||
&self,
|
||||
cur_formation: RoleFormation,
|
||||
world: &mut WorldEntity,
|
||||
) -> UpdateGroupFormationNotify {
|
||||
let group_type = 1;
|
||||
UpdateGroupFormationNotify {
|
||||
group_formation: vec![GroupFormation {
|
||||
player_id: self.basic_info.id,
|
||||
fight_role_infos: vec![FightRoleInfos {
|
||||
group_type,
|
||||
fight_role_infos: cur_formation
|
||||
.role_ids
|
||||
.iter()
|
||||
.map(|&role_id| FightRoleInfo {
|
||||
role_id,
|
||||
entity_id: world.get_entity_id(role_id),
|
||||
})
|
||||
.collect(),
|
||||
cur_role: cur_formation.cur_role,
|
||||
is_retain: false,
|
||||
living_status: LivingStatus::Alive.into(),
|
||||
is_fixed_location: false,
|
||||
}],
|
||||
current_group_type: group_type,
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_update_formation_notify(&self) -> UpdateFormationNotify {
|
||||
let role_map: HashMap<i32, (i32, i32)> = self
|
||||
.role_list
|
||||
.values()
|
||||
.map(|role| (role.role_id, (role.role_id, role.level)))
|
||||
.collect();
|
||||
|
||||
UpdateFormationNotify {
|
||||
players_formations: vec![PlayerFightFormations {
|
||||
player_id: self.basic_info.id,
|
||||
formations: self
|
||||
.formation_list
|
||||
.iter()
|
||||
.map(|(&formation_id, formation)| FightFormationNotifyInfo {
|
||||
formation_id,
|
||||
cur_role: formation.cur_role,
|
||||
role_infos: formation
|
||||
.role_ids
|
||||
.iter()
|
||||
.map(|role_id| {
|
||||
if !role_map.contains_key(role_id) {
|
||||
tracing::warn!("Role {} not found in use role list", role_id);
|
||||
return Default::default();
|
||||
}
|
||||
let &(role_id, level) = role_map.get(&role_id).unwrap();
|
||||
FormationRoleInfo {
|
||||
role_id,
|
||||
max_hp: 0,
|
||||
cur_hp: 0,
|
||||
level,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
is_current: formation.is_current,
|
||||
})
|
||||
.collect(),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_save(save_data: PlayerSaveData) -> Self {
|
||||
|
@ -144,17 +285,20 @@ impl Player {
|
|||
|
||||
Self {
|
||||
session: None,
|
||||
basic_info: PlayerBasicInfo::load_from_save(save_data.basic_data.unwrap_or_default()),
|
||||
basic_info: PlayerBasicInfo::load_from_save(
|
||||
save_data.basic_data.clone().unwrap_or_default(),
|
||||
),
|
||||
role_list: role_data
|
||||
.role_list
|
||||
.into_iter()
|
||||
.map(Role::load_from_save)
|
||||
.collect(),
|
||||
.collect::<HashMap<i32, Role>>(),
|
||||
formation_list: role_data
|
||||
.role_formation_list
|
||||
.into_iter()
|
||||
.map(RoleFormation::load_from_save)
|
||||
.map(|(k, v)| (k, RoleFormation::load_from_save(v)))
|
||||
.collect(),
|
||||
cur_formation_id: role_data.cur_formation_id,
|
||||
location: save_data
|
||||
.location_data
|
||||
.map(PlayerLocation::load_from_save)
|
||||
|
@ -163,8 +307,13 @@ impl Player {
|
|||
.func_data
|
||||
.map(PlayerFunc::load_from_save)
|
||||
.unwrap_or_default(),
|
||||
explore_tools: save_data
|
||||
.explore_tools_data
|
||||
.map(ExploreTools::load_from_save)
|
||||
.unwrap_or_default(),
|
||||
world: Rc::new(RefCell::new(World::new())),
|
||||
last_save_time: time_util::unix_timestamp(),
|
||||
quadrant_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,15 +321,21 @@ impl Player {
|
|||
PlayerSaveData {
|
||||
basic_data: Some(self.basic_info.build_save_data()),
|
||||
role_data: Some(PlayerRoleData {
|
||||
role_list: self.role_list.iter().map(|r| r.build_save_data()).collect(),
|
||||
role_list: self
|
||||
.role_list
|
||||
.iter()
|
||||
.map(|(_, role)| role.build_save_data())
|
||||
.collect(),
|
||||
role_formation_list: self
|
||||
.formation_list
|
||||
.iter()
|
||||
.map(|rf| rf.build_save_data())
|
||||
.map(|(&k, v)| (k, v.build_save_data()))
|
||||
.collect(),
|
||||
cur_formation_id: self.cur_formation_id,
|
||||
}),
|
||||
location_data: Some(self.location.build_save_data()),
|
||||
func_data: Some(self.func.build_save_data()),
|
||||
explore_tools_data: Some(self.explore_tools.build_save_data()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +345,11 @@ impl Player {
|
|||
|
||||
pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
|
||||
PbGetRoleListNotify {
|
||||
role_list: self.role_list.iter().map(|r| r.to_protobuf()).collect(),
|
||||
role_list: self
|
||||
.role_list
|
||||
.iter()
|
||||
.map(|(_, role)| role.to_protobuf())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,26 +374,23 @@ impl Player {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_main_character_role(name: String, sex: i32) -> Role {
|
||||
let mut role = match sex {
|
||||
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
|
||||
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
|
||||
_ => unreachable!(),
|
||||
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
|
||||
let role_id = match sex {
|
||||
0 => Role::MAIN_CHARACTER_FEMALE_ID, // 1502
|
||||
1 => Role::MAIN_CHARACTER_MALE_ID, // 1501
|
||||
_ => Role::MAIN_CHARACTER_MALE_ID, // Default to male
|
||||
};
|
||||
|
||||
role.name = name;
|
||||
role
|
||||
}
|
||||
|
||||
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
|
||||
PlayerSaveData {
|
||||
basic_data: Some(PlayerBasicData {
|
||||
id,
|
||||
name,
|
||||
sex,
|
||||
level: 1,
|
||||
head_photo: 1505,
|
||||
head_photo: 1603,
|
||||
head_frame: 80060009,
|
||||
cur_map_id: 8,
|
||||
role_show_list: vec![role_id],
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
|
|
|
@ -8,6 +8,10 @@ pub struct PlayerFunc {
|
|||
}
|
||||
|
||||
impl PlayerFunc {
|
||||
pub fn unlock(&mut self, id: i32) {
|
||||
self.func_map.insert(id, 2);
|
||||
}
|
||||
|
||||
pub fn load_from_save(data: PlayerFuncData) -> Self {
|
||||
PlayerFunc {
|
||||
func_map: data.func_map,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use shorekeeper_protocol::RoleFormationData;
|
||||
|
||||
pub struct RoleFormation {
|
||||
pub id: i32,
|
||||
pub cur_role: i32,
|
||||
pub role_id_set: HashSet<i32>,
|
||||
pub role_ids: Vec<i32>,
|
||||
pub is_current: bool,
|
||||
}
|
||||
|
||||
|
@ -14,7 +12,7 @@ impl RoleFormation {
|
|||
Self {
|
||||
id: data.formation_id,
|
||||
cur_role: data.cur_role,
|
||||
role_id_set: data.role_id_list.into_iter().collect(),
|
||||
role_ids: data.role_id_list,
|
||||
is_current: data.is_current,
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +21,7 @@ impl RoleFormation {
|
|||
RoleFormationData {
|
||||
formation_id: self.id,
|
||||
cur_role: self.cur_role,
|
||||
role_id_list: self.role_id_set.iter().cloned().collect(),
|
||||
role_id_list: self.role_ids.iter().map(|&role_id| role_id).collect(),
|
||||
is_current: self.is_current,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::logic::utils::load_role_info::load_key_value;
|
||||
use common::time_util;
|
||||
use shorekeeper_data::role_info_data;
|
||||
pub use formation::RoleFormation;
|
||||
use shorekeeper_data::{base_property_data, role_info_data};
|
||||
use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo};
|
||||
|
||||
mod formation;
|
||||
pub use formation::RoleFormation;
|
||||
|
||||
pub struct Role {
|
||||
pub role_id: i32,
|
||||
pub name: String,
|
||||
|
@ -17,6 +17,7 @@ pub struct Role {
|
|||
pub star: i32,
|
||||
pub favor: i32,
|
||||
pub create_time: u32,
|
||||
pub equip_weapon: i32,
|
||||
}
|
||||
|
||||
impl Role {
|
||||
|
@ -36,10 +37,17 @@ impl Role {
|
|||
star: 0,
|
||||
favor: 0,
|
||||
create_time: time_util::unix_timestamp() as u32,
|
||||
equip_weapon: data.init_weapon_item_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_protobuf(&self) -> RoleInfo {
|
||||
let base_prop: HashMap<i32, i32> = load_key_value(
|
||||
base_property_data::iter()
|
||||
.find(|d| d.id == self.role_id)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
RoleInfo {
|
||||
role_id: self.role_id,
|
||||
name: self.name.clone(),
|
||||
|
@ -54,22 +62,30 @@ impl Role {
|
|||
.collect(),
|
||||
star: self.star,
|
||||
favor: self.favor,
|
||||
base_prop: base_prop
|
||||
.iter()
|
||||
.map(|(&k, &v)| ArrayIntInt { key: k, value: v })
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_save(data: RoleData) -> Self {
|
||||
Self {
|
||||
role_id: data.role_id,
|
||||
name: data.name,
|
||||
level: data.level,
|
||||
exp: data.exp,
|
||||
breakthrough: data.breakthrough,
|
||||
skill_map: data.skill_map,
|
||||
star: data.star,
|
||||
favor: data.favor,
|
||||
create_time: data.create_time,
|
||||
}
|
||||
pub fn load_from_save(data: RoleData) -> (i32, Self) {
|
||||
(
|
||||
data.role_id,
|
||||
Self {
|
||||
role_id: data.role_id,
|
||||
name: data.name,
|
||||
level: data.level,
|
||||
exp: data.exp,
|
||||
breakthrough: data.breakthrough,
|
||||
skill_map: data.skill_map,
|
||||
star: data.star,
|
||||
favor: data.favor,
|
||||
create_time: data.create_time,
|
||||
equip_weapon: data.equip_weapon,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_save_data(&self) -> RoleData {
|
||||
|
@ -83,6 +99,7 @@ impl Role {
|
|||
star: self.star,
|
||||
favor: self.favor,
|
||||
create_time: self.create_time,
|
||||
equip_weapon: self.equip_weapon,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ pub(super) struct MovementSystem;
|
|||
impl System for MovementSystem {
|
||||
fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) {
|
||||
let mut notify = MovePackageNotify::default();
|
||||
let world_entity = world.get_world_entity();
|
||||
|
||||
for (entity, mut movement, mut position) in query_with!(world, Movement, Position) {
|
||||
for (entity, mut movement, mut position) in query_with!(world_entity, Movement, Position) {
|
||||
if movement.pending_movement_vec.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
@ -46,9 +47,12 @@ impl System for MovementSystem {
|
|||
|
||||
notify.moving_entities.push(moving_entity_data);
|
||||
|
||||
if let (Some(_), Some(owner)) =
|
||||
query_components!(world, i64::from(entity), PlayerEntityMarker, OwnerPlayer)
|
||||
{
|
||||
if let (Some(_), Some(owner)) = query_components!(
|
||||
world_entity,
|
||||
i64::from(entity),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer
|
||||
) {
|
||||
if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) {
|
||||
player.location.position = position.0.clone();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
use common::time_util;
|
||||
use shorekeeper_protocol::PlayerSaveData;
|
||||
use shorekeeper_protocol::{
|
||||
message::Message, AfterJoinSceneNotify, EnterGameResponse, JoinSceneNotify, JsPatchNotify,
|
||||
TransitionOptionPb,
|
||||
};
|
||||
use std::collections::hash_map::Entry::Vacant;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
|
@ -10,16 +17,13 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use common::time_util;
|
||||
use shorekeeper_protocol::{message::Message, JoinSceneNotify, TransitionOptionPb};
|
||||
use shorekeeper_protocol::{AfterJoinSceneNotify, EnterGameResponse, PlayerSaveData};
|
||||
|
||||
use crate::{
|
||||
player_save_task::{self, PlayerSaveReason},
|
||||
session::Session,
|
||||
};
|
||||
|
||||
use super::{ecs::world::World, player::Player, utils::world_util};
|
||||
use crate::logic::ecs::world::WorldEntity;
|
||||
use crate::{logic, player_save_task::{self, PlayerSaveReason}, session::Session};
|
||||
|
||||
const WATER_MASK: &str = include_str!("../../watermask-rr.js");
|
||||
const UID_FIX: &str = include_str!("../../uidfix.js");
|
||||
const CENSORSHIP_FIX: &str = include_str!("../../censorshipfix.js");
|
||||
|
||||
pub enum LogicInput {
|
||||
AddPlayer {
|
||||
|
@ -105,8 +109,7 @@ fn logic_thread_func(receiver: mpsc::Receiver<LogicInput>, load: Arc<AtomicUsize
|
|||
let mut world = world.borrow_mut();
|
||||
let mut players = world
|
||||
.player_ids()
|
||||
.map(|id| state.players.get(id).map(|pl| pl.borrow_mut()))
|
||||
.flatten()
|
||||
.flat_map(|id| state.players.get(id).map(|pl| pl.borrow_mut()))
|
||||
.collect::<Box<_>>();
|
||||
|
||||
super::systems::tick_systems(&mut world, &mut players);
|
||||
|
@ -135,16 +138,33 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
|||
session,
|
||||
player_save_data,
|
||||
} => {
|
||||
let player = state
|
||||
.players
|
||||
.entry(player_id)
|
||||
.or_insert(RefCell::new(Player::load_from_save(player_save_data)));
|
||||
let (player, is_player) = if let Vacant(e) = state.players.entry(player_id) {
|
||||
(
|
||||
e.insert(RefCell::new(Player::load_from_save(player_save_data))),
|
||||
true,
|
||||
)
|
||||
} else {
|
||||
if let Some(player) = state.players.get_mut(&player_id) {
|
||||
(player, false)
|
||||
} else {
|
||||
tracing::warn!("logic_thread: get player requested, but player {player_id} with data doesn't exist");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut player = player.borrow_mut();
|
||||
state.worlds.insert(player_id, player.world.clone());
|
||||
if is_player {
|
||||
player
|
||||
.world
|
||||
.borrow_mut()
|
||||
.world_entitys
|
||||
.insert(player.basic_info.cur_map_id, WorldEntity::default());
|
||||
state.worlds.insert(player_id, player.world.clone());
|
||||
}
|
||||
|
||||
player.init();
|
||||
player.set_session(session);
|
||||
player.respond(EnterGameResponse::default(), enter_rpc_id);
|
||||
player.notify_general_data();
|
||||
|
||||
player
|
||||
|
@ -152,21 +172,41 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
|||
.borrow_mut()
|
||||
.set_in_world_player_data(player.build_in_world_player());
|
||||
|
||||
world_util::add_player_entities(&mut player.world.borrow_mut(), &player);
|
||||
let scene_info = world_util::build_scene_information(
|
||||
&player.world.borrow(),
|
||||
player.location.instance_id,
|
||||
player.basic_info.id,
|
||||
);
|
||||
world_util::add_player_entities(&player);
|
||||
let scene_info = world_util::build_scene_information(&player);
|
||||
|
||||
player.notify(JoinSceneNotify {
|
||||
max_entity_id: i64::MAX,
|
||||
scene_info: Some(scene_info),
|
||||
max_entity_id: i64::MAX,
|
||||
transition_option: Some(TransitionOptionPb::default()),
|
||||
});
|
||||
|
||||
player.respond(EnterGameResponse::default(), enter_rpc_id);
|
||||
player.notify(AfterJoinSceneNotify::default());
|
||||
|
||||
// TODO: maybe move somewhere else?
|
||||
player.notify(JsPatchNotify {
|
||||
// TODO: Add the possibility to customize size and text from options
|
||||
content: WATER_MASK.to_string(),
|
||||
});
|
||||
player.notify(JsPatchNotify {
|
||||
content: UID_FIX
|
||||
.replace("{PLAYER_USERNAME}", &player.basic_info.name)
|
||||
.replace("{SELECTED_COLOR}", "50FC71"),
|
||||
});
|
||||
player.notify(JsPatchNotify {
|
||||
content: CENSORSHIP_FIX.to_string(),
|
||||
});
|
||||
|
||||
let map = logic::utils::quadrant_util::get_map(player.location.instance_id);
|
||||
let quadrant_id = map.get_quadrant_id(
|
||||
player.location.position.position.x * 100.0,
|
||||
player.location.position.position.y * 100.0,
|
||||
);
|
||||
player.quadrant_id = quadrant_id;
|
||||
|
||||
let entities = map.get_initial_entities(quadrant_id);
|
||||
world_util::add_entities(&player, &entities);
|
||||
|
||||
drop(player);
|
||||
|
||||
state
|
||||
|
@ -191,6 +231,7 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
|
|||
|
||||
let _ = state.worlds.remove(&player_id);
|
||||
// TODO: kick co-op players from removed world
|
||||
// TODO: Remove all entitie
|
||||
|
||||
player_save_task::push(
|
||||
player_id,
|
||||
|
|
|
@ -1,26 +1,75 @@
|
|||
use crate::logic::ecs::component::ComponentContainer;
|
||||
use shorekeeper_protocol::{EntityPb, PlayerSceneAoiData};
|
||||
use shorekeeper_protocol::{EEntityType, EntityPb, PlayerSceneAoiData};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{logic::ecs::world::World, query_with};
|
||||
use crate::logic::components::Visibility;
|
||||
use crate::logic::player::Player;
|
||||
use crate::{modify_component, query_hn_with};
|
||||
|
||||
pub fn build_scene_add_on_init_data(world: &World) -> PlayerSceneAoiData {
|
||||
let entities = query_with!(world, PlayerEntityMarker)
|
||||
pub fn build_scene_add_on_init_data(player: &Player) -> PlayerSceneAoiData {
|
||||
let mut world_ref = player.world.borrow_mut();
|
||||
let world = world_ref.get_mut_world_entity();
|
||||
|
||||
let entities = query_hn_with!(world, PlayerEntityMarker)
|
||||
.into_iter()
|
||||
.map(|(e, _)| e)
|
||||
.collect::<Vec<_>>();
|
||||
.map(|(entity_id, _)| {
|
||||
let res_map: (EEntityType, i32);
|
||||
match EEntityType::try_from(
|
||||
world.get_entity(world.get_config_id(entity_id)).entity_type,
|
||||
) {
|
||||
Ok(EEntityType::Player) => {
|
||||
res_map = (EEntityType::Player, entity_id);
|
||||
}
|
||||
_ => {
|
||||
res_map = (EEntityType::default(), -1);
|
||||
}
|
||||
}
|
||||
res_map
|
||||
})
|
||||
.collect::<HashSet<(EEntityType, i32)>>();
|
||||
|
||||
let mut aoi_data = PlayerSceneAoiData::default();
|
||||
for entity in entities {
|
||||
let mut pb = EntityPb::default();
|
||||
pb.id = entity.into();
|
||||
|
||||
world
|
||||
.get_entity_components(entity)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
entities
|
||||
.iter()
|
||||
.filter(|&&(_, entity_id)| entity_id != -1)
|
||||
.for_each(|&(entity_type, entity_id)| {
|
||||
match entity_type {
|
||||
EEntityType::Player => {
|
||||
let config_id = world.get_config_id(entity_id);
|
||||
modify_component!(
|
||||
world.get_entity_components(entity_id),
|
||||
Visibility,
|
||||
|vis: &mut Visibility| {
|
||||
let cur_role_id = player
|
||||
.formation_list
|
||||
.get(&player.cur_formation_id)
|
||||
.unwrap()
|
||||
.cur_role;
|
||||
vis.0 = if config_id == cur_role_id {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
aoi_data.entities.push(pb);
|
||||
}
|
||||
if world.get_entity(config_id).entity_type == EEntityType::Player as i32 {
|
||||
let mut pb = EntityPb {
|
||||
id: entity_id as i64,
|
||||
..Default::default()
|
||||
};
|
||||
world
|
||||
.get_entity_components(entity_id)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
|
||||
aoi_data.entities.push(pb);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
});
|
||||
|
||||
aoi_data
|
||||
}
|
||||
|
|
158
game-server/src/logic/utils/load_role_info.rs
Normal file
158
game-server/src/logic/utils/load_role_info.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use shorekeeper_data::BasePropertyData;
|
||||
use shorekeeper_protocol::EAttributeType;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_base_prop {
|
||||
($($name:ident),*) => {
|
||||
pub fn load_key_value(base_property: &BasePropertyData) -> HashMap<i32, i32> {
|
||||
HashMap::from([$(
|
||||
::paste::paste!((EAttributeType::[<$name:camel>] as i32, base_property.$name as i32)),
|
||||
)*])
|
||||
}
|
||||
pub fn attribute_from_data(base_property: &BasePropertyData) -> HashMap<EAttributeType, (i32, i32)> {
|
||||
HashMap::from([$(
|
||||
::paste::paste!((EAttributeType::[<$name:camel>], (base_property.$name, 0))),
|
||||
)*])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_base_prop!(
|
||||
lv,
|
||||
life_max,
|
||||
life,
|
||||
sheild,
|
||||
sheild_damage_change,
|
||||
sheild_damage_reduce,
|
||||
atk,
|
||||
crit,
|
||||
crit_damage,
|
||||
def,
|
||||
energy_efficiency,
|
||||
cd_reduse,
|
||||
element_efficiency,
|
||||
damage_change_normal_skill,
|
||||
damage_change,
|
||||
damage_reduce,
|
||||
damage_change_auto,
|
||||
damage_change_cast,
|
||||
damage_change_ultra,
|
||||
damage_change_qte,
|
||||
damage_change_phys,
|
||||
damage_change_element1,
|
||||
damage_change_element2,
|
||||
damage_change_element3,
|
||||
damage_change_element4,
|
||||
damage_change_element5,
|
||||
damage_change_element6,
|
||||
damage_resistance_phys,
|
||||
damage_resistance_element1,
|
||||
damage_resistance_element2,
|
||||
damage_resistance_element3,
|
||||
damage_resistance_element4,
|
||||
damage_resistance_element5,
|
||||
damage_resistance_element6,
|
||||
heal_change,
|
||||
healed_change,
|
||||
damage_reduce_phys,
|
||||
damage_reduce_element1,
|
||||
damage_reduce_element2,
|
||||
damage_reduce_element3,
|
||||
damage_reduce_element4,
|
||||
damage_reduce_element5,
|
||||
damage_reduce_element6,
|
||||
reaction_change1,
|
||||
reaction_change2,
|
||||
reaction_change3,
|
||||
reaction_change4,
|
||||
reaction_change5,
|
||||
reaction_change6,
|
||||
reaction_change7,
|
||||
reaction_change8,
|
||||
reaction_change9,
|
||||
reaction_change10,
|
||||
reaction_change11,
|
||||
reaction_change12,
|
||||
reaction_change13,
|
||||
reaction_change14,
|
||||
reaction_change15,
|
||||
energy_max,
|
||||
energy,
|
||||
special_energy_1_max,
|
||||
special_energy_1,
|
||||
special_energy_2_max,
|
||||
special_energy_2,
|
||||
special_energy_3_max,
|
||||
special_energy_3,
|
||||
special_energy_4_max,
|
||||
special_energy_4,
|
||||
strength_max,
|
||||
strength,
|
||||
strength_recover,
|
||||
strength_punish_time,
|
||||
strength_run,
|
||||
strength_swim,
|
||||
strength_fast_swim,
|
||||
hardness_max,
|
||||
hardness,
|
||||
hardness_recover,
|
||||
hardness_punish_time,
|
||||
hardness_change,
|
||||
hardness_reduce,
|
||||
rage_max,
|
||||
rage,
|
||||
rage_recover,
|
||||
rage_punish_time,
|
||||
rage_change,
|
||||
rage_reduce,
|
||||
tough_max,
|
||||
tough,
|
||||
tough_recover,
|
||||
tough_change,
|
||||
tough_reduce,
|
||||
tough_recover_delay_time,
|
||||
element_power1,
|
||||
element_power2,
|
||||
element_power3,
|
||||
element_power4,
|
||||
element_power5,
|
||||
element_power6,
|
||||
special_damage_change,
|
||||
strength_fast_climb_cost,
|
||||
element_property_type,
|
||||
weak_time,
|
||||
ignore_def_rate,
|
||||
ignore_damage_resistance_phys,
|
||||
ignore_damage_resistance_element1,
|
||||
ignore_damage_resistance_element2,
|
||||
ignore_damage_resistance_element3,
|
||||
ignore_damage_resistance_element4,
|
||||
ignore_damage_resistance_element5,
|
||||
ignore_damage_resistance_element6,
|
||||
skill_tough_ratio,
|
||||
strength_climb_jump,
|
||||
strength_gliding,
|
||||
mass,
|
||||
braking_friction_factor,
|
||||
gravity_scale,
|
||||
speed_ratio,
|
||||
damage_change_phantom,
|
||||
auto_attack_speed,
|
||||
cast_attack_speed,
|
||||
status_build_up_1_max,
|
||||
status_build_up_1,
|
||||
status_build_up_2_max,
|
||||
status_build_up_2,
|
||||
status_build_up_3_max,
|
||||
status_build_up_3,
|
||||
status_build_up_4_max,
|
||||
status_build_up_4,
|
||||
status_build_up_5_max,
|
||||
status_build_up_5,
|
||||
paralysis_time_max,
|
||||
paralysis_time,
|
||||
paralysis_time_recover,
|
||||
element_energy_max,
|
||||
element_energy
|
||||
);
|
|
@ -1,2 +1,4 @@
|
|||
pub mod entity_serializer;
|
||||
pub mod load_role_info;
|
||||
pub mod world_util;
|
||||
pub mod quadrant_util;
|
||||
|
|
230
game-server/src/logic/utils/quadrant_util.rs
Normal file
230
game-server/src/logic/utils/quadrant_util.rs
Normal file
|
@ -0,0 +1,230 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use shorekeeper_data::LevelEntityConfigData;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MapBounds {
|
||||
x_max: f32,
|
||||
x_min: f32,
|
||||
x_translate: f32,
|
||||
y_max: f32,
|
||||
y_min: f32,
|
||||
y_translate: f32,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Quadrant {
|
||||
entities: HashMap<i64, &'static LevelEntityConfigData>,
|
||||
}
|
||||
|
||||
pub struct Map {
|
||||
bounds: MapBounds,
|
||||
width: u64,
|
||||
height: u64,
|
||||
quadrants: HashMap<u64, Quadrant>,
|
||||
}
|
||||
|
||||
// TODO: Make it configurable?
|
||||
const EDGE_SIZE: f32 = 1000000f32;
|
||||
const EDGE_CHECK: f32 = EDGE_SIZE * 3.0f32;
|
||||
|
||||
pub(crate) static MAP_TABLE: OnceLock<HashMap<i32, Map>> = OnceLock::new();
|
||||
|
||||
impl MapBounds {
|
||||
fn find_max_min(slice: &[&LevelEntityConfigData]) -> (Self, bool) {
|
||||
let mut x_max = 0f32;
|
||||
let mut x_min = 0f32;
|
||||
|
||||
let mut y_max = 0f32;
|
||||
let mut y_min = 0f32;
|
||||
|
||||
// Find max and min coordinates
|
||||
for entity in slice.iter() {
|
||||
if entity.transform[0].x < x_min { x_min = entity.transform[0].x }
|
||||
if entity.transform[0].x > x_max { x_max = entity.transform[0].x }
|
||||
|
||||
if entity.transform[0].y < y_min { y_min = entity.transform[0].y }
|
||||
if entity.transform[0].y > y_max { y_max = entity.transform[0].y }
|
||||
}
|
||||
|
||||
if (f32::abs(x_max - x_min) < EDGE_CHECK) || (f32::abs(y_max - y_min) < EDGE_CHECK) {
|
||||
// TODO: Handle this special case, since all entities fit, no need for quadrant
|
||||
|
||||
// Move everything to positive coordinates to prevent corner cases
|
||||
let (x_max, x_min, x_translate) = recenter_map(x_max, x_min);
|
||||
let (y_max, y_min, y_translate) = recenter_map(y_max, y_min);
|
||||
|
||||
(MapBounds { x_max, x_min, x_translate, y_max, y_min, y_translate }, false)
|
||||
} else {
|
||||
// Round to edge
|
||||
x_max = round_max_coordinate(x_max, EDGE_SIZE);
|
||||
x_min = round_min_coordinate(x_min, EDGE_SIZE);
|
||||
y_max = round_max_coordinate(y_max, EDGE_SIZE);
|
||||
y_min = round_min_coordinate(y_min, EDGE_SIZE);
|
||||
|
||||
// Adding bounds to prevent OOB when moving
|
||||
x_max += EDGE_SIZE;
|
||||
x_min -= EDGE_SIZE;
|
||||
y_max += EDGE_SIZE;
|
||||
y_min -= EDGE_SIZE;
|
||||
|
||||
// Move everything to positive coordinates to prevent corner cases
|
||||
let (x_max, x_min, x_translate) = recenter_map(x_max, x_min);
|
||||
let (y_max, y_min, y_translate) = recenter_map(y_max, y_min);
|
||||
|
||||
(MapBounds { x_max, x_min, x_translate, y_max, y_min, y_translate }, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Quadrant {
|
||||
fn insert_entity(&mut self, entity_id: i64, entity: &'static LevelEntityConfigData) {
|
||||
self.entities.insert(entity_id, entity);
|
||||
}
|
||||
|
||||
fn get_entities(&self) -> Vec<&LevelEntityConfigData> {
|
||||
self.entities
|
||||
.iter()
|
||||
.map(|(_, v)| *v)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn insert_entity(&mut self, entity: &'static LevelEntityConfigData) {
|
||||
let index = self.get_quadrant_id(entity.transform[0].x, entity.transform[0].y);
|
||||
self.quadrants.entry(index).or_default().insert_entity(entity.entity_id, entity)
|
||||
}
|
||||
|
||||
fn get_neighbour_cells(&self, quadrant_id: u64) -> [u64; 9] {
|
||||
let x = quadrant_id % self.width;
|
||||
let y = (quadrant_id - x) / self.width;
|
||||
return [
|
||||
(self.width * (y - 1)) + (x - 1),
|
||||
(self.width * (y - 1)) + (x),
|
||||
(self.width * (y - 1)) + (x + 1),
|
||||
(self.width * (y)) + (x - 1),
|
||||
(self.width * (y)) + (x),
|
||||
(self.width * (y)) + (x + 1),
|
||||
(self.width * (y + 1)) + (x - 1),
|
||||
(self.width * (y + 1)) + (x),
|
||||
(self.width * (y + 1)) + (x + 1),
|
||||
];
|
||||
}
|
||||
|
||||
fn collect_quadrant_differences(&self, discriminant: [u64; 9], discriminator: [u64; 9]) -> Vec<&LevelEntityConfigData> {
|
||||
let mut output = Vec::new();
|
||||
for quadrant in discriminant {
|
||||
if !discriminator.contains(&quadrant) {
|
||||
if let Some(quadrant) = &self.quadrants.get(&quadrant) {
|
||||
output.extend_from_slice(&quadrant.get_entities())
|
||||
}
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
pub fn get_quadrant_id(&self, x: f32, y: f32) -> u64 {
|
||||
let width: u64 = unsafe {
|
||||
f32::to_int_unchecked(
|
||||
f32::trunc(
|
||||
(self.bounds.x_max + self.bounds.x_translate - x) / EDGE_SIZE
|
||||
)
|
||||
)
|
||||
};
|
||||
let height: u64 = unsafe {
|
||||
f32::to_int_unchecked(
|
||||
f32::trunc(
|
||||
(self.bounds.y_max + self.bounds.y_translate - y) / EDGE_SIZE
|
||||
)
|
||||
)
|
||||
};
|
||||
(self.width * height) + width
|
||||
}
|
||||
|
||||
pub fn get_initial_entities(&self, quadrant_id: u64) -> Vec<&LevelEntityConfigData> {
|
||||
let quadrants = self.get_neighbour_cells(quadrant_id);
|
||||
let mut output = Vec::new();
|
||||
for quadrant in quadrants {
|
||||
if let Some(quadrant) = &self.quadrants.get(&quadrant) {
|
||||
output.extend_from_slice(&quadrant.get_entities())
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
pub fn get_update_entities(&self, old_quadrant_id: u64, new_quadrant_id: u64) -> (Vec<&LevelEntityConfigData>, Vec<&LevelEntityConfigData>) {
|
||||
let old_quadrants = self.get_neighbour_cells(old_quadrant_id);
|
||||
let new_quadrants = self.get_neighbour_cells(new_quadrant_id);
|
||||
|
||||
let entities_to_remove = self.collect_quadrant_differences(old_quadrants, new_quadrants);
|
||||
let entities_to_add = self.collect_quadrant_differences(new_quadrants, old_quadrants);
|
||||
|
||||
(entities_to_remove, entities_to_add)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maps_iter() -> std::collections::hash_map::Iter<'static, i32, Map> {
|
||||
MAP_TABLE.get().unwrap().iter()
|
||||
}
|
||||
|
||||
pub fn initialize_quadrant_system() {
|
||||
let mut map_grouped_entities: HashMap<i32, Vec<&LevelEntityConfigData>> = HashMap::new();
|
||||
for (_, entity) in shorekeeper_data::level_entity_config_data::iter() {
|
||||
map_grouped_entities.entry(entity.map_id).or_default().push(entity);
|
||||
}
|
||||
|
||||
let mut maps: HashMap<i32, Map> = HashMap::new();
|
||||
for (map_id, entities) in map_grouped_entities {
|
||||
let (bounds, _quadrant_enabled) = MapBounds::find_max_min(&entities[..]);
|
||||
let width = unsafe { f32::to_int_unchecked((bounds.x_max - bounds.x_min) / EDGE_SIZE) };
|
||||
let height = unsafe { f32::to_int_unchecked((bounds.y_max - bounds.y_min) / EDGE_SIZE) };
|
||||
let map = maps.entry(map_id).or_insert(
|
||||
Map {
|
||||
bounds: bounds.clone(),
|
||||
width,
|
||||
height,
|
||||
quadrants: HashMap::new(),
|
||||
}
|
||||
);
|
||||
|
||||
for entity in entities {
|
||||
map.insert_entity(entity);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = MAP_TABLE.set(maps);
|
||||
}
|
||||
|
||||
pub fn get_map(map_id: i32) -> &'static Map {
|
||||
// TODO: Error check for map id
|
||||
MAP_TABLE.get().unwrap().get(&map_id).unwrap()
|
||||
}
|
||||
|
||||
fn recenter_map(max: f32, min: f32) -> (f32, f32, f32) {
|
||||
match min < 0.0 {
|
||||
true => (max + f32::abs(min), 0.0, min),
|
||||
false => (max, min, 0.0)
|
||||
}
|
||||
}
|
||||
|
||||
fn round_max_coordinate(coordinate: f32, round: f32) -> f32 {
|
||||
let rounded = f32::round(coordinate);
|
||||
let remainder = rounded % round;
|
||||
if remainder != 0f32 {
|
||||
rounded + (if rounded > 0.0 { round } else { 0.0 } - remainder)
|
||||
} else {
|
||||
rounded
|
||||
}
|
||||
}
|
||||
|
||||
fn round_min_coordinate(coordinate: f32, round: f32) -> f32 {
|
||||
let rounded = f32::round(coordinate);
|
||||
let remainder = rounded % round;
|
||||
if remainder != 0f32 {
|
||||
rounded + (remainder.signum() * (if rounded > 0.0 { 0.0 } else { round } - f32::abs(remainder)))
|
||||
} else {
|
||||
rounded
|
||||
}
|
||||
}
|
|
@ -1,76 +1,161 @@
|
|||
use shorekeeper_data::base_property_data;
|
||||
use shorekeeper_protocol::{
|
||||
EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
|
||||
ScenePlayerInformation, SceneTimeInfo,
|
||||
};
|
||||
use std::cell::{BorrowMutError, RefMut};
|
||||
|
||||
use crate::{
|
||||
logic::{
|
||||
components::{
|
||||
Attribute, EntityConfig, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
||||
Visibility,
|
||||
},
|
||||
ecs::{component::ComponentContainer, world::World},
|
||||
player::Player,
|
||||
use shorekeeper_data::{base_property_data, LevelEntityConfigData};
|
||||
use shorekeeper_protocol::{DFsm, EEntityType, EntityAddNotify, EntityConfigType, EntityPb,
|
||||
EntityRemoveInfo, EntityRemoveNotify, EntityState, FightRoleInfo,
|
||||
FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
|
||||
ScenePlayerInformation, SceneTimeInfo};
|
||||
|
||||
use crate::logic::{
|
||||
components::{
|
||||
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
|
||||
Visibility, VisionSkill,
|
||||
},
|
||||
query_with,
|
||||
ecs::component::ComponentContainer,
|
||||
};
|
||||
use crate::logic::components::{Fsm, MonsterAi};
|
||||
use crate::logic::ecs::entity::Entity;
|
||||
use crate::logic::ecs::world::{World, WorldEntity};
|
||||
use crate::logic::math::Transform;
|
||||
use crate::logic::player::Player;
|
||||
use crate::logic::utils::entity_serializer;
|
||||
use crate::query_with;
|
||||
|
||||
use super::entity_serializer;
|
||||
#[macro_export]
|
||||
macro_rules! create_player_entity_pb {
|
||||
($role_list:expr, $cur_map_id:expr, $world:expr, $player_id:expr, $position:expr, $explore_tools:expr) => {{
|
||||
let mut pbs = Vec::new();
|
||||
|
||||
pub fn add_player_entities(world: &mut World, player: &Player) {
|
||||
let cur_role_id = player.get_cur_role_id();
|
||||
for role in $role_list {
|
||||
let role_id: i32 = role.role_id;
|
||||
let base_property = base_property_data::iter()
|
||||
.find(|d| d.id == role_id)
|
||||
.expect("macro create_role_entity_pb: Base property data not found");
|
||||
|
||||
for role in player.get_current_formation_role_list() {
|
||||
let id = world
|
||||
.create_entity()
|
||||
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||
config_id: role.role_id,
|
||||
config_type: EntityConfigType::Character,
|
||||
}))
|
||||
.with(ComponentContainer::OwnerPlayer(OwnerPlayer(
|
||||
player.basic_info.id,
|
||||
)))
|
||||
.with(ComponentContainer::Position(Position(
|
||||
player.location.position.clone(),
|
||||
)))
|
||||
.with(ComponentContainer::Visibility(Visibility(
|
||||
role.role_id == cur_role_id,
|
||||
)))
|
||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||
base_property_data::iter()
|
||||
.find(|d| d.id == role.role_id)
|
||||
.unwrap(),
|
||||
)))
|
||||
.with(ComponentContainer::Movement(Movement::default()))
|
||||
.build();
|
||||
let entity = $world
|
||||
.create_entity(role_id, EEntityType::Player.into(), $cur_map_id)
|
||||
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||
config_id: role_id,
|
||||
config_type: EntityConfigType::Character,
|
||||
entity_type: EEntityType::Player.into(),
|
||||
entity_state: EntityState::Default
|
||||
}))
|
||||
.with(ComponentContainer::OwnerPlayer(OwnerPlayer($player_id)))
|
||||
.with(ComponentContainer::Position(Position($position)))
|
||||
.with(ComponentContainer::Visibility(Visibility(
|
||||
role_id == role_id,
|
||||
)))
|
||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||
base_property,
|
||||
)))
|
||||
.with(ComponentContainer::Movement(Movement::default()))
|
||||
.with(ComponentContainer::Equip(Equip {
|
||||
weapon_id: role.equip_weapon,
|
||||
weapon_breach_level: 90, // TODO: store this too
|
||||
}))
|
||||
.with(ComponentContainer::VisionSkill(VisionSkill {
|
||||
skill_id: $explore_tools.active_explore_skill,
|
||||
}))
|
||||
.build();
|
||||
|
||||
tracing::debug!(
|
||||
"created player entity, id: {}, role_id: {}",
|
||||
i64::from(id),
|
||||
role.role_id
|
||||
);
|
||||
let mut pb = EntityPb {
|
||||
id: entity.entity_id as i64,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
$world
|
||||
.get_entity_components(entity.entity_id)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
pbs.push(pb);
|
||||
}
|
||||
|
||||
EntityAddNotify {
|
||||
entity_pbs: pbs,
|
||||
is_add: true,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn add_player_entities(player: &Player) {
|
||||
let mut world_ref = player.world.borrow_mut();
|
||||
let world = world_ref.get_mut_world_entity();
|
||||
|
||||
let current_formation = player.formation_list.get(&player.cur_formation_id).unwrap();
|
||||
|
||||
let role_vec = current_formation
|
||||
.role_ids
|
||||
.iter()
|
||||
.map(|role_id| player.role_list.get(&role_id).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let cur_role_id = current_formation.cur_role;
|
||||
|
||||
if world.active_entity_empty() {
|
||||
for role in role_vec {
|
||||
let entity = world
|
||||
.create_entity(
|
||||
role.role_id,
|
||||
EEntityType::Player.into(),
|
||||
player.basic_info.cur_map_id,
|
||||
)
|
||||
.with(ComponentContainer::PlayerEntityMarker(PlayerEntityMarker))
|
||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||
config_id: role.role_id,
|
||||
config_type: EntityConfigType::Character,
|
||||
entity_type: EEntityType::Player.into(),
|
||||
entity_state: EntityState::Default,
|
||||
}))
|
||||
.with(ComponentContainer::OwnerPlayer(OwnerPlayer(
|
||||
player.basic_info.id,
|
||||
)))
|
||||
.with(ComponentContainer::Position(Position(
|
||||
player.location.position.clone(),
|
||||
)))
|
||||
.with(ComponentContainer::Visibility(Visibility(
|
||||
role.role_id == cur_role_id,
|
||||
)))
|
||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||
base_property_data::iter()
|
||||
.find(|d| d.id == role.role_id)
|
||||
.unwrap(),
|
||||
)))
|
||||
.with(ComponentContainer::Movement(Movement::default()))
|
||||
.with(ComponentContainer::Equip(Equip {
|
||||
weapon_id: role.equip_weapon,
|
||||
weapon_breach_level: 0, // TODO: store this too
|
||||
}))
|
||||
.with(ComponentContainer::VisionSkill(VisionSkill {
|
||||
skill_id: player.explore_tools.active_explore_skill,
|
||||
}))
|
||||
.build();
|
||||
|
||||
tracing::debug!(
|
||||
"created player entity, id: {}, role_id: {}",
|
||||
entity.entity_id,
|
||||
role.role_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_scene_information(world: &World, instance_id: i32, owner_id: i32) -> SceneInformation {
|
||||
pub fn build_scene_information(player: &Player) -> SceneInformation {
|
||||
SceneInformation {
|
||||
scene_id: String::new(),
|
||||
instance_id,
|
||||
owner_id,
|
||||
instance_id: player.location.instance_id,
|
||||
owner_id: player.basic_info.id,
|
||||
dynamic_entity_list: Vec::new(),
|
||||
blackboard_params: Vec::new(),
|
||||
end_time: 0,
|
||||
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(world)),
|
||||
player_infos: build_player_info_list(world),
|
||||
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(player)),
|
||||
player_infos: build_player_info_list(&player.world.borrow_mut()),
|
||||
mode: SceneMode::Single.into(),
|
||||
time_info: Some(SceneTimeInfo {
|
||||
owner_time_clock_time_span: 0,
|
||||
hour: 8,
|
||||
minute: 0,
|
||||
}),
|
||||
cur_context_id: owner_id as i64,
|
||||
cur_context_id: player.basic_info.id as i64,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
@ -79,24 +164,33 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
|
|||
world
|
||||
.players()
|
||||
.map(|sp| {
|
||||
let (cur_role_id, transform) = query_with!(
|
||||
world,
|
||||
let (cur_role_id, transform, _equip) = query_with!(
|
||||
world.get_world_entity(),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer,
|
||||
Visibility,
|
||||
EntityConfig,
|
||||
Position
|
||||
Position,
|
||||
Equip
|
||||
)
|
||||
.into_iter()
|
||||
.find_map(|(_, _, owner, visibility, conf, pos)| {
|
||||
(sp.player_id == owner.0 && visibility.0).then_some((conf.config_id, pos.0.clone()))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
.into_iter()
|
||||
.find_map(|(_, _, owner, visibility, conf, pos, equip)| {
|
||||
(sp.player_id == owner.0 && visibility.0).then_some((
|
||||
conf.config_id,
|
||||
pos.0.clone(),
|
||||
equip.weapon_id,
|
||||
))
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let active_characters =
|
||||
query_with!(world, PlayerEntityMarker, OwnerPlayer, EntityConfig)
|
||||
.into_iter()
|
||||
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
|
||||
let active_characters = query_with!(
|
||||
world.get_world_entity(),
|
||||
PlayerEntityMarker,
|
||||
OwnerPlayer,
|
||||
EntityConfig
|
||||
)
|
||||
.into_iter()
|
||||
.filter(|(_, _, owner, _)| owner.0 == sp.player_id);
|
||||
|
||||
ScenePlayerInformation {
|
||||
cur_role: cur_role_id,
|
||||
|
@ -125,3 +219,119 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn build_monster_entity(world: &mut WorldEntity, config_id: i32, map_id: i32, transform: Transform) -> Entity {
|
||||
// TODO: Check for more components, AI and so
|
||||
world.create_entity(config_id, EEntityType::Monster.into(), map_id)
|
||||
.with(ComponentContainer::EntityConfig(EntityConfig {
|
||||
config_id,
|
||||
config_type: EntityConfigType::Level,
|
||||
entity_type: EEntityType::Monster.into(),
|
||||
entity_state: EntityState::Born,
|
||||
}))
|
||||
.with(ComponentContainer::Position(Position(transform)))
|
||||
.with(ComponentContainer::Visibility(Visibility(true)))
|
||||
.with(ComponentContainer::Attribute(Attribute::from_data(
|
||||
base_property_data::iter()
|
||||
.find(|d| d.id == 600000100) // TODO: Implement monster stats
|
||||
.unwrap(),
|
||||
)))
|
||||
.with(ComponentContainer::MonsterAi(MonsterAi {
|
||||
weapon_id: 0,
|
||||
hatred_group_id: 0,
|
||||
ai_team_init_id: 100,
|
||||
combat_message_id: 0,
|
||||
}))
|
||||
.with(ComponentContainer::Fsm(Fsm {
|
||||
fsms: vec![
|
||||
DFsm {
|
||||
fsm_id: 10007,
|
||||
current_state: 10013,
|
||||
flag: 0,
|
||||
k_ts: 0,
|
||||
},
|
||||
DFsm {
|
||||
fsm_id: 10007,
|
||||
current_state: 10015,
|
||||
flag: 0,
|
||||
k_ts: 0,
|
||||
},
|
||||
DFsm {
|
||||
fsm_id: 10007,
|
||||
current_state: 10012,
|
||||
flag: 0,
|
||||
k_ts: 0,
|
||||
},
|
||||
],
|
||||
hash_code: 0,
|
||||
common_hash_code: 0,
|
||||
black_board: vec![],
|
||||
fsm_custom_blackboard_datas: None,
|
||||
}))
|
||||
.with(ComponentContainer::Movement(Movement::default()))
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn remove_entities(player: &Player, entities: &[&LevelEntityConfigData]) {
|
||||
let mut removed_entities = Vec::with_capacity(entities.len());
|
||||
// Enclose to drop borrow mut ASAP
|
||||
{
|
||||
let mut world_ref = player.world.borrow_mut();
|
||||
let world = world_ref.get_mut_world_entity();
|
||||
|
||||
for entity in entities {
|
||||
let entity_id = entity.entity_id as i32; // TODO: Should be i64
|
||||
if world.remove_entity(entity_id) {
|
||||
removed_entities.push(world.get_entity_id(entity_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
for entity_id in removed_entities {
|
||||
player.notify(EntityRemoveNotify {
|
||||
remove_infos: vec![EntityRemoveInfo { entity_id, r#type: 0 }],
|
||||
is_remove: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_entities(player: &Player, entities: &[&LevelEntityConfigData]) {
|
||||
let mut added_entities = Vec::with_capacity(entities.len());
|
||||
// Enclose to drop borrow mut ASAP
|
||||
{
|
||||
let mut world_ref = player.world.borrow_mut();
|
||||
let world = world_ref.get_mut_world_entity();
|
||||
|
||||
for entity in entities {
|
||||
// TODO: review other types
|
||||
if entity.blueprint_type.contains("Monster") {
|
||||
added_entities.push(build_monster_entity(
|
||||
world,
|
||||
entity.entity_id as i32, // TODO: Should be i64
|
||||
entity.map_id,
|
||||
Transform::from(&entity.transform[..]),
|
||||
));
|
||||
} else {
|
||||
tracing::debug!("Unhandled entity to be added of type: {}", entity.blueprint_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut world_ref = player.world.borrow();
|
||||
let world = world_ref.get_world_entity();
|
||||
// Since kuro has issues, we can only send one
|
||||
for entity in added_entities {
|
||||
let mut pb = EntityPb {
|
||||
id: entity.entity_id as i64, // TODO: Should be i64
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
world.get_entity_components(entity.entity_id)
|
||||
.into_iter()
|
||||
.for_each(|comp| comp.set_pb_data(&mut pb));
|
||||
|
||||
player.notify(EntityAddNotify {
|
||||
entity_pbs: vec![pb],
|
||||
is_add: true,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -20,7 +20,8 @@ async fn main() -> Result<()> {
|
|||
|
||||
::common::splash::print_splash();
|
||||
::common::logging::init(::tracing::Level::DEBUG);
|
||||
shorekeeper_data::load_json_data("assets/logic/json")?;
|
||||
shorekeeper_data::load_all_json_data("assets/logic/BinData")?;
|
||||
logic::utils::quadrant_util::initialize_quadrant_system();
|
||||
|
||||
let database = Arc::new(shorekeeper_database::connect_to(&CONFIG.database).await?);
|
||||
shorekeeper_database::run_migrations(database.as_ref()).await?;
|
||||
|
|
9
game-server/uidfix.js
Normal file
9
game-server/uidfix.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
setTimeout(() => {
|
||||
const UiManager_1 = require("../Ui/UiManager");
|
||||
const UE = require("ue");
|
||||
const ControllerManagerBase_1 = require("../../Core/Framework/ControllerManagerBase");
|
||||
|
||||
const UiText = UiManager_1.UiManager.GetViewByName("UidView").GetText(0);
|
||||
UiText.SetText("{PLAYER_USERNAME} - Reversed Rooms");
|
||||
UiText.SetColor(UE. Color.FromHex("{SELECTED_COLOR}"));
|
||||
}, 10000);
|
41
game-server/watermask-rr.js
Normal file
41
game-server/watermask-rr.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
const UE = require("ue"),
|
||||
Info_1 = require("../../../Core/Common/Info"),
|
||||
MathUtils_1 = require("../../../Core/Utils/MathUtils"),
|
||||
EventDefine_1 = require("../../Common/Event/EventDefine"),
|
||||
EventSystem_1 = require("../../Common/Event/EventSystem"),
|
||||
UiControllerBase_1 = require("../../Ui/Base/UiControllerBase"),
|
||||
UiLayerType_1 = require("../../Ui/Define/UiLayerType"),
|
||||
UiLayer_1 = require("../../Ui/UiLayer");
|
||||
|
||||
var _a = require('../Module/WaterMask/WaterMaskController').WaterMaskView;
|
||||
_a.LOo = 0.15;
|
||||
_a.yOo = 700;
|
||||
_a.IOo = 700;
|
||||
_a.vOo = function () {
|
||||
void 0 !== _a.SOo && _a.EOo();
|
||||
var e = UiLayer_1.UiLayer.GetLayerRootUiItem(UiLayerType_1.ELayerType.WaterMask),
|
||||
t = (_a.SOo = UE.KuroActorManager.SpawnActor(Info_1.Info.World, UE.UIContainerActor.StaticClass(),
|
||||
MathUtils_1.MathUtils.DefaultTransform, void 0), _a.SOo.RootComponent),
|
||||
e = (t.SetDisplayName("WaterMaskContainer"), UE.KuroStaticLibrary.SetActorPermanent(_a.SOo, !0, !0), _a.SOo
|
||||
.K2_AttachRootComponentTo(e), t.GetRootCanvas().GetOwner().RootComponent),
|
||||
i = e.widget.width % _a.yOo / 2,
|
||||
r = e.widget.height % _a.IOo / 2,
|
||||
n = e.widget.width / 2,
|
||||
_ = e.widget.height / 2,
|
||||
s = Math.ceil(e.widget.width / _a.yOo),
|
||||
o = Math.ceil(e.widget.height / _a.IOo),
|
||||
v = "NCSO @ discord.gg/reversedrooms";
|
||||
for (let a = 0; a < s; a++)
|
||||
for (let e = 0; e < o; e++) {
|
||||
var E = UE.KuroActorManager.SpawnActor(Info_1.Info.World, UE.UITextActor.StaticClass(), MathUtils_1
|
||||
.MathUtils.DefaultTransform, void 0),
|
||||
U = E.RootComponent,
|
||||
U = (E.K2_AttachRootComponentTo(t), U.SetDisplayName("WaterMaskText"), E.GetComponentByClass(UE
|
||||
.UIText.StaticClass()));
|
||||
U.SetFontSize(_a.vFt), U.SetOverflowType(0), U.SetAlpha(_a.LOo), U.SetFont(UE.LGUIFontData
|
||||
.GetDefaultFont()), U.SetText(v), U.SetUIRelativeLocation(new UE.Vector(a * _a.yOo - n + i, e *
|
||||
_a.IOo - _ + r, 0)), U.SetUIRelativeRotation(new UE.Rotator(0, _a.TOo, 0)), UE.KuroStaticLibrary
|
||||
.SetActorPermanent(E, !0, !0)
|
||||
}
|
||||
};
|
||||
_a.vOo();
|
|
@ -867,7 +867,7 @@ impl<Output> Kcp<Output> {
|
|||
}
|
||||
|
||||
let mut ts_flush = self.ts_flush;
|
||||
let mut tm_packet = u32::max_value();
|
||||
let mut tm_packet = u32::MAX;
|
||||
|
||||
if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 {
|
||||
ts_flush = current;
|
||||
|
@ -1142,7 +1142,7 @@ impl<Output: Write> Kcp<Output> {
|
|||
let resent = if self.fastresend > 0 {
|
||||
self.fastresend
|
||||
} else {
|
||||
u32::max_value()
|
||||
u32::MAX
|
||||
};
|
||||
|
||||
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 };
|
||||
|
@ -1381,7 +1381,7 @@ impl<Output: AsyncWrite + Unpin + Send> Kcp<Output> {
|
|||
let resent = if self.fastresend > 0 {
|
||||
self.fastresend
|
||||
} else {
|
||||
u32::max_value()
|
||||
u32::MAX
|
||||
};
|
||||
|
||||
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 };
|
||||
|
|
|
@ -86,7 +86,7 @@ async fn on_login_request(
|
|||
.flatten()
|
||||
else {
|
||||
tracing::debug!("login: account '{}' not found", &request.account);
|
||||
response.code = ErrorCode::InvalidUserId.into();
|
||||
response.error_code = ErrorCode::InvalidUserId.into();
|
||||
return;
|
||||
};
|
||||
|
||||
|
@ -100,7 +100,7 @@ async fn on_login_request(
|
|||
&request.login_trace_id,
|
||||
&account.user_id
|
||||
);
|
||||
response.code = ErrorCode::LoginRetry.into();
|
||||
response.error_code = ErrorCode::LoginRetry.into();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ async fn on_login_request(
|
|||
ban_time_stamp,
|
||||
ban_time_stamp - cur_time_stamp
|
||||
);
|
||||
response.code = ErrorCode::AccountIsBlocked.into();
|
||||
response.error_code = ErrorCode::AccountIsBlocked.into();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -134,13 +134,13 @@ async fn on_login_request(
|
|||
"login: first login on account {}, awaiting create character request",
|
||||
&account.user_id
|
||||
);
|
||||
response.code = ErrorCode::HaveNoCharacter.into();
|
||||
response.error_code = ErrorCode::HaveNoCharacter.into();
|
||||
return;
|
||||
};
|
||||
|
||||
session.player_id = Some(player_id);
|
||||
response.code = ErrorCode::Success.into();
|
||||
response.timestamp = time_util::unix_timestamp() as i64;
|
||||
response.error_code = ErrorCode::Success.into();
|
||||
response.timestamp = time_util::unix_timestamp_ms() as i64;
|
||||
|
||||
tracing::info!(
|
||||
"login success, user_id: {}, player_id: {}",
|
||||
|
|
|
@ -155,11 +155,11 @@ impl Session {
|
|||
|
||||
fn next_message(&mut self) -> Option<Message> {
|
||||
self.decoder.pop_with(|buf| {
|
||||
Message::decode(&buf)
|
||||
Message::decode(buf)
|
||||
.inspect_err(|err| {
|
||||
tracing::error!(
|
||||
"failed to decode a message, err: {err}, buf: {}",
|
||||
hex::encode(&buf)
|
||||
hex::encode(buf)
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
|
|
|
@ -15,8 +15,7 @@ impl LengthFieldBasedDecoder {
|
|||
|
||||
pub fn input(&mut self, data: &[u8]) {
|
||||
self.ensure_capacity(data.len());
|
||||
|
||||
(&mut self.buffer[self.cur_index..self.cur_index + data.len()]).copy_from_slice(data);
|
||||
self.buffer[self.cur_index..self.cur_index + data.len()].copy_from_slice(data);
|
||||
self.cur_index += data.len();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ impl UdpServer {
|
|||
conv_id,
|
||||
addr,
|
||||
self.socket.clone(),
|
||||
&self.protokey_helper,
|
||||
self.protokey_helper,
|
||||
self.db.clone(),
|
||||
);
|
||||
self.session_mgr.add(conv_id, session);
|
||||
|
|
|
@ -26,11 +26,12 @@ async fn main() -> Result<()> {
|
|||
LazyLock::new(|| config_util::load_or_create("hotpatch.toml"));
|
||||
|
||||
::common::splash::print_splash();
|
||||
::common::logging::init(::tracing::Level::DEBUG);
|
||||
::common::logging::init_axum(::tracing::Level::DEBUG);
|
||||
|
||||
Application::new()
|
||||
.get("/:env/client/:hash/:platform/config.json", get_config)
|
||||
.with_encryption(&CONFIG.encryption)
|
||||
.with_logger()
|
||||
.serve(&CONFIG.network)
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -11,13 +11,10 @@ pub async fn handle_login_api_call(
|
|||
tracing::debug!("login requested");
|
||||
|
||||
let user_data = parameters.user_data;
|
||||
let result = match login(&state, parameters).await {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
tracing::warn!("login: internal error occurred {err:?}");
|
||||
schema::LoginResult::error(-1, String::from("Internal server error"))
|
||||
}
|
||||
};
|
||||
let result = login(&state, parameters).await.unwrap_or_else(|err| {
|
||||
tracing::warn!("login: internal error occurred {err:?}");
|
||||
schema::LoginResult::error(-1, String::from("Internal server error"))
|
||||
});
|
||||
|
||||
Json(result.with_user_data(user_data))
|
||||
}
|
||||
|
@ -32,7 +29,7 @@ async fn login(state: &ServiceState, params: schema::LoginParameters) -> Result<
|
|||
Some(account) => {
|
||||
if let Some(ban_time_stamp) = account.ban_time_stamp {
|
||||
if time_util::unix_timestamp() < ban_time_stamp as u64 {
|
||||
return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp as i64));
|
||||
return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::{process, sync::LazyLock};
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use config::{GatewayConfig, ServerConfig};
|
||||
use shorekeeper_database::PgPool;
|
||||
use shorekeeper_http::{Application, StatusCode};
|
||||
|
@ -21,7 +22,7 @@ async fn main() -> Result<()> {
|
|||
LazyLock::new(|| ::common::config_util::load_or_create("loginserver.toml"));
|
||||
|
||||
::common::splash::print_splash();
|
||||
::common::logging::init(::tracing::Level::DEBUG);
|
||||
::common::logging::init_axum(::tracing::Level::DEBUG);
|
||||
|
||||
let Ok(pool) = shorekeeper_database::connect_to(&CONFIG.database).await else {
|
||||
tracing::error!(
|
||||
|
@ -37,10 +38,11 @@ async fn main() -> Result<()> {
|
|||
pool,
|
||||
gateway: &CONFIG.gateway,
|
||||
})
|
||||
.get("/health", || async { StatusCode::OK })
|
||||
.get("/api/login", handler::handle_login_api_call)
|
||||
.serve(&CONFIG.network)
|
||||
.await?;
|
||||
.get("/health", || async { StatusCode::OK })
|
||||
.get("/api/login", handler::handle_login_api_call)
|
||||
.with_logger()
|
||||
.serve(&CONFIG.network)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue