Compare commits

..

No commits in common. "master" and "0.1.0" have entirely different histories.

116 changed files with 186842 additions and 7394449 deletions

View file

@ -1,12 +0,0 @@
.git
assets
postgres
**/target
.gitignore
docker-compose.yml
Dockerfile-builder
Dockerfile-service
LICENSE
*.md
*.zip
*.png

1
.gitignore vendored
View file

@ -1,4 +1,3 @@
.idea
/target /target
/hotpatch.toml /hotpatch.toml
/configserver.toml /configserver.toml

106
Cargo.lock generated
View file

@ -402,7 +402,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"hashbrown 0.14.5", "hashbrown",
"lock_api", "lock_api",
"once_cell", "once_cell",
"parking_lot_core", "parking_lot_core",
@ -416,7 +416,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"hashbrown 0.14.5", "hashbrown",
"lock_api", "lock_api",
"once_cell", "once_cell",
"parking_lot_core", "parking_lot_core",
@ -724,19 +724,13 @@ dependencies = [
"allocator-api2", "allocator-api2",
] ]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]] [[package]]
name = "hashlink" name = "hashlink"
version = "0.9.1" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [ dependencies = [
"hashbrown 0.14.5", "hashbrown",
] ]
[[package]] [[package]]
@ -831,12 +825,6 @@ dependencies = [
"pin-project-lite", "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]] [[package]]
name = "httparse" name = "httparse"
version = "1.9.4" version = "1.9.4"
@ -896,12 +884,12 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.6.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.15.0", "hashbrown",
] ]
[[package]] [[package]]
@ -1018,15 +1006,6 @@ dependencies = [
"tracing", "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]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.3" version = "0.7.3"
@ -1064,16 +1043,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 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]] [[package]]
name = "minimal-lexical" name = "minimal-lexical"
version = "0.2.1" version = "0.2.1"
@ -1493,17 +1462,8 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata 0.4.7", "regex-automata",
"regex-syntax 0.8.4", "regex-syntax",
]
[[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]] [[package]]
@ -1514,15 +1474,9 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-syntax 0.8.4", "regex-syntax",
] ]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.8.4" version = "0.8.4"
@ -1771,7 +1725,6 @@ dependencies = [
"rbase64", "rbase64",
"serde", "serde",
"thiserror", "thiserror",
"tower-http",
"tracing", "tracing",
] ]
@ -1792,14 +1745,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"crc32fast", "crc32fast",
"prettyplease",
"prost", "prost",
"prost-build", "prost-build",
"quote", "quote",
"serde",
"serde_json",
"shorekeeper-protocol-derive", "shorekeeper-protocol-derive",
"syn",
"thiserror", "thiserror",
] ]
@ -1933,7 +1882,7 @@ dependencies = [
"futures-intrusive", "futures-intrusive",
"futures-io", "futures-io",
"futures-util", "futures-util",
"hashbrown 0.14.5", "hashbrown",
"hashlink", "hashlink",
"hex", "hex",
"indexmap", "indexmap",
@ -2320,31 +2269,6 @@ dependencies = [
"tracing", "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]] [[package]]
name = "tower-layer" name = "tower-layer"
version = "0.3.3" version = "0.3.3"
@ -2407,14 +2331,10 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [ dependencies = [
"matchers",
"nu-ansi-term", "nu-ansi-term",
"once_cell",
"regex",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
] ]
@ -2425,12 +2345,6 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicase"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.15" version = "0.3.15"

View file

@ -8,7 +8,6 @@ version = "0.1.0"
[workspace.dependencies] [workspace.dependencies]
# Framework # Framework
tokio = { version = "1.39.3", features = ["full"] } tokio = { version = "1.39.3", features = ["full"] }
tower-http = { version = "0.6.1", features = ["fs", "trace"] }
axum = "0.7.5" axum = "0.7.5"
axum-server = "0.7.1" axum-server = "0.7.1"
zeromq = { version = "0.4.0", default-features = false, features = ["tokio-runtime", "tcp-transport"] } zeromq = { version = "0.4.0", default-features = false, features = ["tokio-runtime", "tcp-transport"] }
@ -42,18 +41,18 @@ crc32fast = "1.4.2"
# Tracing # Tracing
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = "0.3.18"
# Internal # Internal
kcp = { path = "gateway-server/kcp" } kcp = { path = "gateway-server/kcp" }
common = { path = "common" } common = { path = "common/" }
shorekeeper-http = { path = "shorekeeper-http" } shorekeeper-http = { path = "shorekeeper-http/" }
shorekeeper-data = { path = "shorekeeper-data" } shorekeeper-data = { path = "shorekeeper-data/" }
shorekeeper-database = { path = "shorekeeper-database" } shorekeeper-database = { path = "shorekeeper-database/" }
shorekeeper-network = { path = "shorekeeper-network" } shorekeeper-network = { path = "shorekeeper-network/" }
shorekeeper-protocol = { path = "shorekeeper-protocol" } shorekeeper-protocol = { path = "shorekeeper-protocol/" }
shorekeeper-protocol-derive = { path = "shorekeeper-protocol/shorekeeper-protocol-derive" } shorekeeper-protocol-derive = { path = "shorekeeper-protocol/shorekeeper-protocol-derive" }
shorekeeper-protokey = { path = "shorekeeper-protokey" } shorekeeper-protokey = { path = "shorekeeper-protokey/" }
[profile.release] [profile.release]
strip = true # Automatically strip symbols from the binary. strip = true # Automatically strip symbols from the binary.

View file

@ -1,6 +0,0 @@
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

View file

@ -1,6 +0,0 @@
FROM alpine:3.20
ARG MICROSERVICE
WORKDIR /app
COPY --from=wicked-waifus-builder:1.4.0-SNAPSHOT /app/target/release/$MICROSERVICE ./service
CMD ["./service"]

View file

@ -24,22 +24,7 @@ cargo run --bin gateway-server
cargo run --bin game-server cargo run --bin game-server
``` ```
##### b) building from sources(docker edition) ##### b) using pre-built binaries
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) Navigate to the [Releases](https://git.xeondev.com/Shorekeeper/Shorekeeper/releases)
page and download the latest release for your platform.<br> page and download the latest release for your platform.<br>
Launch all servers: `config-server`, `hotpatch-server`, `login-server`, `gateway-server`, `game-server` Launch all servers: `config-server`, `hotpatch-server`, `login-server`, `gateway-server`, `game-server`

View file

@ -1,64 +0,0 @@
{
"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
}
}

View file

@ -1,60 +0,0 @@
{
"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
}
}

View file

