Hi
This commit is contained in:
parent
393967e308
commit
24abc716d8
31 changed files with 2815 additions and 2 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
target = "armv7-linux-androideabi"
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
target/
|
951
Cargo.lock
generated
Normal file
951
Cargo.lock
generated
Normal file
|
@ -0,0 +1,951 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_log-sys"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"itertools",
|
||||
"log",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cesu8"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
"thiserror",
|
||||
"walkdir",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni-sys"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07d0e07885d6a754b9c7993f2625187ad694ee985d60f23355ff0e7077261502"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libproc"
|
||||
version = "0.14.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"errno",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libserver"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"libc",
|
||||
"proc-maps",
|
||||
"rand",
|
||||
"rbase64",
|
||||
"rusqlite",
|
||||
"tracing",
|
||||
"tracing-android",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbb8270bb4060bd76c6e96f20c52d80620f1d82a3470885694e41e0f81ef6fe7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99585191385958383e13f6b822e6b6d8d9cf928e7d286ceb092da92b43c87bc1"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||
dependencies = [
|
||||
"overload",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-maps"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3db44c5aa60e193a25fcd93bb9ed27423827e8f118897866f946e2cf936c44fb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen",
|
||||
"libc",
|
||||
"libproc",
|
||||
"mach2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rbase64"
|
||||
version = "2.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b133fdd52a7cbb7619c86d93c8a34ea6e056462f901e08f6cbb6c9baf138b13"
|
||||
dependencies = [
|
||||
"mimalloc",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e34486da88d8e051c7c0e23c3f15fd806ea8546260aa2fec247e97242ec143"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-android"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12612be8f868a09c0ceae7113ff26afe79d81a24473a393cb9120ece162e86c0"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
20
Cargo.toml
Normal file
20
Cargo.toml
Normal file
|
@ -0,0 +1,20 @@
|
|||
[workspace]
|
||||
members = ["libserver"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
|
||||
[workspace.dependencies]
|
||||
jni = "0.21.1"
|
||||
tracing = "0.1.41"
|
||||
tracing-android = "0.2.0"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
||||
rand = "0.9"
|
||||
rusqlite = { version = "0.34.0", features = ["bundled"] }
|
||||
rbase64 = "2.0.3"
|
||||
|
||||
proc-maps = "0.4.0"
|
||||
libc = "0.2.171"
|
50
README.md
50
README.md
|
@ -1,3 +1,49 @@
|
|||
# Magic
|
||||
# magic-rs
|
||||
Experimental Clash of Clans server emulator on top of libg.so (v8.67.8)
|
||||

|
||||
|
||||
Experimental Clash of Clans server on top of libg.so
|
||||
# Why?
|
||||
Supercell's games have their game logic implementation included in both client and server, for independent execution (the `Logic*` family of classes is shared). However, to implement a feature-complete server emulator, you should rewrite the logic entirely. This experimental way is to use logic that is retained in the game, basically turning client into a server.
|
||||
|
||||
# Current features
|
||||
- Full home state emulation
|
||||
- NPC attacks
|
||||
- Player progress saving
|
||||
|
||||
# Implementation
|
||||
Server side code is written in Rust. We provide idiomatic bindings for the structures/functions from `libg.so`. Player data is being saved inside application data in form of SQLite database (for sake of simplicity).
|
||||
|
||||
# Getting started
|
||||
#### NOTE: you have to use a device with support of armeabi-v7a binaries
|
||||
### a) Using pre-built apk files
|
||||
Navigate to the [Releases](https://git.xeondev.com/Supercell/Magic/releases) page and download both Server and Client APK files (by default, they're targeted to the `127.0.0.1:9339` endpoint). Next, install both of them. Open the server application first (it should stay with black screen), then leave it running in the background. Open the client and play!
|
||||
### b) Building from sources
|
||||
#### Requirements:
|
||||
- [Rust 1.85+](https://www.rust-lang.org/tools/install)
|
||||
- [Android NDK](https://developer.android.com/ndk/downloads)
|
||||
- [cargo-ndk](https://docs.rs/crate/cargo-ndk/3.5.4)
|
||||
|
||||
#### Preparing the toolchain
|
||||
- Install android armv7 target via rustup:
|
||||
```sh
|
||||
rustup target add armv7-linux-androideabi
|
||||
```
|
||||
- Install cargo-ndk extension:
|
||||
```sh
|
||||
cargo install cargo-ndk
|
||||
```
|
||||
|
||||
##### NOTE: make sure you have configured the `ANDROID_NDK_HOME` environment variable before invoking build command.
|
||||
|
||||
#### Compiling server library
|
||||
```sh
|
||||
git clone https://git.xeondev.com/Supercell/Magic
|
||||
cd Magic
|
||||
cargo ndk -t armeabi-v7a build --release
|
||||
```
|
||||
|
||||
### Community
|
||||
[Our Discord Server](https://discord.gg/reversedrooms) is open for everyone who's interested in our projects!
|
||||
|
||||
### Support
|
||||
Your support for this project is greatly appreciated! If you'd like to contribute, feel free to send a tip [via Boosty](https://boosty.to/xeondev/donate)!
|
||||
|
|
22
libserver/Cargo.toml
Normal file
22
libserver/Cargo.toml
Normal file
|
@ -0,0 +1,22 @@
|
|||
[package]
|
||||
name = "libserver"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "server"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
jni.workspace = true
|
||||
|
||||
tracing.workspace = true
|
||||
tracing-android.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
||||
rand.workspace = true
|
||||
rusqlite.workspace = true
|
||||
rbase64.workspace = true
|
||||
|
||||
proc-maps.workspace = true
|
||||
libc.workspace = true
|
15
libserver/src/array_list.rs
Normal file
15
libserver/src/array_list.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
#[repr(C)]
|
||||
pub struct LogicArrayList<T> {
|
||||
pub data: *const T,
|
||||
pub _capacity: usize,
|
||||
pub count: usize,
|
||||
}
|
||||
|
||||
impl<T> LogicArrayList<T>
|
||||
where
|
||||
T: Sized,
|
||||
{
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
unsafe { std::slice::from_raw_parts(self.data, self.count) }
|
||||
}
|
||||
}
|
53
libserver/src/byte_stream.rs
Normal file
53
libserver/src/byte_stream.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use crate::{import, malloc};
|
||||
|
||||
pub struct ByteStream(pub *const u8);
|
||||
|
||||
impl ByteStream {
|
||||
pub fn new(initial_capacity: usize) -> Self {
|
||||
import!(byte_stream_ctor(ptr: *const u8, initial_capacity: i32) -> () = 0x1A0DA8 + 1);
|
||||
let instance = malloc(40);
|
||||
byte_stream_ctor(instance, initial_capacity as i32);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn get_byte_array(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let byte_array_ptr = *(self.0.wrapping_add(28) as *const *const u8);
|
||||
let length = self.get_length();
|
||||
|
||||
std::slice::from_raw_parts(byte_array_ptr, length as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_length(&self) -> i32 {
|
||||
unsafe {
|
||||
let offset = *(self.0.wrapping_add(16) as *const i32);
|
||||
let length = *(self.0.wrapping_add(20) as *const i32);
|
||||
std::cmp::max(offset, length)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_offset(&mut self) {
|
||||
unsafe { *(self.0.wrapping_add(16) as *mut i32) = 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for ByteStream
|
||||
where
|
||||
T: AsRef<[u8]>,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
let value = value.as_ref();
|
||||
let stream = ByteStream::new(value.len());
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(
|
||||
*(stream.0.wrapping_add(28) as *const *mut u8),
|
||||
value.len(),
|
||||
)
|
||||
.copy_from_slice(value);
|
||||
*(stream.0.wrapping_add(20) as *mut i32) = value.len() as i32;
|
||||
}
|
||||
|
||||
stream
|
||||
}
|
||||
}
|
124
libserver/src/database.rs
Normal file
124
libserver/src/database.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use rand::distr::{Alphanumeric, SampleString};
|
||||
use rusqlite::{params, Connection, Result};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
byte_stream::ByteStream, logic::avatar::LogicClientAvatar, math::LogicLong,
|
||||
resources::ResourceManager, sc_string::StringBuilder,
|
||||
};
|
||||
|
||||
pub struct DatabaseConnection(Connection);
|
||||
|
||||
pub struct PlayerSaveData {
|
||||
pub id: LogicLong,
|
||||
pub pass_token: String,
|
||||
pub home_json: String,
|
||||
pub client_avatar_blob: String,
|
||||
}
|
||||
|
||||
impl DatabaseConnection {
|
||||
pub fn connect(path: &str) -> Result<Self> {
|
||||
const INIT_QUERY: &str = r#"
|
||||
CREATE TABLE IF NOT EXISTS t_player_data (
|
||||
id INTEGER PRIMARY KEY,
|
||||
pass_token TEXT NOT NULL,
|
||||
home_json TEXT NOT NULL,
|
||||
client_avatar_blob TEXT NOT NULL,
|
||||
score INTEGER NOT NULL
|
||||
)
|
||||
"#;
|
||||
|
||||
let connection = rusqlite::Connection::open(path)?;
|
||||
connection.execute(INIT_QUERY, [])?;
|
||||
|
||||
Ok(Self(connection))
|
||||
}
|
||||
|
||||
pub fn fetch_or_create_player(&self, id: &LogicLong) -> Result<Option<PlayerSaveData>> {
|
||||
if id.is_zero() {
|
||||
Ok(Some(self.create_new_player_data()?))
|
||||
} else {
|
||||
self.load_existing_player_data(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_player_data(
|
||||
&self,
|
||||
id: &LogicLong,
|
||||
home_json: &str,
|
||||
avatar: &LogicClientAvatar,
|
||||
) -> Result<()> {
|
||||
const UPDATE_QUERY: &str =
|
||||
r#"UPDATE t_player_data SET home_json = ?1, client_avatar_blob = ?2 WHERE id = ?3"#;
|
||||
|
||||
let mut byte_stream = ByteStream::new(10);
|
||||
avatar.encode(&mut byte_stream);
|
||||
let client_avatar_blob = rbase64::encode(byte_stream.get_byte_array());
|
||||
|
||||
self.0.execute(
|
||||
UPDATE_QUERY,
|
||||
params![home_json, &client_avatar_blob, id.lower_int],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_new_player_data(&self) -> Result<PlayerSaveData> {
|
||||
const INSERT_QUERY: &str = r#"
|
||||
INSERT INTO t_player_data (pass_token, home_json, client_avatar_blob, score)
|
||||
values (?1, ?2, ?3, ?4) RETURNING *
|
||||
"#;
|
||||
|
||||
let pass_token = Alphanumeric.sample_string(&mut rand::rng(), 40);
|
||||
|
||||
let mut sb = StringBuilder::new();
|
||||
ResourceManager::get_json("level/starting_home.json").write_to_string(&mut sb);
|
||||
let home_json = sb.to_string();
|
||||
|
||||
let logic_client_avatar = LogicClientAvatar::get_default_avatar();
|
||||
let mut byte_stream = ByteStream::new(10);
|
||||
logic_client_avatar.encode(&mut byte_stream);
|
||||
let client_avatar_blob = rbase64::encode(byte_stream.get_byte_array());
|
||||
|
||||
let id: i32 = self
|
||||
.0
|
||||
.prepare(INSERT_QUERY)
|
||||
.inspect_err(|err| {
|
||||
error!("db::prepare `insert into t_player_data` failed: {err}");
|
||||
})?
|
||||
.query_map(
|
||||
params![&pass_token, &home_json, &client_avatar_blob, 0],
|
||||
|row| row.get(0),
|
||||
)?
|
||||
.next()
|
||||
.expect("query didn't return inserted data")?;
|
||||
|
||||
Ok(PlayerSaveData {
|
||||
id: LogicLong::new(0, id),
|
||||
pass_token,
|
||||
home_json,
|
||||
client_avatar_blob,
|
||||
})
|
||||
}
|
||||
|
||||
fn load_existing_player_data(&self, id: &LogicLong) -> Result<Option<PlayerSaveData>> {
|
||||
const SELECT_QUERY: &str = r#"SELECT * FROM t_player_data WHERE id = (?1)"#;
|
||||
|
||||
self.0
|
||||
.prepare(SELECT_QUERY)
|
||||
.inspect_err(|err| {
|
||||
error!("db::prepare `select from t_player_data` failed: {err}");
|
||||
})?
|
||||
.query_map(params![id.lower_int], |row| {
|
||||
Ok(PlayerSaveData {
|
||||
id: LogicLong::new(0, row.get(0)?),
|
||||
pass_token: row.get(1)?,
|
||||
home_json: row.get(2)?,
|
||||
client_avatar_blob: row.get(3)?,
|
||||
})
|
||||
})?
|
||||
.into_iter()
|
||||
.next()
|
||||
.transpose()
|
||||
}
|
||||
}
|
55
libserver/src/ffi_util.rs
Normal file
55
libserver/src/ffi_util.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use proc_maps::Pid;
|
||||
use std::{ffi::c_void, sync::LazyLock};
|
||||
|
||||
pub static LIBG_BASE: LazyLock<usize> = LazyLock::new(|| get_module_base("libg.so").unwrap());
|
||||
|
||||
macro_rules! import {
|
||||
($name:ident($($arg_name:ident: $arg_type:ty),*) -> $ret_type:ty = $rva:expr) => {
|
||||
pub fn $name($($arg_name: $arg_type,)*) -> $ret_type {
|
||||
unsafe {
|
||||
type FuncType = unsafe extern "C" fn($($arg_type,)*) -> $ret_type;
|
||||
::std::mem::transmute::<usize, FuncType>(*crate::ffi_util::LIBG_BASE + $rva)($($arg_name,)*)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn disable_event_tracker() {
|
||||
// Causes crashes in logic functions due to being not initialized
|
||||
// useless SC analytics.
|
||||
|
||||
const EVENT_TRACKER_FUNCTIONS: &[i32] = &[0x14BCC0, 0x14BA1C, 0x14BB4C, 0x14BA88, 0x1A39A0];
|
||||
const TRACK_FUNCTIONS: &[i32] = &[0x1A3E58, 0x14BE64, 0x14BC58];
|
||||
|
||||
unsafe {
|
||||
for &addr in EVENT_TRACKER_FUNCTIONS.iter().chain(TRACK_FUNCTIONS) {
|
||||
let page_size = libc::sysconf(libc::_SC_PAGE_SIZE);
|
||||
let addr = LIBG_BASE.wrapping_add(addr as usize);
|
||||
libc::mprotect(
|
||||
((addr as i32) & !(page_size - 1)) as *mut c_void,
|
||||
page_size as usize,
|
||||
libc::PROT_READ | libc::PROT_WRITE | libc::PROT_EXEC,
|
||||
);
|
||||
|
||||
std::slice::from_raw_parts_mut(addr as *mut u8, 2).copy_from_slice(&[0x70, 0x47]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use import;
|
||||
|
||||
pub fn get_module_base(shared_object_name: &str) -> Option<usize> {
|
||||
const ELF_MAGIC: u32 = 0x464C457F;
|
||||
|
||||
proc_maps::get_process_maps(std::process::id() as Pid)
|
||||
.ok()?
|
||||
.into_iter()
|
||||
.filter(|range| {
|
||||
range
|
||||
.filename()
|
||||
.map(|p| p.to_string_lossy().ends_with(shared_object_name))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.find(|range| unsafe { *(range.start() as *const u32) } == ELF_MAGIC)
|
||||
.map(|range| range.start())
|
||||
}
|
37
libserver/src/jni_util.rs
Normal file
37
libserver/src/jni_util.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use jni::JNIEnv;
|
||||
|
||||
pub fn get_package_name(mut env: JNIEnv) -> String {
|
||||
let activity_thread = env.find_class("android/app/ActivityThread").unwrap();
|
||||
let current_application = env
|
||||
.call_static_method(
|
||||
&activity_thread,
|
||||
"currentApplication",
|
||||
"()Landroid/app/Application;",
|
||||
&[],
|
||||
)
|
||||
.unwrap()
|
||||
.l()
|
||||
.unwrap();
|
||||
|
||||
let context = env
|
||||
.call_method(
|
||||
current_application,
|
||||
"getApplicationContext",
|
||||
"()Landroid/content/Context;",
|
||||
&[],
|
||||
)
|
||||
.unwrap()
|
||||
.l()
|
||||
.unwrap();
|
||||
|
||||
let package_name = env
|
||||
.call_method(context, "getPackageName", "()Ljava/lang/String;", &[])
|
||||
.unwrap()
|
||||
.l()
|
||||
.unwrap();
|
||||
|
||||
env.get_string(&package_name.into())
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
}
|
452
libserver/src/lib.rs
Normal file
452
libserver/src/lib.rs
Normal file
|
@ -0,0 +1,452 @@
|
|||
use std::{
|
||||
ffi::c_void,
|
||||
net::{SocketAddr, TcpListener},
|
||||
os::fd::IntoRawFd,
|
||||
sync::{Arc, LazyLock, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use byte_stream::ByteStream;
|
||||
use database::DatabaseConnection;
|
||||
use ffi_util::import;
|
||||
|
||||
use logic::avatar::*;
|
||||
use logic::home::LogicClientHome;
|
||||
use logic::json::LogicJSONNode;
|
||||
use logic::mode::LogicGameMode;
|
||||
|
||||
use math::LogicLong;
|
||||
use network::PiranhaMessage;
|
||||
use rand::RngCore;
|
||||
use resources::ResourceManager;
|
||||
use sc_string::StringBuilder;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
mod array_list;
|
||||
mod byte_stream;
|
||||
mod database;
|
||||
mod ffi_util;
|
||||
mod jni_util;
|
||||
mod logic;
|
||||
mod math;
|
||||
mod message;
|
||||
mod network;
|
||||
mod resources;
|
||||
mod sc_string;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn JNI_OnLoad(vm: jni::JavaVM, _: *mut c_void) -> jni::sys::jint {
|
||||
init_tracing();
|
||||
|
||||
let env = vm.get_env().unwrap();
|
||||
let package_name = jni_util::get_package_name(env);
|
||||
|
||||
info!("OnLoad()");
|
||||
info!("PackageName: {package_name}");
|
||||
|
||||
thread::spawn(move || {
|
||||
server_main(ServerConfig {
|
||||
database_path: format!("/data/data/{package_name}/magic.db"),
|
||||
})
|
||||
});
|
||||
|
||||
jni::sys::JNI_VERSION_1_6
|
||||
}
|
||||
|
||||
struct ServerConfig {
|
||||
pub database_path: String,
|
||||
}
|
||||
|
||||
import!(malloc(amount: usize) -> *const u8 = 0x56A20);
|
||||
import!(free(ptr: *const u8) -> () = 0x56A2C);
|
||||
|
||||
fn server_main(config: ServerConfig) {
|
||||
const TCP_ADDR: &str = "127.0.0.1:9339";
|
||||
|
||||
info!("starting server...");
|
||||
|
||||
ffi_util::disable_event_tracker();
|
||||
resources::init();
|
||||
|
||||
info!("successfully initialized resources");
|
||||
|
||||
let db = DatabaseConnection::connect(&config.database_path).unwrap_or_else(|err| {
|
||||
error!("DatabaseConnection::connect failed: {err}");
|
||||
panic!();
|
||||
});
|
||||
|
||||
let db = Arc::new(Mutex::new(db));
|
||||
|
||||
let listener = TcpListener::bind(TCP_ADDR).unwrap();
|
||||
info!("server is listening at {TCP_ADDR}");
|
||||
|
||||
while let Ok((stream, addr)) = listener.accept() {
|
||||
info!("new connection from {addr}");
|
||||
|
||||
let fd = stream.into_raw_fd();
|
||||
let db = Arc::clone(&db);
|
||||
|
||||
thread::spawn(move || receive_loop(fd, addr, db));
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_loop(fd: i32, addr: SocketAddr, db: Arc<Mutex<DatabaseConnection>>) {
|
||||
use network::{LogicMagicMessageFactory, Messaging, RC4Encrypter};
|
||||
|
||||
static MESSAGE_FACTORY: LazyLock<LogicMagicMessageFactory> =
|
||||
LazyLock::new(|| LogicMagicMessageFactory::new());
|
||||
|
||||
let mut messaging = Messaging::new(fd);
|
||||
messaging.set_message_factory(&*MESSAGE_FACTORY);
|
||||
messaging.set_encrypters(
|
||||
RC4Encrypter::new(LogicMagicMessageFactory::RC4_KEY, "nonce"),
|
||||
RC4Encrypter::new(LogicMagicMessageFactory::RC4_KEY, "nonce"),
|
||||
);
|
||||
|
||||
let mut session = PlayerSession {
|
||||
messaging,
|
||||
account_id: LogicLong::new(0, 0),
|
||||
logic_game_mode: None,
|
||||
saved_home_json: None,
|
||||
};
|
||||
|
||||
while session.messaging.get_connection().is_connected {
|
||||
session.messaging.on_receive();
|
||||
while let Some(message) = session.messaging.next_message() {
|
||||
handle_message(&mut session, db.as_ref(), message);
|
||||
}
|
||||
}
|
||||
|
||||
info!("client from {addr} disconnected");
|
||||
}
|
||||
|
||||
struct PlayerSession {
|
||||
pub messaging: network::Messaging,
|
||||
pub account_id: LogicLong,
|
||||
pub logic_game_mode: Option<LogicGameMode>,
|
||||
pub saved_home_json: Option<String>,
|
||||
}
|
||||
|
||||
fn handle_message(
|
||||
session: &mut PlayerSession,
|
||||
db: &Mutex<DatabaseConnection>,
|
||||
message: PiranhaMessage,
|
||||
) {
|
||||
match message.get_message_type() {
|
||||
10101 => handle_login_message(session, db, message),
|
||||
10108 => handle_keep_alive_message(session, message),
|
||||
10212 => handle_change_avatar_name_message(session, message),
|
||||
14101 => handle_go_home_message(session, message),
|
||||
14102 => handle_end_client_turn_message(session, db, message),
|
||||
14134 => handle_attack_npc_message(session, message),
|
||||
unhandled => warn!("unhandled message: {unhandled}"),
|
||||
}
|
||||
|
||||
session.messaging.on_wakeup();
|
||||
}
|
||||
|
||||
fn handle_login_message(
|
||||
session: &mut PlayerSession,
|
||||
db: &Mutex<DatabaseConnection>,
|
||||
message: PiranhaMessage,
|
||||
) {
|
||||
use message::{ExtendedSetEncryptionMessage, LoginMessage, LoginOkMessage, OwnHomeDataMessage};
|
||||
use network::{LogicMagicMessageFactory, RC4Encrypter};
|
||||
|
||||
let login_message = LoginMessage(message);
|
||||
|
||||
info!(
|
||||
"LoginMessage received, account_id: {}, pass_token: {:?}",
|
||||
login_message.get_account_id(),
|
||||
login_message.get_pass_token()
|
||||
);
|
||||
|
||||
let Ok(Some(player_data)) = db
|
||||
.lock()
|
||||
.unwrap()
|
||||
.fetch_or_create_player(&login_message.get_account_id())
|
||||
else {
|
||||
warn!(
|
||||
"Login: player with id {} was not found in the database",
|
||||
login_message.get_account_id()
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if !login_message.get_account_id().is_zero() {
|
||||
let Some(pass_token) = login_message.get_pass_token() else {
|
||||
error!(
|
||||
"Login: received null pass token with non-zero account id: {}",
|
||||
login_message.get_account_id()
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
if pass_token.to_string() != player_data.pass_token {
|
||||
warn!(
|
||||
"Login: pass token mismatch, account id: {}",
|
||||
login_message.get_account_id()
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut set_encryption_message = ExtendedSetEncryptionMessage::new();
|
||||
let mut nonce = [0u8; 64];
|
||||
rand::rng().fill_bytes(&mut nonce);
|
||||
set_encryption_message.set_nonce(&nonce);
|
||||
set_encryption_message.set_scrambler_method(1);
|
||||
session.messaging.send(set_encryption_message.0);
|
||||
session.messaging.on_wakeup();
|
||||
|
||||
network::Messaging::scramble_nonce_using_mersenne_twister(
|
||||
login_message.get_scrambler_seed(),
|
||||
&mut nonce,
|
||||
);
|
||||
|
||||
let encrypter = RC4Encrypter::new_with_nonce_bytes(LogicMagicMessageFactory::RC4_KEY, &nonce);
|
||||
let decrypter = RC4Encrypter::new_with_nonce_bytes(LogicMagicMessageFactory::RC4_KEY, &nonce);
|
||||
session.messaging.set_encrypters(encrypter, decrypter);
|
||||
|
||||
let mut login_ok_message = LoginOkMessage::new();
|
||||
login_ok_message.set_account_id(player_data.id.clone());
|
||||
login_ok_message.set_home_id(player_data.id.clone());
|
||||
login_ok_message.set_pass_token(&player_data.pass_token);
|
||||
login_ok_message.set_server_major_version(8);
|
||||
login_ok_message.set_server_build(67);
|
||||
login_ok_message.set_content_version(0);
|
||||
login_ok_message.set_server_environment("dev");
|
||||
|
||||
let mut own_home_data_message = OwnHomeDataMessage::new();
|
||||
|
||||
let mut logic_client_home = LogicClientHome::new();
|
||||
logic_client_home.set_home_json(&player_data.home_json);
|
||||
|
||||
let mut logic_client_avatar = LogicClientAvatar::new();
|
||||
|
||||
let data = rbase64::decode(&player_data.client_avatar_blob).unwrap();
|
||||
let mut byte_stream = ByteStream::from(&data);
|
||||
logic_client_avatar.decode(&mut byte_stream);
|
||||
|
||||
let mut logic_game_mode = LogicGameMode::new();
|
||||
logic_game_mode.load_home_state(&logic_client_home, &logic_client_avatar, 0);
|
||||
|
||||
own_home_data_message.set_logic_client_home({
|
||||
let mut logic_client_home = LogicClientHome::new();
|
||||
logic_client_home.set_home_json(&player_data.home_json);
|
||||
logic_client_home
|
||||
});
|
||||
|
||||
own_home_data_message.set_logic_client_avatar(logic_game_mode.get_cloned_home_owner().unwrap());
|
||||
|
||||
session.account_id = player_data.id;
|
||||
session.logic_game_mode = Some(logic_game_mode);
|
||||
session.saved_home_json = Some(player_data.home_json);
|
||||
|
||||
session.messaging.send(login_ok_message.0);
|
||||
session.messaging.send(own_home_data_message.0);
|
||||
|
||||
info!("successfully logged in");
|
||||
}
|
||||
|
||||
fn handle_keep_alive_message(session: &mut PlayerSession, _message: PiranhaMessage) {
|
||||
session
|
||||
.messaging
|
||||
.send(message::KeepAliveServerMessage::new().0);
|
||||
}
|
||||
|
||||
fn handle_go_home_message(session: &mut PlayerSession, _message: PiranhaMessage) {
|
||||
use message::OwnHomeDataMessage;
|
||||
|
||||
let Some(logic_game_mode) = session.logic_game_mode.as_mut() else {
|
||||
error!("received GoHomeMessage while LogicGameMode is NULL!");
|
||||
return;
|
||||
};
|
||||
|
||||
if logic_game_mode.get_state() == 1 {
|
||||
error!("received GoHomeMessage being already in home state!");
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(home_json) = session.saved_home_json.as_ref() else {
|
||||
error!("received GoHomeMessage while saved_home_json is NULL!");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut own_home_data_message = OwnHomeDataMessage::new();
|
||||
|
||||
let mut logic_client_home = LogicClientHome::new();
|
||||
logic_client_home.set_home_json(home_json);
|
||||
|
||||
let logic_client_avatar = logic_game_mode.get_cloned_visitor().unwrap();
|
||||
|
||||
let mut logic_game_mode = LogicGameMode::new();
|
||||
logic_game_mode.load_home_state(&logic_client_home, &logic_client_avatar, 0);
|
||||
|
||||
own_home_data_message.set_logic_client_home({
|
||||
let mut logic_client_home = LogicClientHome::new();
|
||||
logic_client_home.set_home_json(home_json);
|
||||
logic_client_home
|
||||
});
|
||||
|
||||
own_home_data_message.set_logic_client_avatar(logic_game_mode.get_cloned_home_owner().unwrap());
|
||||
|
||||
session.logic_game_mode = Some(logic_game_mode);
|
||||
session.messaging.send(own_home_data_message.0);
|
||||
}
|
||||
|
||||
fn handle_end_client_turn_message(
|
||||
session: &mut PlayerSession,
|
||||
db: &Mutex<DatabaseConnection>,
|
||||
message: PiranhaMessage,
|
||||
) {
|
||||
use message::{EndClientTurnMessage, OutOfSyncMessage};
|
||||
|
||||
let message = EndClientTurnMessage(message);
|
||||
|
||||
let Some(logic_game_mode) = session.logic_game_mode.as_mut() else {
|
||||
error!("received EndClientTurnMessage while LogicGameMode is NULL!");
|
||||
return;
|
||||
};
|
||||
|
||||
info!(
|
||||
"EndClientTurnMessage received: sub_tick: {}, checksum: {}",
|
||||
message.get_sub_tick(),
|
||||
message.get_checksum()
|
||||
);
|
||||
|
||||
let client_sub_tick = message.get_sub_tick();
|
||||
while logic_game_mode.get_level().get_time().sub_tick < client_sub_tick {
|
||||
if let Some(commands) = message.get_commands() {
|
||||
let cur_sub_tick = logic_game_mode.get_level().get_time().sub_tick;
|
||||
for command in commands.as_slice().iter() {
|
||||
if command.get_execute_sub_tick() == cur_sub_tick {
|
||||
info!(
|
||||
"received command: {}, exec sub tick: {}",
|
||||
command.get_command_type(),
|
||||
command.get_execute_sub_tick()
|
||||
);
|
||||
|
||||
logic_game_mode.get_command_manager().add_command(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logic_game_mode.update_one_sub_tick();
|
||||
}
|
||||
|
||||
let mut debug_json = LogicJSONNode::new_json_object();
|
||||
let checksum = logic_game_mode.calculate_checksum(Some(&mut debug_json), false);
|
||||
if checksum != message.get_checksum() {
|
||||
error!("Client and server are out of sync! sub_tick: {}, server checksum: {}, client checksum: {}", message.get_sub_tick(), checksum, message.get_checksum());
|
||||
|
||||
let mut sb = StringBuilder::new();
|
||||
debug_json.write_to_string(&mut sb);
|
||||
info!("{}", sb.to_string());
|
||||
|
||||
let mut out_of_sync_message = OutOfSyncMessage::new();
|
||||
out_of_sync_message.set_server_checksum(checksum);
|
||||
out_of_sync_message.set_client_checksum(message.get_checksum());
|
||||
out_of_sync_message.set_sub_tick(message.get_sub_tick());
|
||||
session.messaging.send(out_of_sync_message.0);
|
||||
}
|
||||
|
||||
if logic_game_mode.get_state() == 1 {
|
||||
let mut string_builder = StringBuilder::new();
|
||||
let mut home_json_object = LogicJSONNode::new_json_object();
|
||||
|
||||
logic_game_mode.save_to_json(&mut home_json_object);
|
||||
home_json_object.write_to_string(&mut string_builder);
|
||||
|
||||
let home_json = string_builder.to_string();
|
||||
|
||||
if let Err(err) = db.lock().unwrap().save_player_data(
|
||||
&session.account_id,
|
||||
&home_json,
|
||||
&logic_game_mode.get_cloned_home_owner().unwrap(),
|
||||
) {
|
||||
error!("failed to save player data: {err}");
|
||||
}
|
||||
|
||||
session.saved_home_json = Some(home_json);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_attack_npc_message(session: &mut PlayerSession, message: PiranhaMessage) {
|
||||
use message::{AttackNpcMessage, NpcDataMessage};
|
||||
|
||||
let message = AttackNpcMessage(message);
|
||||
|
||||
let Some(logic_game_mode) = session.logic_game_mode.as_mut() else {
|
||||
error!("received AttackNpcMessage while LogicGameMode is NULL!");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(home_owner_avatar) = logic_game_mode.get_level().get_home_owner_avatar() else {
|
||||
error!("received AttackNpcMessage while home_owner_avatar is NULL!");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut string_builder = StringBuilder::new();
|
||||
ResourceManager::get_json(
|
||||
&message
|
||||
.get_npc_data()
|
||||
.get_level_json_file_name()
|
||||
.to_string(),
|
||||
)
|
||||
.write_to_string(&mut string_builder);
|
||||
|
||||
let mut logic_client_home = LogicClientHome::new();
|
||||
logic_client_home.set_home_json(&string_builder.to_string());
|
||||
|
||||
let mut logic_npc_avatar = LogicNpcAvatar::new();
|
||||
logic_npc_avatar.set_npc_data(&message.get_npc_data());
|
||||
|
||||
let mut logic_game_mode = LogicGameMode::new();
|
||||
logic_game_mode.load_npc_attack_state(
|
||||
&logic_client_home,
|
||||
&logic_npc_avatar,
|
||||
&home_owner_avatar,
|
||||
0,
|
||||
);
|
||||
|
||||
let mut npc_data_message = NpcDataMessage::new();
|
||||
npc_data_message.set_level_json(&string_builder.to_string());
|
||||
npc_data_message.set_logic_npc_avatar(&logic_game_mode.get_cloned_home_owner().unwrap());
|
||||
npc_data_message.set_logic_client_avatar(&logic_game_mode.get_cloned_visitor().unwrap());
|
||||
|
||||
session.logic_game_mode = Some(logic_game_mode);
|
||||
session.messaging.send(npc_data_message.0);
|
||||
}
|
||||
|
||||
fn handle_change_avatar_name_message(session: &mut PlayerSession, message: PiranhaMessage) {
|
||||
use logic::command::LogicChangeAvatarNameCommand;
|
||||
use message::{AvailableServerCommandMessage, ChangeAvatarNameMessage};
|
||||
|
||||
let message = ChangeAvatarNameMessage(message);
|
||||
|
||||
let mut logic_change_avatar_name_command = LogicChangeAvatarNameCommand::new();
|
||||
logic_change_avatar_name_command.set_avatar_name(&message.get_avatar_name());
|
||||
logic_change_avatar_name_command.set_name_change_state(1);
|
||||
|
||||
let mut available_server_command_message = AvailableServerCommandMessage::new();
|
||||
available_server_command_message.set_server_command(&logic_change_avatar_name_command.0);
|
||||
|
||||
session.messaging.send(available_server_command_message.0);
|
||||
}
|
||||
|
||||
fn init_tracing() {
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(tracing_android::layer("MAGIC-SERVER").unwrap())
|
||||
.with(
|
||||
tracing_subscriber::EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy(),
|
||||
)
|
||||
.init();
|
||||
}
|
112
libserver/src/logic/avatar.rs
Normal file
112
libserver/src/logic/avatar.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use crate::{byte_stream::ByteStream, import, malloc};
|
||||
|
||||
use super::data::LogicNpcData;
|
||||
|
||||
pub trait LogicAvatar: Sized {
|
||||
fn new_from_ptr(ptr: *const u8) -> Self;
|
||||
fn new() -> Self;
|
||||
fn decode(&mut self, stream: &mut ByteStream);
|
||||
fn encode(&self, stream: &mut ByteStream);
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicClientAvatar(pub *const u8);
|
||||
|
||||
impl LogicClientAvatar {
|
||||
pub fn new() -> Self {
|
||||
import!(logic_client_avatar_ctor(ptr: *const u8) -> () = 0x1164A4 + 1);
|
||||
let instance = malloc(296);
|
||||
logic_client_avatar_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn get_default_avatar() -> Self {
|
||||
import!(logic_avatar_set_resource_count(ptr: *const u8, data: *const u8, count: i32) -> () = 0x114E7C + 1);
|
||||
import!(logic_data_tables_get_gold_data() -> *const u8 = 0x12D4F8 + 1);
|
||||
import!(logic_data_tables_get_elixir_data() -> *const u8 = 0x12D510 + 1);
|
||||
|
||||
let avatar = Self::new();
|
||||
logic_avatar_set_resource_count(avatar.0, logic_data_tables_get_gold_data(), 750);
|
||||
logic_avatar_set_resource_count(avatar.0, logic_data_tables_get_elixir_data(), 750);
|
||||
|
||||
unsafe {
|
||||
*(avatar.0.wrapping_add(96) as *mut i32) = 1; // level
|
||||
*(avatar.0.wrapping_add(208) as *mut i32) = 100_000_000; // diamonds
|
||||
}
|
||||
|
||||
avatar
|
||||
}
|
||||
|
||||
pub fn decode(&mut self, stream: &mut ByteStream) {
|
||||
import!(logic_client_avatar_decode(ptr: *const u8, s: *const u8) -> () = 0x1174AC + 1);
|
||||
logic_client_avatar_decode(self.0, stream.0);
|
||||
}
|
||||
|
||||
pub fn encode(&self, stream: &mut ByteStream) {
|
||||
import!(logic_client_avatar_encode(ptr: *const u8, s: *const u8) -> () = 0x115D24 + 1);
|
||||
logic_client_avatar_encode(self.0, stream.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicNpcAvatar(pub *const u8);
|
||||
|
||||
impl LogicNpcAvatar {
|
||||
pub fn new() -> Self {
|
||||
import!(logic_npc_avatar_ctor(ptr: *const u8) -> () = 0x117FE4 + 1);
|
||||
let instance = malloc(112);
|
||||
logic_npc_avatar_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn set_npc_data(&mut self, data: &LogicNpcData) {
|
||||
import!(logic_npc_avatar_set_npc_data(ptr: *const u8, data: *const u8) -> () = 0x118008 + 1);
|
||||
logic_npc_avatar_set_npc_data(self.0, data.0);
|
||||
}
|
||||
|
||||
pub fn decode(&mut self, stream: &mut ByteStream) {
|
||||
import!(logic_npc_avatar_decode(ptr: *const u8, s: *const u8) -> () = 0x1180E4 + 1);
|
||||
logic_npc_avatar_decode(self.0, stream.0);
|
||||
}
|
||||
|
||||
pub fn encode(&self, stream: &mut ByteStream) {
|
||||
import!(logic_npc_avatar_encode(ptr: *const u8, s: *const u8) -> () = 0x117FB8 + 1);
|
||||
logic_npc_avatar_encode(self.0, stream.0);
|
||||
}
|
||||
}
|
||||
|
||||
impl LogicAvatar for LogicClientAvatar {
|
||||
fn new_from_ptr(ptr: *const u8) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn decode(&mut self, stream: &mut ByteStream) {
|
||||
self.decode(stream);
|
||||
}
|
||||
|
||||
fn encode(&self, stream: &mut ByteStream) {
|
||||
self.encode(stream);
|
||||
}
|
||||
}
|
||||
|
||||
impl LogicAvatar for LogicNpcAvatar {
|
||||
fn new_from_ptr(ptr: *const u8) -> Self {
|
||||
Self(ptr)
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
fn decode(&mut self, stream: &mut ByteStream) {
|
||||
self.decode(stream);
|
||||
}
|
||||
|
||||
fn encode(&self, stream: &mut ByteStream) {
|
||||
self.encode(stream);
|
||||
}
|
||||
}
|
53
libserver/src/logic/command.rs
Normal file
53
libserver/src/logic/command.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
use crate::{import, malloc, sc_string::ScString};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicCommand(pub *const u8);
|
||||
|
||||
impl LogicCommand {
|
||||
pub fn get_command_type(&self) -> i32 {
|
||||
let get_command_type = unsafe {
|
||||
std::mem::transmute::<_, extern "C" fn(*const u8) -> i32>(
|
||||
*((*(self.0 as *const usize) + 16) as *const usize),
|
||||
)
|
||||
};
|
||||
|
||||
get_command_type(self.0)
|
||||
}
|
||||
|
||||
pub fn get_execute_sub_tick(&self) -> i32 {
|
||||
unsafe { *(self.0.wrapping_add(4) as *const i32) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicCommandManager(pub *const u8);
|
||||
|
||||
impl LogicCommandManager {
|
||||
pub fn add_command(&self, command: &LogicCommand) {
|
||||
import!(logic_command_manager_add_command(ptr: *const u8, command: *const u8) -> () = 0x11CA20 + 1);
|
||||
logic_command_manager_add_command(self.0, command.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LogicChangeAvatarNameCommand(pub LogicCommand);
|
||||
|
||||
impl LogicChangeAvatarNameCommand {
|
||||
pub fn new() -> Self {
|
||||
import!(logic_change_avatar_name_command_ctor(ptr: *const u8) -> () = 0x14F7D8 + 1);
|
||||
let instance = malloc(20);
|
||||
logic_change_avatar_name_command_ctor(instance);
|
||||
Self(LogicCommand(instance))
|
||||
}
|
||||
|
||||
pub fn set_avatar_name(&mut self, name: &str) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(12) as *mut usize) = ScString::from(name).0 as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_name_change_state(&mut self, value: i32) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(16) as *mut i32) = value;
|
||||
}
|
||||
}
|
||||
}
|
3
libserver/src/logic/data/mod.rs
Normal file
3
libserver/src/logic/data/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod npc;
|
||||
|
||||
pub use npc::LogicNpcData;
|
9
libserver/src/logic/data/npc.rs
Normal file
9
libserver/src/logic/data/npc.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use crate::sc_string::ScString;
|
||||
|
||||
pub struct LogicNpcData(pub *const u8);
|
||||
|
||||
impl LogicNpcData {
|
||||
pub fn get_level_json_file_name(&self) -> ScString {
|
||||
ScString(self.0.wrapping_add(144) as *const u8)
|
||||
}
|
||||
}
|
20
libserver/src/logic/home.rs
Normal file
20
libserver/src/logic/home.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use crate::{import, malloc, sc_string::ScString};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicClientHome(pub *const u8);
|
||||
|
||||
impl LogicClientHome {
|
||||
pub fn new() -> Self {
|
||||
import!(logic_client_home_ctor(ptr: *const u8) -> () = 0x144900 + 1);
|
||||
|
||||
let instance = malloc(48);
|
||||
logic_client_home_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn set_home_json(&mut self, home_json: &str) {
|
||||
unsafe {
|
||||
*(self.0.wrapping_add(36) as *mut usize) = ScString::from(home_json).0 as usize;
|
||||
}
|
||||
}
|
||||
}
|
22
libserver/src/logic/json.rs
Normal file
22
libserver/src/logic/json.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use crate::{import, malloc, sc_string::StringBuilder};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicJSONNode(pub *const u8);
|
||||
|
||||
impl LogicJSONNode {
|
||||
pub fn new_json_object() -> Self {
|
||||
import!(logic_json_object_ctor(ptr: *const u8) -> () = 0x1A1DF0 + 1);
|
||||
let instance = malloc(28);
|
||||
logic_json_object_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn write_to_string(&self, string_builder: &mut StringBuilder) {
|
||||
let write_to_string = unsafe {
|
||||
std::mem::transmute::<_, extern "C" fn(*const u8, *const u8)>(
|
||||
*((*(self.0 as *const usize) + 16) as *const usize),
|
||||
)
|
||||
};
|
||||
write_to_string(self.0, string_builder.0);
|
||||
}
|
||||
}
|
31
libserver/src/logic/level.rs
Normal file
31
libserver/src/logic/level.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use crate::import;
|
||||
|
||||
use super::{avatar::LogicAvatar, json::LogicJSONNode, time::LogicTime};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicLevel(pub *const u8);
|
||||
|
||||
impl LogicLevel {
|
||||
pub fn get_time(&self) -> &LogicTime {
|
||||
unsafe { &*(self.0.wrapping_add(88) as *const LogicTime) }
|
||||
}
|
||||
|
||||
pub fn get_home_owner_avatar<T: LogicAvatar>(&self) -> Option<T> {
|
||||
unsafe {
|
||||
let ptr = *(self.0.wrapping_add(76) as *const *const u8);
|
||||
(!ptr.is_null()).then_some(T::new_from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_visitor_avatar<T: LogicAvatar>(&self) -> Option<T> {
|
||||
unsafe {
|
||||
let ptr = *(self.0.wrapping_add(80) as *const *const u8);
|
||||
(!ptr.is_null()).then_some(T::new_from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save_to_json(&self, json: &mut LogicJSONNode) {
|
||||
import!(logic_level_save_to_json(ptr: *const u8, json: *const u8) -> () = 0x147A48 + 1);
|
||||
logic_level_save_to_json(self.0, json.0);
|
||||
}
|
||||
}
|
8
libserver/src/logic/mod.rs
Normal file
8
libserver/src/logic/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
pub mod avatar;
|
||||
pub mod command;
|
||||
pub mod data;
|
||||
pub mod home;
|
||||
pub mod json;
|
||||
pub mod level;
|
||||
pub mod mode;
|
||||
pub mod time;
|
116
libserver/src/logic/mode.rs
Normal file
116
libserver/src/logic/mode.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use crate::{byte_stream::ByteStream, import, malloc};
|
||||
|
||||
use super::{
|
||||
avatar::{LogicAvatar, LogicClientAvatar, LogicNpcAvatar},
|
||||
command::LogicCommandManager,
|
||||
home::LogicClientHome,
|
||||
json::LogicJSONNode,
|
||||
level::LogicLevel,
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct LogicGameMode(pub *const u8);
|
||||
|
||||
impl LogicGameMode {
|
||||
pub fn new() -> Self {
|
||||
import!(logic_game_mode_ctor(ptr: *const u8) -> () = 0x149E0C + 1);
|
||||
let instance = malloc(68);
|
||||
logic_game_mode_ctor(instance);
|
||||
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn load_home_state(
|
||||
&mut self,
|
||||
logic_client_home: &LogicClientHome,
|
||||
logic_client_avatar: &LogicClientAvatar,
|
||||
seconds_since_last_save: i32,
|
||||
) {
|
||||
import!(logic_game_mode_load_home_state(lgm: *const u8, lch: *const u8, lca: *const u8, ssls: i32, a5: i32, a6: i32, a7: i32) -> () = 0x14ACCA + 1);
|
||||
|
||||
logic_game_mode_load_home_state(
|
||||
self.0,
|
||||
logic_client_home.0,
|
||||
logic_client_avatar.0,
|
||||
seconds_since_last_save,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn load_npc_attack_state(
|
||||
&mut self,
|
||||
logic_client_home: &LogicClientHome,
|
||||
logic_npc_avatar: &LogicNpcAvatar,
|
||||
logic_client_avatar: &LogicClientAvatar,
|
||||
seconds_since_last_save: i32,
|
||||
) {
|
||||
import!(logic_game_mode_load_npc_attack_state(lgm: *const u8, lch: *const u8, lna: *const u8, lca: *const u8, ssls: i32) -> () = 0x14A1D8 + 1);
|
||||
|
||||
logic_game_mode_load_npc_attack_state(
|
||||
self.0,
|
||||
logic_client_home.0,
|
||||
logic_npc_avatar.0,
|
||||
logic_client_avatar.0,
|
||||
seconds_since_last_save,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_cloned_home_owner<T: LogicAvatar>(&self) -> Option<T> {
|
||||
let avatar = self.get_level().get_home_owner_avatar::<T>()?;
|
||||
let mut stream = ByteStream::new(10);
|
||||
LogicAvatar::encode(&avatar, &mut stream);
|
||||
stream.reset_offset();
|
||||
let mut cloned_avatar = T::new();
|
||||
LogicAvatar::decode(&mut cloned_avatar, &mut stream);
|
||||
|
||||
Some(cloned_avatar)
|
||||
}
|
||||
|
||||
pub fn get_cloned_visitor<T: LogicAvatar>(&self) -> Option<T> {
|
||||
let avatar = self.get_level().get_visitor_avatar::<T>()?;
|
||||
let mut stream = ByteStream::new(10);
|
||||
LogicAvatar::encode(&avatar, &mut stream);
|
||||
stream.reset_offset();
|
||||
let mut cloned_avatar = T::new();
|
||||
LogicAvatar::decode(&mut cloned_avatar, &mut stream);
|
||||
|
||||
Some(cloned_avatar)
|
||||
}
|
||||
|
||||
pub fn update_one_sub_tick(&self) {
|
||||
import!(logic_game_mode_update_one_sub_tick(ptr: *const u8) -> () = 0x14A8EC + 1);
|
||||
logic_game_mode_update_one_sub_tick(self.0);
|
||||
}
|
||||
|
||||
pub fn calculate_checksum(
|
||||
&self,
|
||||
debug_json: Option<&mut LogicJSONNode>,
|
||||
include_game_objects: bool,
|
||||
) -> i32 {
|
||||
import!(logic_game_mode_calculate_checksum(ptr: *const u8, debug_json: *const u8, include_game_objects: bool) -> i32 = 0x14AB54 + 1);
|
||||
|
||||
logic_game_mode_calculate_checksum(
|
||||
self.0,
|
||||
debug_json.map(|ptr| ptr.0).unwrap_or(std::ptr::null()),
|
||||
include_game_objects,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn save_to_json(&self, json: &mut LogicJSONNode) {
|
||||
self.get_level().save_to_json(json);
|
||||
}
|
||||
|
||||
pub fn get_command_manager(&self) -> LogicCommandManager {
|
||||
unsafe { LogicCommandManager(*(self.0.wrapping_add(20) as *const *const u8)) }
|
||||
}
|
||||
|
||||
pub fn get_level(&self) -> LogicLevel {
|
||||
unsafe { LogicLevel(*(self.0.wrapping_add(16) as *const *const u8)) }
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> i32 {
|
||||
unsafe { *(self.0 as *const i32) }
|
||||
}
|
||||
}
|
5
libserver/src/logic/time.rs
Normal file
5
libserver/src/logic/time.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#[repr(C)]
|
||||
pub struct LogicTime {
|
||||
pub sub_tick: i32,
|
||||
pub tick: i32,
|
||||
}
|
39
libserver/src/math.rs
Normal file
39
libserver/src/math.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::malloc;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct LogicLong {
|
||||
pub higher_int: i32,
|
||||
pub lower_int: i32,
|
||||
}
|
||||
|
||||
impl LogicLong {
|
||||
pub fn new(higher_int: i32, lower_int: i32) -> Self {
|
||||
Self {
|
||||
higher_int,
|
||||
lower_int,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_heap(&self) -> *const LogicLong {
|
||||
let ll = malloc(8);
|
||||
unsafe {
|
||||
*(ll as *mut i32) = self.higher_int;
|
||||
*(ll.wrapping_add(4) as *mut i32) = self.lower_int;
|
||||
}
|
||||
|
||||
ll as *const LogicLong
|
||||
}
|
||||
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.higher_int == 0 && self.lower_int == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LogicLong {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "LogicLong({},{})", self.higher_int, self.lower_int)
|
||||
}
|
||||
}
|
112
libserver/src/message/account.rs
Normal file
112
libserver/src/message/account.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
use crate::{import, malloc, math::LogicLong, network::PiranhaMessage, sc_string::ScString};
|
||||
|
||||
pub struct LoginMessage(pub PiranhaMessage);
|
||||
|
||||
impl LoginMessage {
|
||||
pub fn get_account_id(&self) -> &LogicLong {
|
||||
unsafe { &**(self.0 .0.wrapping_add(48) as *const *const LogicLong) }
|
||||
}
|
||||
|
||||
pub fn get_pass_token(&self) -> Option<ScString> {
|
||||
unsafe {
|
||||
let strptr = *(self.0 .0.wrapping_add(52) as *const ScString);
|
||||
(!strptr.0.is_null()).then_some(strptr)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_scrambler_seed(&self) -> i32 {
|
||||
unsafe { *(self.0 .0.wrapping_add(244) as *const i32) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExtendedSetEncryptionMessage(pub PiranhaMessage);
|
||||
|
||||
impl ExtendedSetEncryptionMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(set_encryption_message_ctor(ptr: *const u8) -> () = 0x16F324 + 1);
|
||||
let instance = malloc(60);
|
||||
set_encryption_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_nonce(&mut self, nonce: &[u8]) {
|
||||
let bytes = malloc(nonce.len());
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(bytes as *mut u8, nonce.len()).copy_from_slice(nonce);
|
||||
|
||||
*(self.0 .0.wrapping_add(48) as *mut *const u8) = bytes;
|
||||
*(self.0 .0.wrapping_add(52) as *mut i32) = nonce.len() as i32;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_scrambler_method(&mut self, method: i32) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(56) as *mut i32) = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoginOkMessage(pub PiranhaMessage);
|
||||
|
||||
impl LoginOkMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(login_ok_message_ctor(ptr: *const u8) -> () = 0x1629FC + 1);
|
||||
|
||||
let instance = malloc(124);
|
||||
login_ok_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_account_id(&mut self, account_id: LogicLong) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(48) as *mut usize) = account_id.to_heap() as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_home_id(&mut self, home_id: LogicLong) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(52) as *mut usize) = home_id.to_heap() as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pass_token(&mut self, pass_token: &str) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(56) as *mut usize) = ScString::from(pass_token).0 as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_server_major_version(&mut self, value: i32) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(76) as *mut i32) = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_server_build(&mut self, value: i32) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(80) as *mut i32) = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_content_version(&mut self, value: i32) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(84) as *mut i32) = value;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_server_environment(&mut self, value: &str) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(88) as *mut usize) = ScString::from(value).0 as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct KeepAliveServerMessage(pub PiranhaMessage);
|
||||
|
||||
impl KeepAliveServerMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(keep_alive_server_message_ctor(ptr: *const u8) -> () = 0x161838 + 1);
|
||||
let instance = malloc(48);
|
||||
keep_alive_server_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
}
|
9
libserver/src/message/avatar.rs
Normal file
9
libserver/src/message/avatar.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use crate::{network::PiranhaMessage, sc_string::ScString};
|
||||
|
||||
pub struct ChangeAvatarNameMessage(pub PiranhaMessage);
|
||||
|
||||
impl ChangeAvatarNameMessage {
|
||||
pub fn get_avatar_name(&self) -> String {
|
||||
unsafe { ScString(*(self.0 .0.wrapping_add(48) as *const *const u8)).to_string() }
|
||||
}
|
||||
}
|
132
libserver/src/message/home.rs
Normal file
132
libserver/src/message/home.rs
Normal file
|
@ -0,0 +1,132 @@
|
|||
use crate::{
|
||||
array_list::LogicArrayList,
|
||||
import,
|
||||
logic::{
|
||||
avatar::{LogicClientAvatar, LogicNpcAvatar},
|
||||
command::LogicCommand,
|
||||
data::LogicNpcData,
|
||||
home::LogicClientHome,
|
||||
},
|
||||
malloc,
|
||||
network::PiranhaMessage,
|
||||
sc_string::ScString,
|
||||
};
|
||||
|
||||
pub struct OwnHomeDataMessage(pub PiranhaMessage);
|
||||
|
||||
impl OwnHomeDataMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(own_home_data_message_ctor(ptr: *const u8) -> () = 0x16CD30 + 1);
|
||||
|
||||
let instance = malloc(104);
|
||||
own_home_data_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_logic_client_home(&mut self, logic_client_home: LogicClientHome) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(68) as *mut usize) = logic_client_home.0 as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_logic_client_avatar(&mut self, logic_client_avatar: LogicClientAvatar) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(72) as *mut usize) = logic_client_avatar.0 as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndClientTurnMessage(pub PiranhaMessage);
|
||||
|
||||
impl EndClientTurnMessage {
|
||||
pub fn get_sub_tick(&self) -> i32 {
|
||||
unsafe { *(self.0 .0.wrapping_add(52) as *const i32) }
|
||||
}
|
||||
|
||||
pub fn get_checksum(&self) -> i32 {
|
||||
unsafe { *(self.0 .0.wrapping_add(56) as *const i32) }
|
||||
}
|
||||
|
||||
pub fn get_commands(&self) -> Option<&LogicArrayList<LogicCommand>> {
|
||||
unsafe {
|
||||
let list_ptr =
|
||||
*(self.0 .0.wrapping_add(48) as *const *const LogicArrayList<LogicCommand>);
|
||||
(!list_ptr.is_null()).then_some(&*list_ptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutOfSyncMessage(pub PiranhaMessage);
|
||||
|
||||
impl OutOfSyncMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(out_of_sync_message_ctor(ptr: *const u8) -> () = 0x16CB60 + 1);
|
||||
let instance = malloc(64);
|
||||
out_of_sync_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_server_checksum(&mut self, value: i32) {
|
||||
unsafe { *(self.0 .0.wrapping_add(48) as *mut i32) = value }
|
||||
}
|
||||
|
||||
pub fn set_client_checksum(&mut self, value: i32) {
|
||||
unsafe { *(self.0 .0.wrapping_add(52) as *mut i32) = value }
|
||||
}
|
||||
|
||||
pub fn set_sub_tick(&mut self, value: i32) {
|
||||
unsafe { *(self.0 .0.wrapping_add(56) as *mut i32) = value }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AvailableServerCommandMessage(pub PiranhaMessage);
|
||||
|
||||
impl AvailableServerCommandMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(available_server_command_message_ctor(ptr: *const u8) -> () = 0x16B870 + 1);
|
||||
let instance = malloc(100);
|
||||
available_server_command_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_server_command(&mut self, command: &LogicCommand) {
|
||||
unsafe { *(self.0 .0.wrapping_add(48) as *mut usize) = command.0 as usize }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AttackNpcMessage(pub PiranhaMessage);
|
||||
|
||||
impl AttackNpcMessage {
|
||||
pub fn get_npc_data(&self) -> LogicNpcData {
|
||||
unsafe { LogicNpcData(*(self.0 .0.wrapping_add(48) as *const *const u8)) }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NpcDataMessage(pub PiranhaMessage);
|
||||
|
||||
impl NpcDataMessage {
|
||||
pub fn new() -> Self {
|
||||
import!(npc_data_message_ctor(ptr: *const u8) -> () = 0x16C9E8 + 1);
|
||||
let instance = malloc(64);
|
||||
npc_data_message_ctor(instance);
|
||||
Self(PiranhaMessage(instance))
|
||||
}
|
||||
|
||||
pub fn set_level_json(&mut self, json: &str) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(52) as *mut usize) = ScString::from(json).0 as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_logic_client_avatar(&mut self, value: &LogicClientAvatar) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(56) as *mut usize) = value.0 as usize;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_logic_npc_avatar(&mut self, value: &LogicNpcAvatar) {
|
||||
unsafe {
|
||||
*(self.0 .0.wrapping_add(60) as *mut usize) = value.0 as usize;
|
||||
}
|
||||
}
|
||||
}
|
7
libserver/src/message/mod.rs
Normal file
7
libserver/src/message/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
mod account;
|
||||
mod avatar;
|
||||
mod home;
|
||||
|
||||
pub use account::*;
|
||||
pub use avatar::*;
|
||||
pub use home::*;
|
139
libserver/src/network.rs
Normal file
139
libserver/src/network.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use tracing::info;
|
||||
|
||||
use crate::{import, malloc, sc_string::ScString};
|
||||
|
||||
pub struct Messaging(*const u8);
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Connection {
|
||||
pub fd: i32,
|
||||
pub is_connected: bool,
|
||||
unk_1: i32,
|
||||
unk_2: i32,
|
||||
unk_3: i32,
|
||||
}
|
||||
|
||||
impl Messaging {
|
||||
pub fn new(fd: i32) -> Self {
|
||||
import!(messaging_ctor(ptr: *const u8, queue_size: i32) -> () = 0x17476C + 1);
|
||||
|
||||
let instance = malloc(300);
|
||||
messaging_ctor(instance, 50);
|
||||
|
||||
let mut messaging = Self(instance);
|
||||
messaging.get_connection().fd = fd;
|
||||
messaging.get_connection().is_connected = true;
|
||||
|
||||
messaging
|
||||
}
|
||||
|
||||
pub fn set_encrypters(&mut self, encrypter: RC4Encrypter, decrypter: RC4Encrypter) {
|
||||
import!(messaging_set_encrypters(ptr: *const u8, en: *const u8, de: *const u8, a4: i32) -> () = 0x17469C + 1);
|
||||
messaging_set_encrypters(self.0, encrypter.0, decrypter.0, 0);
|
||||
}
|
||||
|
||||
pub fn set_message_factory(&mut self, factory: *const LogicMagicMessageFactory) {
|
||||
unsafe { *(self.0.wrapping_add(4) as *mut usize) = factory as usize }
|
||||
}
|
||||
|
||||
pub fn on_receive(&mut self) {
|
||||
import!(messaging_on_receive(ptr: *const u8, connection: *mut Connection) -> () = 0x175118 + 1);
|
||||
unsafe { messaging_on_receive(self.0, std::mem::transmute(self.0.wrapping_add(64))) }
|
||||
}
|
||||
|
||||
pub fn next_message(&mut self) -> Option<PiranhaMessage> {
|
||||
import!(messaging_next_message(ptr: *const u8) -> usize = 0x174A92 + 1);
|
||||
|
||||
let message = messaging_next_message(self.0);
|
||||
(message != 0).then_some(PiranhaMessage(message as *const u8))
|
||||
}
|
||||
|
||||
pub fn send(&mut self, message: PiranhaMessage) {
|
||||
import!(messaging_send(ptr: *const u8, message: *const u8) -> () = 0x174BD8 + 1);
|
||||
info!(
|
||||
"Messaging::send: sending message of type {}",
|
||||
message.get_message_type()
|
||||
);
|
||||
|
||||
messaging_send(self.0, message.0);
|
||||
}
|
||||
|
||||
pub fn on_wakeup(&mut self) {
|
||||
import!(messaging_on_wakeup(ptr: *const u8, connection: *mut Connection) -> () = 0x1749A4 + 1);
|
||||
unsafe { messaging_on_wakeup(self.0, std::mem::transmute(self.0.wrapping_add(64))) }
|
||||
}
|
||||
|
||||
pub fn get_connection(&mut self) -> &mut Connection {
|
||||
unsafe { std::mem::transmute(self.0.wrapping_add(64)) }
|
||||
}
|
||||
|
||||
pub fn scramble_nonce_using_mersenne_twister(seed: i32, nonce: &mut [u8]) {
|
||||
import!(messaging_scramble_nonce_using_mersenne_twister(seed: i32, nonce: *const u8, nonce_len: i32) -> () = 0x1A62E4 + 1);
|
||||
messaging_scramble_nonce_using_mersenne_twister(seed, nonce.as_ptr(), nonce.len() as i32);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct PiranhaMessage(pub *const u8);
|
||||
|
||||
impl PiranhaMessage {
|
||||
pub fn get_message_type(&self) -> u16 {
|
||||
unsafe {
|
||||
let fn_ptr = ((*(self.0 as *const usize)) + 20) as *const usize;
|
||||
std::mem::transmute::<_, extern "C" fn(*const u8) -> u16>(*fn_ptr)(self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct LogicMagicMessageFactory {
|
||||
vtable: usize,
|
||||
}
|
||||
|
||||
impl LogicMagicMessageFactory {
|
||||
pub const RC4_KEY: &str = "fhsd6f86f67rt8fw78fw789we78r9789wer6re";
|
||||
|
||||
pub fn new() -> Self {
|
||||
import!(logic_magic_message_factory_ctor(ptr: *mut u8) -> () = 0x1497DC + 1);
|
||||
|
||||
let mut instance = Self { vtable: 0 };
|
||||
unsafe {
|
||||
logic_magic_message_factory_ctor(std::mem::transmute(&mut instance));
|
||||
}
|
||||
|
||||
instance
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct RC4Encrypter(*const u8);
|
||||
|
||||
impl RC4Encrypter {
|
||||
pub fn new(key: &str, nonce: &str) -> Self {
|
||||
import!(rc4_encrypter_ctor(ptr: *const u8, key: *const u8, nonce: *const u8) -> () = 0x188D0C + 1);
|
||||
|
||||
let instance = malloc(268);
|
||||
rc4_encrypter_ctor(instance, ScString::from(key).0, ScString::from(nonce).0);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn new_with_nonce_bytes(key: &str, nonce: &[u8]) -> Self {
|
||||
import!(rc4_encrypter_ctor(ptr: *const u8, key: *const u8, nonce: *const u8, nonce_len: i32) -> () = 0x188CD8 + 1);
|
||||
|
||||
let instance = malloc(268);
|
||||
let nonce_bytes = malloc(nonce.len());
|
||||
unsafe {
|
||||
std::slice::from_raw_parts_mut(nonce_bytes as *mut u8, nonce.len())
|
||||
.copy_from_slice(nonce);
|
||||
};
|
||||
|
||||
rc4_encrypter_ctor(
|
||||
instance,
|
||||
ScString::from(key).0,
|
||||
nonce_bytes,
|
||||
nonce.len() as i32,
|
||||
);
|
||||
|
||||
Self(instance)
|
||||
}
|
||||
}
|
139
libserver/src/resources.rs
Normal file
139
libserver/src/resources.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use std::ffi::CString;
|
||||
|
||||
use crate::{import, logic::json::LogicJSONNode, malloc, sc_string::ScString};
|
||||
|
||||
pub struct ResourceManager;
|
||||
|
||||
impl ResourceManager {
|
||||
pub fn get_json(path: &str) -> LogicJSONNode {
|
||||
import!(resource_manager_get_json(path: *const u8) -> *const u8 = 0x18A750 + 1);
|
||||
LogicJSONNode(resource_manager_get_json(
|
||||
CString::new(path).unwrap().as_ptr(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct DataLoaderFactory(*const u8);
|
||||
|
||||
impl DataLoaderFactory {
|
||||
pub fn new() -> Self {
|
||||
import!(data_loader_factory_ctor(ptr: *const u8) -> () = 0x18967C + 1);
|
||||
|
||||
let instance = malloc(4);
|
||||
data_loader_factory_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct ResourceListener(*const u8);
|
||||
|
||||
impl ResourceListener {
|
||||
pub fn new() -> Self {
|
||||
import!(resource_listener_ctor(ptr: *const u8) -> () = 0x189464 + 1);
|
||||
|
||||
let instance = malloc(20);
|
||||
resource_listener_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
|
||||
pub fn add_file(&self, name: &str) {
|
||||
import!(resource_listener_add_file(ptr: *const u8, name: *const u8, a3: i32, a4: i32, a5: i32, a6: i32) -> () = 0x18B1C0 + 1);
|
||||
resource_listener_add_file(self.0, ScString::from(name).0, -1, -1, -1, -1);
|
||||
}
|
||||
|
||||
pub fn start_loading(&self) {
|
||||
import!(resource_listener_start_loading(ptr: *const u8) -> () = 0x18B50C + 1);
|
||||
resource_listener_start_loading(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
const CSV_FILES: &[&str] = &[
|
||||
"logic/buildings.csv",
|
||||
"logic/locales.csv",
|
||||
"logic/resources.csv",
|
||||
"logic/characters.csv",
|
||||
"csv/animations.csv",
|
||||
"logic/projectiles.csv",
|
||||
"csv/texts.csv",
|
||||
"csv/texts_patch.csv",
|
||||
"logic/regions.csv",
|
||||
"logic/building_classes.csv",
|
||||
"logic/obstacles.csv",
|
||||
"logic/effects.csv",
|
||||
"csv/particle_emitters.csv",
|
||||
"logic/experience_levels.csv",
|
||||
"logic/traps.csv",
|
||||
"logic/alliance_badges.csv",
|
||||
"logic/alliance_badge_layers.csv",
|
||||
"logic/globals.csv",
|
||||
"csv/client_globals.csv",
|
||||
"logic/townhall_levels.csv",
|
||||
"logic/alliance_portal.csv",
|
||||
"logic/npcs.csv",
|
||||
"logic/decos.csv",
|
||||
"csv/resource_packs.csv",
|
||||
"logic/shields.csv",
|
||||
"logic/missions.csv",
|
||||
"csv/billing_packages.csv",
|
||||
"logic/achievements.csv",
|
||||
"csv/credits.csv",
|
||||
"csv/faq.csv",
|
||||
"logic/spells.csv",
|
||||
"csv/hints.csv",
|
||||
"logic/heroes.csv",
|
||||
"logic/leagues.csv",
|
||||
"csv/news.csv",
|
||||
"logic/war.csv",
|
||||
"logic/alliance_levels.csv",
|
||||
"csv/helpshift.csv",
|
||||
];
|
||||
|
||||
const NPCS_COUNT: usize = 48;
|
||||
const PREBASES_COUNT: usize = 11;
|
||||
|
||||
import!(resource_manager_init(factory: DataLoaderFactory, a2: *const u8) -> () = 0x18B898 + 1);
|
||||
import!(resource_manager_resource_to_load() -> i32 = 0x1894B8 + 1);
|
||||
import!(resource_manager_load_next_resource() -> () = 0x18A9EC + 1);
|
||||
import!(logic_data_tables_init() -> () = 0x12CF54 + 1);
|
||||
import!(logic_resources_create_data_table_resources_array() -> *const u8 = 0x136978 + 1);
|
||||
import!(resource_manager_get_csv(csv: *const u8) -> *const u8 = 0x18A816 + 1);
|
||||
import!(logic_resources_load(data_table_resources_array: *const u8, index: i32, csv: *const u8) -> *const u8 = 0x1368A8 + 1);
|
||||
|
||||
let data_loader_factory = DataLoaderFactory::new();
|
||||
|
||||
resource_manager_init(data_loader_factory, [0x00].as_ptr());
|
||||
logic_data_tables_init();
|
||||
|
||||
let listener = ResourceListener::new();
|
||||
listener.add_file("level/starting_home.json");
|
||||
|
||||
for i in 1..=NPCS_COUNT {
|
||||
listener.add_file(&format!("level/npc{i}.json"));
|
||||
}
|
||||
|
||||
for i in 1..=PREBASES_COUNT {
|
||||
listener.add_file(&format!("level/townhall{i}.json"));
|
||||
}
|
||||
|
||||
listener.add_file("level/tutorial_npc.json");
|
||||
listener.add_file("level/tutorial_npc2.json");
|
||||
|
||||
for path in CSV_FILES {
|
||||
listener.add_file(path);
|
||||
}
|
||||
|
||||
listener.start_loading();
|
||||
|
||||
while resource_manager_resource_to_load() != 0 {
|
||||
resource_manager_load_next_resource();
|
||||
}
|
||||
|
||||
let data_table_resources_array = logic_resources_create_data_table_resources_array();
|
||||
for (index, path) in CSV_FILES.iter().enumerate() {
|
||||
let csv = resource_manager_get_csv(ScString::from(path).0);
|
||||
logic_resources_load(data_table_resources_array, index as i32, csv);
|
||||
}
|
||||
}
|
79
libserver/src/sc_string.rs
Normal file
79
libserver/src/sc_string.rs
Normal file
|
@ -0,0 +1,79 @@
|
|||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use crate::{import, malloc};
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ScString(pub *const u8);
|
||||
|
||||
impl<S> From<S> for ScString
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn from(value: S) -> Self {
|
||||
import!(string_ctor(ptr: *const u8, data: *const u8) -> () = 0x176748 + 1);
|
||||
let sc_string = malloc(32);
|
||||
|
||||
string_ctor(sc_string, CString::new(value.as_ref()).unwrap().as_ptr());
|
||||
Self(sc_string)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ScString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let length = unsafe { *(self.0.wrapping_add(4) as *const i32) as usize };
|
||||
if length + 1 > 8 {
|
||||
let c_string = unsafe { *(self.0.wrapping_add(8) as *const *const u8) };
|
||||
unsafe {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
CStr::from_ptr(c_string).to_string_lossy().to_string()
|
||||
)
|
||||
}
|
||||
} else if length > 0 {
|
||||
unsafe {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
CStr::from_ptr(self.0.wrapping_add(8) as *const u8)
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct StringBuilder(pub *const u8);
|
||||
|
||||
impl StringBuilder {
|
||||
pub fn new() -> Self {
|
||||
import!(string_builder_ctor(ptr: *const u8) -> () = 0x1772BA + 1);
|
||||
|
||||
let instance = malloc(12);
|
||||
string_builder_ctor(instance);
|
||||
Self(instance)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StringBuilder {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
import!(string_builder_to_string(sc_str: *const u8, ptr: *const u8) -> () = 0x1772D8 + 1);
|
||||
let sc_str = ScString::from("");
|
||||
string_builder_to_string(sc_str.0, self.0);
|
||||
write!(f, "{sc_str}")
|
||||
}
|
||||
}
|
BIN
screenshot.jpg
Normal file
BIN
screenshot.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
Loading…
Reference in a new issue