@ -2,20 +2,8 @@
"default": { "default": {
"CdnUrl": [ "CdnUrl": [
{ {
"url": "https://cdn-huoshan-cn-mc.aki-game.com/prod/client/", "url": "http://127.0.0.1:10002/prod/client/",
"weight": "2323" "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": [], "SecondaryUrl": [],

View file

@ -1,115 +0,0 @@
{
"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
}
}

View file

@ -1,44 +1,60 @@
{ {
"PackageVersion": "1.4.0", "PackageVersion": "1.3.0",
"LauncherVersion": "1.4.0", "LauncherVersion": "1.3.9",
"ResourceVersion": "1.4.3", "ResourceVersion": "1.3.9",
"LauncherIndexSha1": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "LauncherIndexSha1": {
"ResourceIndexSha1": { "1.3.1": "90FDF17EA0B4015D43C344CB7229E76AB32549DD",
"1.4.1": "0C747E444EC6DBA11D0C57B34E2638DE5182029F", "1.3.2": "C9A587AB1FA6CA57CD23E0FB3F0103BFDCAA8E37",
"1.4.2": "41751A94DBD406653DB52262E85BB716E45D9864", "1.3.3": "1C7AF02F13DBE69637DB43039E2FFB8C9AD9A04B",
"1.4.3": "424EDFD97BA8699CBC2CB9E27D3FCBB6F7C70806" "1.3.4": "DA50F315041E216568A7713074C6475F6AB4530E",
"1.3.5": "EA9C6F6D5E920F47F96D8F8BC366A4CED62A0346",
"1.3.6": "8CA7E6573A52B16CFAA29E996D389918B6829E7A",
"1.3.7": "FCAAED58E5983027A82F52C350418CCE7BD531D2",
"1.3.8": "91D6231B3F4C9A6605B79E23D0C02F9790DD6BCF",
"1.3.9": "B36BD648AB2A637A4E087B7B115A6CCBDAEBDF9A"
}, },
"ChangeList": "2585778", "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"
},
"ChangeList": "2333675",
"CompatibleChangeLists": [], "CompatibleChangeLists": [],
"Versions": [ "Versions": [
{ {
"Name": "en", "Name": "en",
"Version": "1.4.0", "Version": "1.3.0",
"IndexSha1": { "IndexSha1": {
"1.4.0": "34F9AC326B3E81E972B93094E43751967A6A3396" "1.3.0": "6FB5B66EF8B3EECBBBEBE74A82BC23E3FC35450B"
} }
}, },
{ {
"Name": "ja", "Name": "ja",
"Version": "1.4.0", "Version": "1.3.0",
"IndexSha1": { "IndexSha1": {
"1.4.0": "3233223428D586FBC0A2EC817F4E94DDD10C646F" "1.3.0": "E4DA1960DB36CE8166C042AD8B9AF98C1A9119F3"
} }
}, },
{ {
"Name": "ko", "Name": "ko",
"Version": "1.4.0", "Version": "1.3.0",
"IndexSha1": { "IndexSha1": {
"1.4.0": "3CC0312BDFFF5FA4D3F5256E2618D0EF6AA84912" "1.3.0": "498B379E95FC617385CCD832B8C359FA5AC220CE"
} }
}, },
{ {
"Name": "zh", "Name": "zh",
"Version": "1.4.0", "Version": "1.3.0",
"IndexSha1": { "IndexSha1": {
"1.4.0": "2F3AD1412C6991FE0E146C5E2F6F86D8D9F44A5D" "1.3.0": "CC58C357A80E7B3846264918197FC3ECAA1FE190"
} }
} }
], ],
"UpdateTime": 1729243476 "UpdateTime": 1725869509
} }

File diff suppressed because it is too large Load diff

View file

@ -1,246 +0,0 @@
[
{
"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
}
]

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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 one or more lines are too long

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 one or more lines are too long

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

View file

@ -1,12 +0,0 @@
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

View file

@ -1,12 +0,0 @@
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

View file

@ -4,7 +4,7 @@ pub trait TomlConfig: DeserializeOwned {
const DEFAULT_TOML: &str; const DEFAULT_TOML: &str;
} }
pub fn load_or_create<C>(path: &str) -> C pub fn load_or_create<'a, C>(path: &str) -> C
where where
C: DeserializeOwned + TomlConfig, C: DeserializeOwned + TomlConfig,
{ {

View file

@ -1,6 +1,4 @@
use tracing::Level; use tracing::Level;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub fn init(max_level: Level) { pub fn init(max_level: Level) {
tracing_subscriber::fmt() tracing_subscriber::fmt()
@ -8,21 +6,3 @@ pub fn init(max_level: Level) {
.with_target(false) .with_target(false)
.init(); .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();
}

View file

@ -1,10 +1,3 @@
pub fn print_splash() { pub fn print_splash() {
println!(" println!(" _____ __ __ \n / ___// /_ ____ ________ / /_____ ___ ____ ___ _____\n \\__ \\/ __ \\/ __ \\/ ___/ _ \\/ //_/ _ \\/ _ \\/ __ \\/ _ \\/ ___/\n ___/ / / / / /_/ / / / __/ ,< / __/ __/ /_/ / __/ / \n/____/_/ /_/\\____/_/ \\___/_/|_|\\___/\\___/ .___/\\___/_/ \n /_/ ");
");
} }

View file

@ -4,7 +4,7 @@ pub fn unix_timestamp() -> u64 {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.unwrap() .unwrap()
.as_secs() .as_secs() as u64
} }
pub fn unix_timestamp_ms() -> u64 { pub fn unix_timestamp_ms() -> u64 {

View file

@ -1,3 +1,4 @@
use std::fs;
use std::sync::LazyLock; use std::sync::LazyLock;
use anyhow::Result; use anyhow::Result;
@ -25,14 +26,20 @@ async fn main() -> Result<()> {
LazyLock::new(|| config_util::load_or_create("configserver.toml")); LazyLock::new(|| config_util::load_or_create("configserver.toml"));
::common::splash::print_splash(); ::common::splash::print_splash();
::common::logging::init_axum(::tracing::Level::DEBUG); ::common::logging::init(::tracing::Level::DEBUG);
Application::new() Application::new()
.serve_dir("/", "assets/config") .get("/index.json", get_index)
.with_encryption(&CONFIG.encryption) .with_encryption(&CONFIG.encryption)
.with_logger()
.serve(&CONFIG.network) .serve(&CONFIG.network)
.await?; .await?;
Ok(()) Ok(())
} }
async fn get_index() -> &'static str {
static INDEX: LazyLock<String> =
LazyLock::new(|| fs::read_to_string("assets/config/index.json").unwrap());
&*INDEX
}

View file

@ -1,13 +0,0 @@
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"

View file

@ -1,63 +0,0 @@
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

View file

@ -1,6 +0,0 @@
[network]
http_addr = "0.0.0.0:10001"
[encryption]
key = "t+AEu5SGdpz06tomonajLMau9AJgmyTvVhz9VtGf1+0="
iv = "fprc5lBWADQB7tim0R2JxQ=="

View file

@ -1,13 +0,0 @@
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"

View file

@ -1,20 +0,0 @@
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"

View file

@ -1,6 +0,0 @@
[network]
http_addr = "0.0.0.0:10002"
[encryption]
key = "t+AEu5SGdpz06tomonajLMau9AJgmyTvVhz9VtGf1+0="
iv = "fprc5lBWADQB7tim0R2JxQ=="

View file

@ -1,12 +0,0 @@
[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"

View file

@ -1,3 +0,0 @@
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;

View file

@ -1,2 +0,0 @@
\c wicked_waifus_db;
GRANT ALL ON SCHEMA public TO wicked_waifus_user;

View file

@ -1,12 +0,0 @@
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);
};

View file

@ -1,31 +1,52 @@
use std::collections::HashMap;
use shorekeeper_data::BasePropertyData; use shorekeeper_data::BasePropertyData;
use shorekeeper_protocol::{ use shorekeeper_protocol::{
entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType, entity_component_pb::ComponentPb, AttrData, AttributeComponentPb, EAttributeType,
EntityComponentPb, LivingStatus, EntityComponentPb, LivingStatus,
}; };
use std::collections::HashMap;
use crate::logic::ecs::component::Component; use crate::logic::ecs::component::Component;
use crate::logic::utils::load_role_info::attribute_from_data;
pub struct Attribute { pub struct Attribute {
pub attr_map: HashMap<EAttributeType, (i32, i32)>, 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 { impl Component for Attribute {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) { fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.living_status = (if self.is_alive() { pb.living_status = self
LivingStatus::Alive .is_alive()
} else { .then_some(LivingStatus::Alive)
LivingStatus::Dead .unwrap_or(LivingStatus::Dead)
})
.into(); .into();
pb.component_pbs.push(EntityComponentPb { pb.component_pbs.push(EntityComponentPb {
component_pb: Some(ComponentPb::AttributeComponent( component_pb: Some(ComponentPb::AttributeComponent(AttributeComponentPb {
self.build_entity_attribute(), 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,
})),
})
} }
} }
@ -39,27 +60,142 @@ impl Attribute {
> 0 > 0
} }
#[inline(always)] impl_from_data!(
pub fn from_data(base_property: &BasePropertyData) -> Self { lv,
Self { life_max,
attr_map: attribute_from_data(base_property), life,
} sheild,
} sheild_damage_change,
sheild_damage_reduce,
#[inline(always)] atk,
pub fn build_entity_attribute(&self) -> AttributeComponentPb { crit,
AttributeComponentPb { crit_damage,
attr_data: self def,
.attr_map energy_efficiency,
.iter() cd_reduse,
.map(|(ty, (base, incr))| AttrData { reaction_efficiency,
attribute_type: (*ty).into(), damage_change_normal_skill,
base_value: *base, damage_change,
increment: *incr, damage_reduce,
}) damage_change_auto,
.collect(), damage_change_cast,
hardness_mode_id: 0, damage_change_ultra,
rage_mode_id: 0, 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
);
} }

View file

@ -1,19 +1,15 @@
use shorekeeper_protocol::{EEntityType, EntityConfigType, EntityState}; use shorekeeper_protocol::EntityConfigType;
use crate::logic::ecs::component::Component; use crate::logic::ecs::component::Component;
pub struct EntityConfig { pub struct EntityConfig {
pub config_id: i32, pub config_id: i32,
pub config_type: EntityConfigType, pub config_type: EntityConfigType,
pub entity_type: EEntityType,
pub entity_state: EntityState
} }
impl Component for EntityConfig { impl Component for EntityConfig {
fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) { fn set_pb_data(&self, pb: &mut shorekeeper_protocol::EntityPb) {
pb.config_id = self.config_id; pb.config_id = self.config_id;
pb.config_type = self.config_type.into(); pb.config_type = self.config_type.into();
pb.entity_type = self.entity_type.into();
pb.entity_state = self.entity_state.into();
} }
} }

View file

@ -1,19 +0,0 @@
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,
})),
})
}
}

View file

@ -1,25 +0,0 @@
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(),
})),
})
}
}

View file

@ -1,23 +1,15 @@
mod attribute; mod attribute;
mod entity_config; mod entity_config;
mod equip;
mod movement; mod movement;
mod owner_player; mod owner_player;
mod player_entity_marker; mod player_entity_marker;
mod position; mod position;
mod visibility; mod visibility;
mod vision_skill;
mod monster_ai;
mod fsm;
pub use attribute::Attribute; pub use attribute::Attribute;
pub use entity_config::EntityConfig; pub use entity_config::EntityConfig;
pub use equip::Equip;
pub use movement::Movement; pub use movement::Movement;
pub use owner_player::OwnerPlayer; pub use owner_player::OwnerPlayer;
pub use player_entity_marker::PlayerEntityMarker; pub use player_entity_marker::PlayerEntityMarker;
pub use position::Position; pub use position::Position;
pub use visibility::Visibility; pub use visibility::Visibility;
pub use vision_skill::VisionSkill;
pub use monster_ai::MonsterAi;
pub use fsm::Fsm;

View file

@ -1,23 +0,0 @@
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,
})),
})
}
}

View file

@ -1,23 +0,0 @@
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()
}],
})),
})
}
}

View file

@ -30,10 +30,6 @@ impl_component_container! {
Attribute; Attribute;
PlayerEntityMarker; PlayerEntityMarker;
Movement; Movement;
Equip;
VisionSkill;
MonsterAi;
Fsm;
} }
pub trait Component { pub trait Component {

View file

@ -1,118 +1,34 @@
use std::{cell::RefCell, collections::HashSet};
use super::component::ComponentContainer; 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)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Entity { pub struct Entity(i64);
pub entity_id: i32,
pub entity_type: i32,
pub map_id: i32,
}
pub struct EntityBuilder<'comp>(Entity, &'comp mut Vec<RefCell<ComponentContainer>>); pub struct EntityBuilder<'comp>(Entity, &'comp mut Vec<RefCell<ComponentContainer>>);
#[derive(Default)]
pub struct EntityManager { pub struct EntityManager {
active_entity_set: HashMap<i32, Vec<Entity>>, entity_id_counter: i64,
next_id: AtomicI32, active_entity_set: HashSet<Entity>,
recycled_ids: HashMap<i32, VecDeque<i32>>,
} }
impl EntityManager { impl EntityManager {
pub fn create(&mut self, config_id: i32, entity_type: i32, map_id: i32) -> Entity { pub fn create(&mut self) -> Entity {
let entity_id = self self.entity_id_counter += 1;
.recycled_ids let entity = Entity(self.entity_id_counter);
.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 entity
} }
pub fn get_entity_id(&self, config_id: i32) -> i32 { pub fn get(&self, id: i64) -> Option<Entity> {
self.active_entity_set self.active_entity_set.get(&Entity(id)).copied()
.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
})
} }
pub fn get_config_id(&self, entity_id: i32) -> i32 { #[expect(dead_code)]
self.active_entity_set pub fn remove(&mut self, entity: Entity) -> bool {
.iter() self.active_entity_set.remove(&entity)
.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
} }
} }
@ -136,26 +52,6 @@ impl<'comp> EntityBuilder<'comp> {
impl From<Entity> for i64 { impl From<Entity> for i64 {
fn from(value: Entity) -> Self { fn from(value: Entity) -> Self {
value.entity_id as i64 value.0
}
}
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,
}
} }
} }

View file

@ -2,56 +2,28 @@ pub mod component;
pub mod entity; pub mod entity;
pub mod world; pub mod world;
// Query specified components from all entities
#[macro_export] #[macro_export]
macro_rules! find_component { macro_rules! query_with {
($comps:expr, $comp:ident) => { ($world:expr, $($comp:ident),*) => {
$comps.iter().find_map(|comp| { $world.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()?; let r = comp.try_borrow_mut().ok()?;
if matches!(&*r, ComponentContainer::$comp(_)) { if matches!(&*r, ComponentContainer::$comp(_)) {
Some(::std::cell::RefMut::map(r, |r| { Some(::std::cell::RefMut::map(r, |r| {
let ComponentContainer::$comp(comp_inner) = r else { let ComponentContainer::$comp(comp_inner) = r else { unreachable!() };
unreachable!()
};
comp_inner comp_inner
})) }))
} else { }
else {
None None
} }
}) }).unwrap(),
};
}
// Query specified components from all entities (and)
#[macro_export]
macro_rules! query_with {
($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,
$(
$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),
)* )*
) )
}) })
@ -69,24 +41,24 @@ macro_rules! ident_as_none {
// Query components of specified entity // Query components of specified entity
#[macro_export] #[macro_export]
macro_rules! query_components { macro_rules! query_components {
($world_entitys:expr, $entity_id:expr, $($comp:ident),*) => { ($world:expr, $entity_id:expr, $($comp:ident),*) => {
$world_entitys.components().iter().find(|(id, _)| $entity_id == i64::from(**id)) $world.components().iter().find(|(id, _)| $entity_id == i64::from(**id))
.map(|(_, comps)| { .map(|(_, comps)| {
($( ($(
$crate::find_component!(comps, $comp), 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_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);
}
});
}; };
} }

View file

@ -1,30 +1,48 @@
use super::component::ComponentContainer;
use super::entity::{Entity, EntityBuilder, EntityManager};
use crate::logic::player::InWorldPlayer;
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::hash_map::{Keys, Values}; use std::collections::hash_map::{Keys, Values};
use std::collections::HashMap; use std::collections::HashMap;
pub struct WorldEntity { use crate::logic::player::InWorldPlayer;
components: HashMap<i32, Vec<RefCell<ComponentContainer>>>,
entity_manager: EntityManager, use super::component::ComponentContainer;
} use super::entity::{Entity, EntityBuilder, EntityManager};
pub struct World { pub struct World {
pub player_cur_map_id: i32, components: HashMap<Entity, Vec<RefCell<ComponentContainer>>>,
pub world_entitys: HashMap<i32, WorldEntity>, // i32 -> map_id entity_manager: EntityManager,
pub in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata in_world_players: HashMap<i32, InWorldPlayer>, // joined players metadata
} }
impl World { impl World {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
player_cur_map_id: 8, components: HashMap::new(),
world_entitys: HashMap::new(), entity_manager: EntityManager::default(),
in_world_players: 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> { pub fn player_ids(&self) -> Keys<'_, i32, InWorldPlayer> {
self.in_world_players.keys() self.in_world_players.keys()
} }
@ -37,84 +55,4 @@ impl World {
self.in_world_players self.in_world_players
.insert(in_world_player.player_id, in_world_player); .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(),
}
}
} }

View file

@ -1,73 +0,0 @@
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();
}

View file

@ -1,139 +0,0 @@
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);
}
}
}

View file

@ -1,12 +0,0 @@
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();
}

View file

@ -1,16 +0,0 @@
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,
});
}

View file

@ -1,49 +0,0 @@
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
);
}

View file

@ -1,53 +1,31 @@
mod combat;
mod entity;
mod guide;
mod mail;
mod misc;
mod role;
mod scene; 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 scene::*;
pub use skill::*;
use shorekeeper_protocol::message::Message; use shorekeeper_protocol::message::Message;
macro_rules! handle_request { macro_rules! handle_request {
($($name:ident $(, $inner_package:ident)?;)*) => { ($($name:ident;)*) => {
fn handle_request(player: &mut super::player::Player, mut msg: Message) { fn handle_request(player: &mut super::player::Player, mut msg: Message) {
use ::shorekeeper_protocol::{MessageID, Protobuf}; use ::shorekeeper_protocol::{MessageID, Protobuf};
::paste::paste! { ::paste::paste! {
match msg.get_message_id() { match msg.get_message_id() {
$( $(
::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::MESSAGE_ID => { ::shorekeeper_protocol::[<$name Request>]::MESSAGE_ID => {
let Ok(request) = ::shorekeeper_protocol::$($inner_package::)?[<$name Request>]::decode(&*msg.remove_payload()) else { let Ok(request) = ::shorekeeper_protocol::[<$name Request>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Request>]), player.basic_info.id); tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Request>]), player.basic_info.id);
return; return;
}; };
tracing::debug!("logic: processing request {}", stringify!($($inner_package::)?[<$name Request>])); tracing::debug!("logic: processing request {}", stringify!([<$name Request>]));
let mut response = ::shorekeeper_protocol::$($inner_package::)?[<$name Response>]::default(); let mut response = ::shorekeeper_protocol::[<$name Response>]::default();
[<on_ $($inner_package:snake _)? $name:snake _request>](player, request, &mut response); [<on_ $name:snake _request>](player, request, &mut response);
player.respond(response, msg.get_rpc_id()); player.respond(response, msg.get_rpc_id());
}, },
)* )*
unhandled => { unhandled => ::tracing::warn!("can't find handler for request with message_id={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}")
}
} }
} }
} }
@ -55,32 +33,23 @@ macro_rules! handle_request {
} }
macro_rules! handle_push { macro_rules! handle_push {
($($name:ident $(, $inner_package:ident)?;)*) => { ($($name:ident;)*) => {
fn handle_push(player: &mut super::player::Player, mut msg: Message) { fn handle_push(player: &mut super::player::Player, mut msg: Message) {
use ::shorekeeper_protocol::{MessageID, Protobuf}; use ::shorekeeper_protocol::{MessageID, Protobuf};
::paste::paste! { ::paste::paste! {
match msg.get_message_id() { match msg.get_message_id() {
$( $(
::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::MESSAGE_ID => { ::shorekeeper_protocol::[<$name Push>]::MESSAGE_ID => {
let Ok(push) = ::shorekeeper_protocol::$($inner_package::)?[<$name Push>]::decode(&*msg.remove_payload()) else { let Ok(push) = ::shorekeeper_protocol::[<$name Push>]::decode(&*msg.remove_payload()) else {
tracing::debug!("failed to decode {}, player_id: {}", stringify!($($inner_package::)?[<$name Push>]), player.basic_info.id); tracing::debug!("failed to decode {}, player_id: {}", stringify!([<$name Push>]), player.basic_info.id);
return; return;
}; };
tracing::debug!("logic: processing push {}", stringify!($($inner_package::)?[<$name Push>])); [<on_ $name:snake _push>](player, push);
[<on_ $($inner_package:snake _)? $name:snake _push>](player, push);
}, },
)* )*
unhandled => { unhandled => ::tracing::warn!("can't find handler for push with message_id={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}")
}
} }
} }
} }
@ -88,49 +57,13 @@ macro_rules! handle_push {
} }
handle_request! { handle_request! {
// Combat UpdateSceneDate;
CombatSendPack, combat_message;
// Role
RoleShowListUpdate;
ClientCurrentRoleReport;
RoleFavorList;
FormationAttr;
UpdateFormation;
// Entity
EntityActive; EntityActive;
EntityOnLanded; EntityOnLanded;
EntityPosition;
EntityLoadComplete;
// Guide
GuideInfo;
// Mail
MailBindInfo;
// Misc
InputSetting;
InputSettingUpdate;
LanguageSettingUpdate;
ServerPlayStationPlayOnlyState;
// Scene
SceneTrace;
SceneLoadingFinish;
UpdateSceneDate;
// Skill
VisionExploreSkillSet;
} }
handle_push! { handle_push! {
// Entity
MovePackage; MovePackage;
// Misc
VersionInfo;
} }
pub fn handle_logic_message(player: &mut super::player::Player, msg: Message) { pub fn handle_logic_message(player: &mut super::player::Player, msg: Message) {

View file

@ -1,141 +0,0 @@
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();
}

View file

@ -1,26 +1,9 @@
use shorekeeper_protocol::{ use shorekeeper_protocol::{
ErrorCode, SceneLoadingFinishRequest, SceneLoadingFinishResponse, SceneTraceRequest, EntityActiveRequest, EntityActiveResponse, EntityOnLandedRequest, EntityOnLandedResponse,
SceneTraceResponse, UpdateSceneDateRequest, UpdateSceneDateResponse, ErrorCode, MovePackagePush, UpdateSceneDateRequest, UpdateSceneDateResponse,
}; };
use crate::logic::player::Player; use crate::{logic::ecs::component::ComponentContainer, logic::player::Player, query_components};
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( pub fn on_update_scene_date_request(
_player: &Player, _player: &Player,
@ -29,3 +12,66 @@ pub fn on_update_scene_date_request(
) { ) {
response.error_code = ErrorCode::Success.into(); 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);
}
}

View file

@ -1,35 +0,0 @@
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;
}

View file

@ -1,4 +1,3 @@
use shorekeeper_data::RawVectorData;
use shorekeeper_protocol::{Rotator, TransformData}; use shorekeeper_protocol::{Rotator, TransformData};
use super::Vector3f; use super::Vector3f;
@ -48,12 +47,3 @@ impl Transform {
} }
} }
} }
impl From<&[RawVectorData]> for Transform {
fn from(transform: &[RawVectorData]) -> Self {
Self {
position: Vector3f::from(&transform[0]),
..Default::default()
}
}
}

View file

@ -1,4 +1,3 @@
use shorekeeper_data::RawVectorData;
use shorekeeper_protocol::{Vector, VectorData}; use shorekeeper_protocol::{Vector, VectorData};
#[derive(Default, Clone, PartialEq, Debug)] #[derive(Default, Clone, PartialEq, Debug)]
@ -41,13 +40,3 @@ 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,
}
}
}

View file

@ -10,8 +10,6 @@ pub struct PlayerBasicInfo {
pub exp: i32, pub exp: i32,
pub head_photo: i32, pub head_photo: i32,
pub head_frame: i32, pub head_frame: i32,
pub cur_map_id: i32,
pub role_show_list: Vec<i32>,
} }
impl PlayerBasicInfo { impl PlayerBasicInfo {
@ -39,8 +37,6 @@ impl PlayerBasicInfo {
exp: data.exp, exp: data.exp,
head_photo: data.head_photo, head_photo: data.head_photo,
head_frame: data.head_frame, head_frame: data.head_frame,
cur_map_id: data.cur_map_id,
role_show_list: data.role_show_list,
} }
} }
@ -53,8 +49,6 @@ impl PlayerBasicInfo {
exp: self.exp, exp: self.exp,
head_photo: self.head_photo, head_photo: self.head_photo,
head_frame: self.head_frame, head_frame: self.head_frame,
cur_map_id: self.cur_map_id,
role_show_list: self.role_show_list.clone(),
} }
} }
} }

View file

@ -1,76 +0,0 @@
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(),
}
}
}

View file

@ -1,23 +1,14 @@
use common::time_util; use std::{cell::RefCell, collections::HashSet, rc::Rc, sync::Arc};
use shorekeeper_protocol::{
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::{ use basic_info::PlayerBasicInfo;
components::{ use common::time_util;
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position, use location::PlayerLocation;
Visibility, VisionSkill, use player_func::PlayerFunc;
}, use shorekeeper_protocol::{
ecs::component::ComponentContainer, message::Message, PbGetRoleListNotify, PlayerBasicData, PlayerRoleData, PlayerSaveData,
ProtocolUnit,
}; };
use crate::session::Session; use crate::session::Session;
use super::{ use super::{
@ -26,145 +17,94 @@ use super::{
}; };
mod basic_info; mod basic_info;
mod explore_tools;
mod in_world_player; mod in_world_player;
mod location; mod location;
mod player_func; 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; 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 { pub struct Player {
session: Option<Arc<Session>>, session: Option<Arc<Session>>,
// Persistent
pub basic_info: PlayerBasicInfo, pub basic_info: PlayerBasicInfo,
pub role_list: HashMap<i32, Role>, pub role_list: Vec<Role>,
pub formation_list: HashMap<i32, RoleFormation>, pub formation_list: Vec<RoleFormation>,
pub cur_formation_id: i32,
pub location: PlayerLocation, pub location: PlayerLocation,
pub func: PlayerFunc, pub func: PlayerFunc,
pub explore_tools: ExploreTools,
// Runtime
pub world: Rc<RefCell<World>>, pub world: Rc<RefCell<World>>,
pub last_save_time: u64, pub last_save_time: u64,
pub quadrant_id: u64,
} }
impl Player { impl Player {
pub fn init(&mut self) { pub fn init(&mut self) {
if self.role_list.is_empty() || self.formation_list.is_empty() { if self.role_list.is_empty() {
self.init_role_and_formation(); self.on_first_enter();
} }
self.ensure_basic_unlock_func(); // 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();
} }
pub fn notify_general_data(&self) { pub fn notify_general_data(&self) {
self.notify(self.basic_info.build_notify()); self.notify(self.basic_info.build_notify());
self.notify(self.func.build_func_open_notify()); self.notify(self.func.build_func_open_notify());
self.notify(self.build_role_list_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());
self.notify(ItemPkgOpenNotify {
open_pkg: (0..8).collect(),
});
self.notify(self.build_update_formation_notify());
} }
fn init_role_and_formation(&mut self) { fn on_first_enter(&mut self) {
self.role_list.clear(); self.role_list.push(Self::create_main_character_role(
let mut role = match self.basic_info.sex { self.basic_info.name.clone(),
0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID), self.basic_info.sex,
1 => Role::new(Role::MAIN_CHARACTER_MALE_ID), ));
_ => unreachable!(),
};
role.name = self.basic_info.name.clone(); let role = &self.role_list[0];
self.role_list.insert(role.role_id, role); self.formation_list.push(RoleFormation {
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, id: 1,
cur_role: *formation.iter().next().unwrap(), cur_role: role.role_id,
role_ids: formation, role_id_set: HashSet::from([role.role_id]),
is_current: true, 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(); self.location = PlayerLocation::default();
}
// 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) { fn ensure_current_formation(&mut self) {
// If the list off formation is empty, add a default formation
if self.formation_list.is_empty() { if self.formation_list.is_empty() {
let mut role_list_clone = self.role_list.iter().clone(); let role = &self.role_list[0];
self.formation_list.insert( self.formation_list.push(RoleFormation {
1,
RoleFormation {
id: 1, id: 1,
cur_role: role_list_clone.next().unwrap().1.role_id, cur_role: role.role_id,
role_ids: role_list_clone role_id_set: HashSet::from([role.role_id]),
.take(3)
.map(|(&role_id, _)| role_id)
.collect(),
is_current: true, is_current: true,
}, });
);
} }
// If there is no current formation, set the first formation as the current formation if !self.formation_list.iter().any(|rf| rf.is_current) {
if !self.formation_list.values().any(|rf| rf.is_current) { self.formation_list[0].is_current = true;
self.formation_list.get_mut(&1).unwrap().is_current = true;
} }
// 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.iter_mut().find(|rf| rf.is_current) {
if let Some(rf) = self.formation_list.values_mut().find(|rf| rf.is_current) { if rf.role_id_set.is_empty() {
if rf.role_ids.is_empty() { rf.role_id_set.insert(self.role_list[0].role_id);
rf.role_ids
.push(self.role_list.iter().next().unwrap().1.role_id);
} }
if !rf.role_ids.contains(&rf.cur_role) { if !rf.role_id_set.contains(&rf.cur_role) {
rf.cur_role = *rf.role_ids.iter().nth(0).unwrap(); rf.cur_role = *rf.role_id_set.iter().nth(0).unwrap();
} }
} }
} }
@ -179,105 +119,24 @@ impl Player {
} }
} }
pub fn build_player_entity_add_notify( pub fn get_current_formation_role_list(&self) -> Vec<&Role> {
&self, self.formation_list
role_list: Vec<Role>, .iter()
world: &mut WorldEntity, .find(|rf| rf.is_current)
) -> EntityAddNotify { .unwrap()
create_player_entity_pb!( .role_id_set
role_list, .iter()
self.basic_info.cur_map_id, .map(|id| self.role_list.iter().find(|r| r.role_id == *id))
world, .flatten()
self.basic_info.id, .collect()
self.location.position.clone(),
self.explore_tools
)
} }
pub fn build_player_entity_remove_notify( pub fn get_cur_role_id(&self) -> i32 {
&self, self.formation_list
entities: Vec<i64>,
remove_type: ERemoveEntityType,
) -> EntityRemoveNotify {
EntityRemoveNotify {
remove_infos: entities
.iter() .iter()
.map(|&entity_id| EntityRemoveInfo { .find(|rf| rf.is_current)
entity_id, .unwrap()
r#type: remove_type.into(), .cur_role
})
.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 { pub fn load_from_save(save_data: PlayerSaveData) -> Self {
@ -285,20 +144,17 @@ impl Player {
Self { Self {
session: None, session: None,
basic_info: PlayerBasicInfo::load_from_save( basic_info: PlayerBasicInfo::load_from_save(save_data.basic_data.unwrap_or_default()),
save_data.basic_data.clone().unwrap_or_default(),
),
role_list: role_data role_list: role_data
.role_list .role_list
.into_iter() .into_iter()
.map(Role::load_from_save) .map(Role::load_from_save)
.collect::<HashMap<i32, Role>>(), .collect(),
formation_list: role_data formation_list: role_data
.role_formation_list .role_formation_list
.into_iter() .into_iter()
.map(|(k, v)| (k, RoleFormation::load_from_save(v))) .map(RoleFormation::load_from_save)
.collect(), .collect(),
cur_formation_id: role_data.cur_formation_id,
location: save_data location: save_data
.location_data .location_data
.map(PlayerLocation::load_from_save) .map(PlayerLocation::load_from_save)
@ -307,13 +163,8 @@ impl Player {
.func_data .func_data
.map(PlayerFunc::load_from_save) .map(PlayerFunc::load_from_save)
.unwrap_or_default(), .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())), world: Rc::new(RefCell::new(World::new())),
last_save_time: time_util::unix_timestamp(), last_save_time: time_util::unix_timestamp(),
quadrant_id: 0,
} }
} }
@ -321,21 +172,15 @@ impl Player {
PlayerSaveData { PlayerSaveData {
basic_data: Some(self.basic_info.build_save_data()), basic_data: Some(self.basic_info.build_save_data()),
role_data: Some(PlayerRoleData { role_data: Some(PlayerRoleData {
role_list: self role_list: self.role_list.iter().map(|r| r.build_save_data()).collect(),
.role_list
.iter()
.map(|(_, role)| role.build_save_data())
.collect(),
role_formation_list: self role_formation_list: self
.formation_list .formation_list
.iter() .iter()
.map(|(&k, v)| (k, v.build_save_data())) .map(|rf| rf.build_save_data())
.collect(), .collect(),
cur_formation_id: self.cur_formation_id,
}), }),
location_data: Some(self.location.build_save_data()), location_data: Some(self.location.build_save_data()),
func_data: Some(self.func.build_save_data()), func_data: Some(self.func.build_save_data()),
explore_tools_data: Some(self.explore_tools.build_save_data()),
} }
} }
@ -345,11 +190,7 @@ impl Player {
pub fn build_role_list_notify(&self) -> PbGetRoleListNotify { pub fn build_role_list_notify(&self) -> PbGetRoleListNotify {
PbGetRoleListNotify { PbGetRoleListNotify {
role_list: self role_list: self.role_list.iter().map(|r| r.to_protobuf()).collect(),
.role_list
.iter()
.map(|(_, role)| role.to_protobuf())
.collect(),
} }
} }
@ -374,23 +215,26 @@ impl Player {
} }
} }
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData { fn create_main_character_role(name: String, sex: i32) -> Role {
let role_id = match sex { let mut role = match sex {
0 => Role::MAIN_CHARACTER_FEMALE_ID, // 1502 0 => Role::new(Role::MAIN_CHARACTER_FEMALE_ID),
1 => Role::MAIN_CHARACTER_MALE_ID, // 1501 1 => Role::new(Role::MAIN_CHARACTER_MALE_ID),
_ => Role::MAIN_CHARACTER_MALE_ID, // Default to male _ => unreachable!(),
}; };
role.name = name;
role
}
pub fn create_default_save_data(id: i32, name: String, sex: i32) -> PlayerSaveData {
PlayerSaveData { PlayerSaveData {
basic_data: Some(PlayerBasicData { basic_data: Some(PlayerBasicData {
id, id,
name, name,
sex, sex,
level: 1, level: 1,
head_photo: 1603, head_photo: 1505,
head_frame: 80060009, head_frame: 80060009,
cur_map_id: 8,
role_show_list: vec![role_id],
..Default::default() ..Default::default()
}), }),
..Default::default() ..Default::default()

View file

@ -8,10 +8,6 @@ pub struct PlayerFunc {
} }
impl PlayerFunc { impl PlayerFunc {
pub fn unlock(&mut self, id: i32) {
self.func_map.insert(id, 2);
}
pub fn load_from_save(data: PlayerFuncData) -> Self { pub fn load_from_save(data: PlayerFuncData) -> Self {
PlayerFunc { PlayerFunc {
func_map: data.func_map, func_map: data.func_map,

View file

@ -1,9 +1,11 @@
use std::collections::HashSet;
use shorekeeper_protocol::RoleFormationData; use shorekeeper_protocol::RoleFormationData;
pub struct RoleFormation { pub struct RoleFormation {
pub id: i32, pub id: i32,
pub cur_role: i32, pub cur_role: i32,
pub role_ids: Vec<i32>, pub role_id_set: HashSet<i32>,
pub is_current: bool, pub is_current: bool,
} }
@ -12,7 +14,7 @@ impl RoleFormation {
Self { Self {
id: data.formation_id, id: data.formation_id,
cur_role: data.cur_role, cur_role: data.cur_role,
role_ids: data.role_id_list, role_id_set: data.role_id_list.into_iter().collect(),
is_current: data.is_current, is_current: data.is_current,
} }
} }
@ -21,7 +23,7 @@ impl RoleFormation {
RoleFormationData { RoleFormationData {
formation_id: self.id, formation_id: self.id,
cur_role: self.cur_role, cur_role: self.cur_role,
role_id_list: self.role_ids.iter().map(|&role_id| role_id).collect(), role_id_list: self.role_id_set.iter().cloned().collect(),
is_current: self.is_current, is_current: self.is_current,
} }
} }

View file

@ -1,12 +1,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::logic::utils::load_role_info::load_key_value;
use common::time_util; use common::time_util;
pub use formation::RoleFormation; use shorekeeper_data::role_info_data;
use shorekeeper_data::{base_property_data, role_info_data};
use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo}; use shorekeeper_protocol::{ArrayIntInt, RoleData, RoleInfo};
mod formation; mod formation;
pub use formation::RoleFormation;
pub struct Role { pub struct Role {
pub role_id: i32, pub role_id: i32,
pub name: String, pub name: String,
@ -17,7 +17,6 @@ pub struct Role {
pub star: i32, pub star: i32,
pub favor: i32, pub favor: i32,
pub create_time: u32, pub create_time: u32,
pub equip_weapon: i32,
} }
impl Role { impl Role {
@ -37,17 +36,10 @@ impl Role {
star: 0, star: 0,
favor: 0, favor: 0,
create_time: time_util::unix_timestamp() as u32, create_time: time_util::unix_timestamp() as u32,
equip_weapon: data.init_weapon_item_id,
} }
} }
pub fn to_protobuf(&self) -> RoleInfo { 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 { RoleInfo {
role_id: self.role_id, role_id: self.role_id,
name: self.name.clone(), name: self.name.clone(),
@ -62,17 +54,11 @@ impl Role {
.collect(), .collect(),
star: self.star, star: self.star,
favor: self.favor, favor: self.favor,
base_prop: base_prop
.iter()
.map(|(&k, &v)| ArrayIntInt { key: k, value: v })
.collect(),
..Default::default() ..Default::default()
} }
} }
pub fn load_from_save(data: RoleData) -> (i32, Self) { pub fn load_from_save(data: RoleData) -> Self {
(
data.role_id,
Self { Self {
role_id: data.role_id, role_id: data.role_id,
name: data.name, name: data.name,
@ -83,9 +69,7 @@ impl Role {
star: data.star, star: data.star,
favor: data.favor, favor: data.favor,
create_time: data.create_time, create_time: data.create_time,
equip_weapon: data.equip_weapon, }
},
)
} }
pub fn build_save_data(&self) -> RoleData { pub fn build_save_data(&self) -> RoleData {
@ -99,7 +83,6 @@ impl Role {
star: self.star, star: self.star,
favor: self.favor, favor: self.favor,
create_time: self.create_time, create_time: self.create_time,
equip_weapon: self.equip_weapon,
..Default::default() ..Default::default()
} }
} }

View file

@ -15,9 +15,8 @@ pub(super) struct MovementSystem;
impl System for MovementSystem { impl System for MovementSystem {
fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) { fn tick(&self, world: &mut World, players: &mut [RefMut<Player>]) {
let mut notify = MovePackageNotify::default(); let mut notify = MovePackageNotify::default();
let world_entity = world.get_world_entity();
for (entity, mut movement, mut position) in query_with!(world_entity, Movement, Position) { for (entity, mut movement, mut position) in query_with!(world, Movement, Position) {
if movement.pending_movement_vec.is_empty() { if movement.pending_movement_vec.is_empty() {
continue; continue;
} }
@ -47,12 +46,9 @@ impl System for MovementSystem {
notify.moving_entities.push(moving_entity_data); notify.moving_entities.push(moving_entity_data);
if let (Some(_), Some(owner)) = query_components!( if let (Some(_), Some(owner)) =
world_entity, query_components!(world, i64::from(entity), PlayerEntityMarker, OwnerPlayer)
i64::from(entity), {
PlayerEntityMarker,
OwnerPlayer
) {
if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) { if let Some(player) = players.iter_mut().find(|pl| pl.basic_info.id == owner.0) {
player.location.position = position.0.clone(); player.location.position = position.0.clone();
} }

View file

@ -1,10 +1,3 @@
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::{ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
@ -17,13 +10,16 @@ use std::{
time::Duration, time::Duration,
}; };
use super::{ecs::world::World, player::Player, utils::world_util}; use common::time_util;
use crate::logic::ecs::world::WorldEntity; use shorekeeper_protocol::{message::Message, JoinSceneNotify, TransitionOptionPb};
use crate::{logic, player_save_task::{self, PlayerSaveReason}, session::Session}; use shorekeeper_protocol::{AfterJoinSceneNotify, EnterGameResponse, PlayerSaveData};
const WATER_MASK: &str = include_str!("../../watermask-rr.js"); use crate::{
const UID_FIX: &str = include_str!("../../uidfix.js"); player_save_task::{self, PlayerSaveReason},
const CENSORSHIP_FIX: &str = include_str!("../../censorshipfix.js"); session::Session,
};
use super::{ecs::world::World, player::Player, utils::world_util};
pub enum LogicInput { pub enum LogicInput {
AddPlayer { AddPlayer {
@ -109,7 +105,8 @@ fn logic_thread_func(receiver: mpsc::Receiver<LogicInput>, load: Arc<AtomicUsize
let mut world = world.borrow_mut(); let mut world = world.borrow_mut();
let mut players = world let mut players = world
.player_ids() .player_ids()
.flat_map(|id| state.players.get(id).map(|pl| pl.borrow_mut())) .map(|id| state.players.get(id).map(|pl| pl.borrow_mut()))
.flatten()
.collect::<Box<_>>(); .collect::<Box<_>>();
super::systems::tick_systems(&mut world, &mut players); super::systems::tick_systems(&mut world, &mut players);
@ -138,33 +135,16 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
session, session,
player_save_data, player_save_data,
} => { } => {
let (player, is_player) = if let Vacant(e) = state.players.entry(player_id) { let player = state
( .players
e.insert(RefCell::new(Player::load_from_save(player_save_data))), .entry(player_id)
true, .or_insert(RefCell::new(Player::load_from_save(player_save_data)));
)
} 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(); let mut player = player.borrow_mut();
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()); state.worlds.insert(player_id, player.world.clone());
}
player.init(); player.init();
player.set_session(session); player.set_session(session);
player.respond(EnterGameResponse::default(), enter_rpc_id);
player.notify_general_data(); player.notify_general_data();
player player
@ -172,41 +152,21 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
.borrow_mut() .borrow_mut()
.set_in_world_player_data(player.build_in_world_player()); .set_in_world_player_data(player.build_in_world_player());
world_util::add_player_entities(&player); world_util::add_player_entities(&mut player.world.borrow_mut(), &player);
let scene_info = world_util::build_scene_information(&player); let scene_info = world_util::build_scene_information(
&player.world.borrow(),
player.location.instance_id,
player.basic_info.id,
);
player.notify(JoinSceneNotify { player.notify(JoinSceneNotify {
scene_info: Some(scene_info),
max_entity_id: i64::MAX, max_entity_id: i64::MAX,
scene_info: Some(scene_info),
transition_option: Some(TransitionOptionPb::default()), transition_option: Some(TransitionOptionPb::default()),
}); });
player.respond(EnterGameResponse::default(), enter_rpc_id);
player.notify(AfterJoinSceneNotify::default()); 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); drop(player);
state state
@ -231,7 +191,6 @@ fn handle_logic_input(state: &mut LogicState, input: LogicInput) {
let _ = state.worlds.remove(&player_id); let _ = state.worlds.remove(&player_id);
// TODO: kick co-op players from removed world // TODO: kick co-op players from removed world
// TODO: Remove all entitie
player_save_task::push( player_save_task::push(
player_id, player_id,

View file

@ -1,75 +1,26 @@
use crate::logic::ecs::component::ComponentContainer; use crate::logic::ecs::component::ComponentContainer;
use shorekeeper_protocol::{EEntityType, EntityPb, PlayerSceneAoiData}; use shorekeeper_protocol::{EntityPb, PlayerSceneAoiData};
use std::collections::HashSet;
use crate::logic::components::Visibility; use crate::{logic::ecs::world::World, query_with};
use crate::logic::player::Player;
use crate::{modify_component, query_hn_with};
pub fn build_scene_add_on_init_data(player: &Player) -> PlayerSceneAoiData { pub fn build_scene_add_on_init_data(world: &World) -> PlayerSceneAoiData {
let mut world_ref = player.world.borrow_mut(); let entities = query_with!(world, PlayerEntityMarker)
let world = world_ref.get_mut_world_entity();
let entities = query_hn_with!(world, PlayerEntityMarker)
.into_iter() .into_iter()
.map(|(entity_id, _)| { .map(|(e, _)| e)
let res_map: (EEntityType, i32); .collect::<Vec<_>>();
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(); let mut aoi_data = PlayerSceneAoiData::default();
for entity in entities {
let mut pb = EntityPb::default();
pb.id = entity.into();
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
};
}
);
if world.get_entity(config_id).entity_type == EEntityType::Player as i32 {
let mut pb = EntityPb {
id: entity_id as i64,
..Default::default()
};
world world
.get_entity_components(entity_id) .get_entity_components(entity)
.into_iter() .into_iter()
.for_each(|comp| comp.set_pb_data(&mut pb)); .for_each(|comp| comp.set_pb_data(&mut pb));
aoi_data.entities.push(pb); aoi_data.entities.push(pb);
} }
}
_ => {}
};
});
aoi_data aoi_data
} }

View file

@ -1,158 +0,0 @@
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
);

View file

@ -1,4 +1,2 @@
pub mod entity_serializer; pub mod entity_serializer;
pub mod load_role_info;
pub mod world_util; pub mod world_util;
pub mod quadrant_util;

View file

@ -1,230 +0,0 @@
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
}
}

View file

@ -1,110 +1,33 @@
use std::cell::{BorrowMutError, RefMut}; use shorekeeper_data::base_property_data;
use shorekeeper_protocol::{
use shorekeeper_data::{base_property_data, LevelEntityConfigData}; EntityConfigType, FightRoleInfo, FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
use shorekeeper_protocol::{DFsm, EEntityType, EntityAddNotify, EntityConfigType, EntityPb, ScenePlayerInformation, SceneTimeInfo,
EntityRemoveInfo, EntityRemoveNotify, EntityState, FightRoleInfo,
FightRoleInfos, LivingStatus, SceneInformation, SceneMode,
ScenePlayerInformation, SceneTimeInfo};
use crate::logic::{
components::{
Attribute, EntityConfig, Equip, Movement, OwnerPlayer, PlayerEntityMarker, Position,
Visibility, VisionSkill,
},
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;
#[macro_export] use crate::{
macro_rules! create_player_entity_pb { logic::{
($role_list:expr, $cur_map_id:expr, $world:expr, $player_id:expr, $position:expr, $explore_tools:expr) => {{ components::{
let mut pbs = Vec::new(); Attribute, EntityConfig, Movement, OwnerPlayer, PlayerEntityMarker, Position,
Visibility,
},
ecs::{component::ComponentContainer, world::World},
player::Player,
},
query_with,
};
for role in $role_list { use super::entity_serializer;
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");
let entity = $world pub fn add_player_entities(world: &mut World, player: &Player) {
.create_entity(role_id, EEntityType::Player.into(), $cur_map_id) let cur_role_id = player.get_cur_role_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();
let mut pb = EntityPb { for role in player.get_current_formation_role_list() {
id: entity.entity_id as i64, let id = world
..Default::default() .create_entity()
};
$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::PlayerEntityMarker(PlayerEntityMarker))
.with(ComponentContainer::EntityConfig(EntityConfig { .with(ComponentContainer::EntityConfig(EntityConfig {
config_id: role.role_id, config_id: role.role_id,
config_type: EntityConfigType::Character, config_type: EntityConfigType::Character,
entity_type: EEntityType::Player.into(),
entity_state: EntityState::Default,
})) }))
.with(ComponentContainer::OwnerPlayer(OwnerPlayer( .with(ComponentContainer::OwnerPlayer(OwnerPlayer(
player.basic_info.id, player.basic_info.id,
@ -121,41 +44,33 @@ pub fn add_player_entities(player: &Player) {
.unwrap(), .unwrap(),
))) )))
.with(ComponentContainer::Movement(Movement::default())) .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(); .build();
tracing::debug!( tracing::debug!(
"created player entity, id: {}, role_id: {}", "created player entity, id: {}, role_id: {}",
entity.entity_id, i64::from(id),
role.role_id role.role_id
); );
} }
}
} }
pub fn build_scene_information(player: &Player) -> SceneInformation { pub fn build_scene_information(world: &World, instance_id: i32, owner_id: i32) -> SceneInformation {
SceneInformation { SceneInformation {
scene_id: String::new(), scene_id: String::new(),
instance_id: player.location.instance_id, instance_id,
owner_id: player.basic_info.id, owner_id,
dynamic_entity_list: Vec::new(), dynamic_entity_list: Vec::new(),
blackboard_params: Vec::new(), blackboard_params: Vec::new(),
end_time: 0, end_time: 0,
aoi_data: Some(entity_serializer::build_scene_add_on_init_data(player)), aoi_data: Some(entity_serializer::build_scene_add_on_init_data(world)),
player_infos: build_player_info_list(&player.world.borrow_mut()), player_infos: build_player_info_list(world),
mode: SceneMode::Single.into(), mode: SceneMode::Single.into(),
time_info: Some(SceneTimeInfo { time_info: Some(SceneTimeInfo {
owner_time_clock_time_span: 0, owner_time_clock_time_span: 0,
hour: 8, hour: 8,
minute: 0, minute: 0,
}), }),
cur_context_id: player.basic_info.id as i64, cur_context_id: owner_id as i64,
..Default::default() ..Default::default()
} }
} }
@ -164,31 +79,22 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
world world
.players() .players()
.map(|sp| { .map(|sp| {
let (cur_role_id, transform, _equip) = query_with!( let (cur_role_id, transform) = query_with!(
world.get_world_entity(), world,
PlayerEntityMarker, PlayerEntityMarker,
OwnerPlayer, OwnerPlayer,
Visibility, Visibility,
EntityConfig, EntityConfig,
Position, Position
Equip
) )
.into_iter() .into_iter()
.find_map(|(_, _, owner, visibility, conf, pos, equip)| { .find_map(|(_, _, owner, visibility, conf, pos)| {
(sp.player_id == owner.0 && visibility.0).then_some(( (sp.player_id == owner.0 && visibility.0).then_some((conf.config_id, pos.0.clone()))
conf.config_id,
pos.0.clone(),
equip.weapon_id,
))
}) })
.unwrap_or_default(); .unwrap_or_default();
let active_characters = query_with!( let active_characters =
world.get_world_entity(), query_with!(world, PlayerEntityMarker, OwnerPlayer, EntityConfig)
PlayerEntityMarker,
OwnerPlayer,
EntityConfig
)
.into_iter() .into_iter()
.filter(|(_, _, owner, _)| owner.0 == sp.player_id); .filter(|(_, _, owner, _)| owner.0 == sp.player_id);
@ -219,119 +125,3 @@ fn build_player_info_list(world: &World) -> Vec<ScenePlayerInformation> {
}) })
.collect() .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,
});
}
}

View file

@ -20,8 +20,7 @@ async fn main() -> Result<()> {
::common::splash::print_splash(); ::common::splash::print_splash();
::common::logging::init(::tracing::Level::DEBUG); ::common::logging::init(::tracing::Level::DEBUG);
shorekeeper_data::load_all_json_data("assets/logic/BinData")?; shorekeeper_data::load_json_data("assets/logic/json")?;
logic::utils::quadrant_util::initialize_quadrant_system();
let database = Arc::new(shorekeeper_database::connect_to(&CONFIG.database).await?); let database = Arc::new(shorekeeper_database::connect_to(&CONFIG.database).await?);
shorekeeper_database::run_migrations(database.as_ref()).await?; shorekeeper_database::run_migrations(database.as_ref()).await?;

View file

@ -1,9 +0,0 @@
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);

View file

@ -1,41 +0,0 @@
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();

View file

@ -867,7 +867,7 @@ impl<Output> Kcp<Output> {
} }
let mut ts_flush = self.ts_flush; let mut ts_flush = self.ts_flush;
let mut tm_packet = u32::MAX; let mut tm_packet = u32::max_value();
if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 { if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 {
ts_flush = current; ts_flush = current;
@ -1142,7 +1142,7 @@ impl<Output: Write> Kcp<Output> {
let resent = if self.fastresend > 0 { let resent = if self.fastresend > 0 {
self.fastresend self.fastresend
} else { } else {
u32::MAX u32::max_value()
}; };
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 }; 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 { let resent = if self.fastresend > 0 {
self.fastresend self.fastresend
} else { } else {
u32::MAX u32::max_value()
}; };
let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 }; let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 };

View file

@ -86,7 +86,7 @@ async fn on_login_request(
.flatten() .flatten()
else { else {
tracing::debug!("login: account '{}' not found", &request.account); tracing::debug!("login: account '{}' not found", &request.account);
response.error_code = ErrorCode::InvalidUserId.into(); response.code = ErrorCode::InvalidUserId.into();
return; return;
}; };
@ -100,7 +100,7 @@ async fn on_login_request(
&request.login_trace_id, &request.login_trace_id,
&account.user_id &account.user_id
); );
response.error_code = ErrorCode::LoginRetry.into(); response.code = ErrorCode::LoginRetry.into();
return; return;
} }
@ -113,7 +113,7 @@ async fn on_login_request(
ban_time_stamp, ban_time_stamp,
ban_time_stamp - cur_time_stamp ban_time_stamp - cur_time_stamp
); );
response.error_code = ErrorCode::AccountIsBlocked.into(); response.code = ErrorCode::AccountIsBlocked.into();
return; return;
} }
} }
@ -134,13 +134,13 @@ async fn on_login_request(
"login: first login on account {}, awaiting create character request", "login: first login on account {}, awaiting create character request",
&account.user_id &account.user_id
); );
response.error_code = ErrorCode::HaveNoCharacter.into(); response.code = ErrorCode::HaveNoCharacter.into();
return; return;
}; };
session.player_id = Some(player_id); session.player_id = Some(player_id);
response.error_code = ErrorCode::Success.into(); response.code = ErrorCode::Success.into();
response.timestamp = time_util::unix_timestamp_ms() as i64; response.timestamp = time_util::unix_timestamp() as i64;
tracing::info!( tracing::info!(
"login success, user_id: {}, player_id: {}", "login success, user_id: {}, player_id: {}",

View file

@ -155,11 +155,11 @@ impl Session {
fn next_message(&mut self) -> Option<Message> { fn next_message(&mut self) -> Option<Message> {
self.decoder.pop_with(|buf| { self.decoder.pop_with(|buf| {
Message::decode(buf) Message::decode(&buf)
.inspect_err(|err| { .inspect_err(|err| {
tracing::error!( tracing::error!(
"failed to decode a message, err: {err}, buf: {}", "failed to decode a message, err: {err}, buf: {}",
hex::encode(buf) hex::encode(&buf)
) )
}) })
.ok() .ok()

View file

@ -15,7 +15,8 @@ impl LengthFieldBasedDecoder {
pub fn input(&mut self, data: &[u8]) { pub fn input(&mut self, data: &[u8]) {
self.ensure_capacity(data.len()); self.ensure_capacity(data.len());
self.buffer[self.cur_index..self.cur_index + data.len()].copy_from_slice(data);
(&mut self.buffer[self.cur_index..self.cur_index + data.len()]).copy_from_slice(data);
self.cur_index += data.len(); self.cur_index += data.len();
} }

View file

@ -64,7 +64,7 @@ impl UdpServer {
conv_id, conv_id,
addr, addr,
self.socket.clone(), self.socket.clone(),
self.protokey_helper, &self.protokey_helper,
self.db.clone(), self.db.clone(),
); );
self.session_mgr.add(conv_id, session); self.session_mgr.add(conv_id, session);

View file

@ -26,12 +26,11 @@ async fn main() -> Result<()> {
LazyLock::new(|| config_util::load_or_create("hotpatch.toml")); LazyLock::new(|| config_util::load_or_create("hotpatch.toml"));
::common::splash::print_splash(); ::common::splash::print_splash();
::common::logging::init_axum(::tracing::Level::DEBUG); ::common::logging::init(::tracing::Level::DEBUG);
Application::new() Application::new()
.get("/:env/client/:hash/:platform/config.json", get_config) .get("/:env/client/:hash/:platform/config.json", get_config)
.with_encryption(&CONFIG.encryption) .with_encryption(&CONFIG.encryption)
.with_logger()
.serve(&CONFIG.network) .serve(&CONFIG.network)
.await?; .await?;

View file

@ -11,10 +11,13 @@ pub async fn handle_login_api_call(
tracing::debug!("login requested"); tracing::debug!("login requested");
let user_data = parameters.user_data; let user_data = parameters.user_data;
let result = login(&state, parameters).await.unwrap_or_else(|err| { let result = match login(&state, parameters).await {
Ok(result) => result,
Err(err) => {
tracing::warn!("login: internal error occurred {err:?}"); tracing::warn!("login: internal error occurred {err:?}");
schema::LoginResult::error(-1, String::from("Internal server error")) schema::LoginResult::error(-1, String::from("Internal server error"))
}); }
};
Json(result.with_user_data(user_data)) Json(result.with_user_data(user_data))
} }
@ -29,7 +32,7 @@ async fn login(state: &ServiceState, params: schema::LoginParameters) -> Result<
Some(account) => { Some(account) => {
if let Some(ban_time_stamp) = account.ban_time_stamp { if let Some(ban_time_stamp) = account.ban_time_stamp {
if time_util::unix_timestamp() < ban_time_stamp as u64 { if time_util::unix_timestamp() < ban_time_stamp as u64 {
return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp)); return Ok(schema::LoginResult::banned(String::from("You're banned MF"), ban_time_stamp as i64));
} }
} }

View file

@ -1,7 +1,6 @@
use std::{process, sync::LazyLock}; use std::{process, sync::LazyLock};
use anyhow::Result; use anyhow::Result;
use config::{GatewayConfig, ServerConfig}; use config::{GatewayConfig, ServerConfig};
use shorekeeper_database::PgPool; use shorekeeper_database::PgPool;
use shorekeeper_http::{Application, StatusCode}; use shorekeeper_http::{Application, StatusCode};
@ -22,7 +21,7 @@ async fn main() -> Result<()> {
LazyLock::new(|| ::common::config_util::load_or_create("loginserver.toml")); LazyLock::new(|| ::common::config_util::load_or_create("loginserver.toml"));
::common::splash::print_splash(); ::common::splash::print_splash();
::common::logging::init_axum(::tracing::Level::DEBUG); ::common::logging::init(::tracing::Level::DEBUG);
let Ok(pool) = shorekeeper_database::connect_to(&CONFIG.database).await else { let Ok(pool) = shorekeeper_database::connect_to(&CONFIG.database).await else {
tracing::error!( tracing::error!(
@ -40,7 +39,6 @@ async fn main() -> Result<()> {
}) })
.get("/health", || async { StatusCode::OK }) .get("/health", || async { StatusCode::OK })
.get("/api/login", handler::handle_login_api_call) .get("/api/login", handler::handle_login_api_call)
.with_logger()
.serve(&CONFIG.network) .serve(&CONFIG.network)
.await?; .await?;

Some files were not shown because too many files have changed in this diff Show more