├── .cargo └── config.toml ├── .env.example ├── .github └── workflows │ └── release.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── config └── pp-server │ ├── default.toml │ ├── development.toml │ └── production.toml ├── example_data └── beatmaps │ └── ccb1f31b5eeaf26d40f8c905293efc03.osu ├── rename_osu_files.py ├── screenshot ├── ef1.png └── ef2.png ├── src ├── main.rs ├── objects │ ├── caches.rs │ ├── calculator.rs │ ├── glob.rs │ ├── mod.rs │ └── server.rs ├── renders │ └── mod.rs ├── routes │ ├── api.rs │ ├── debug.rs │ ├── default.rs │ └── mod.rs ├── settings │ ├── mod.rs │ └── model.rs └── utils.rs ├── templates └── main_page.html └── win_cross_compile_linux.bat /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-musl] 2 | linker = "musl-gcc" 3 | 4 | [alias] 5 | debug = "run" 6 | start = "run --release" 7 | cross_linux_x86 = "build --release --target x86_64-unknown-linux-musl" 8 | release_build = "build --release" 9 | build_doc = "doc --release --package peace --no-deps" 10 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | RUN_MODE = "development" -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [created] 4 | 5 | jobs: 6 | build-with-peace: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v1 12 | 13 | - name: Install latest rust toolchain 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | default: true 18 | override: true 19 | 20 | - name: Build 21 | run: cargo build --features with_peace --release --bin pp-server-with-peace 22 | 23 | - name: Release 24 | uses: softprops/action-gh-release@v1 25 | if: startsWith(github.ref, 'refs/tags/') 26 | with: 27 | files: | 28 | target/release/pp-server-with-peace 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | 32 | build-with-peace-win: 33 | runs-on: windows-latest 34 | 35 | steps: 36 | - name: Checkout 37 | uses: actions/checkout@v1 38 | 39 | - name: Install latest rust toolchain 40 | uses: actions-rs/toolchain@v1 41 | with: 42 | toolchain: stable 43 | default: true 44 | override: true 45 | 46 | - name: Build 47 | run: cargo build --features with_peace --release --bin pp-server-with-peace 48 | 49 | - name: Release 50 | uses: softprops/action-gh-release@v1 51 | if: startsWith(github.ref, 'refs/tags/') 52 | with: 53 | files: | 54 | target/release/pp-server-with-peace.exe 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | 58 | build-without-db: 59 | runs-on: ubuntu-latest 60 | 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@v1 64 | 65 | - name: Install latest rust toolchain 66 | uses: actions-rs/toolchain@v1 67 | with: 68 | toolchain: stable 69 | default: true 70 | override: true 71 | 72 | - name: Build 73 | run: cargo build --no-default-features --features peace-objects/no_database --release --bin pp-server-without-db 74 | 75 | - name: Release 76 | uses: softprops/action-gh-release@v1 77 | if: startsWith(github.ref, 'refs/tags/') 78 | with: 79 | files: | 80 | target/release/pp-server-without-db 81 | env: 82 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 83 | 84 | build-without-db-win: 85 | runs-on: windows-latest 86 | 87 | steps: 88 | - name: Checkout 89 | uses: actions/checkout@v1 90 | 91 | - name: Install latest rust toolchain 92 | uses: actions-rs/toolchain@v1 93 | with: 94 | toolchain: stable 95 | default: true 96 | override: true 97 | 98 | - name: Build 99 | run: cargo build --no-default-features --features peace-objects/no_database --release --bin pp-server-without-db 100 | 101 | - name: Release 102 | uses: softprops/action-gh-release@v1 103 | if: startsWith(github.ref, 'refs/tags/') 104 | with: 105 | files: | 106 | target/release/pp-server-without-db.exe 107 | env: 108 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/target 3 | 4 | *.lock 5 | .data 6 | example_data/beatmaps/* 7 | !!example_data/beatmaps/ccb1f31b5eeaf26d40f8c905293efc03.osu -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "git.ignoreLimitWarning": true 3 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Upcoming 2 | 3 | - No changes so far 4 | 5 | # v0.4.0 6 | 7 | - Framework update 8 | - Migrate from actix-web to ntex. 9 | - Using tokio instead of async-std. 10 | 11 | # v0.3.0 12 | 13 | - Big refactor, improved code reuse, and more. 14 | 15 | # v0.2.4 16 | 17 | - Add auto pp recalculate (for peace) 18 | - If pp calculation fails (such as restarting pp-server), just save task to redis in the format of "`calc:{table(mode)}:{score_id}:{player_id}`":"`md5=xxx&mods=xx&mode=xx&n300=xx`". pp-server will auto recalculate these tasks, and notify peace to update the stats of these players. 19 | 20 | # v0.2.3 21 | 22 | - Add no_miss option, (additional) calculate score pp if no miss. 23 | 24 | # v0.2.2 25 | 26 | - More info. 27 | - Now, we can get Raw pp result: aim, spd, acc, str. 28 | - And, we can get stars, mods, mode of pp results. 29 | - Support for calculate acc list pp. (As Tillerino) 30 | - We can choose simple info or raw info. 31 | 32 | # v0.2.0 33 | 34 | - Big updates. 35 | - Now, we can get beatmap from osu!api, download .osu files. 36 | - Add feature "peace", we can use postgresql database to cache beatmap. 37 | - Support for beatmap id. 38 | - Support for beatmapset id + file name. 39 | - Support for more params. 40 | 41 | ## v0.1.0 42 | 43 | - Initial version. 44 | - Basic ability, Calculate the pp according to local beatmap. 45 | - Simple api. 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pp-server" 3 | version = "0.5.0" 4 | authors = ["Pure-Peace "] 5 | edition = "2018" 6 | license = "MIT" 7 | repository = "https://github.com/pure-peace/pp-server" 8 | default-run = "pp-server-with-peace" 9 | 10 | [[bin]] 11 | name = "pp-server-with-peace" 12 | path = "src/main.rs" 13 | 14 | [[bin]] 15 | name = "pp-server-without-db" 16 | path = "src/main.rs" 17 | 18 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 19 | [features] 20 | # default = ["peace-objects/no_database"] # Not use peace database, run pp-server independently. 21 | default = ["with_peace"] # Works with peace, including peace database 22 | 23 | with_peace = [ 24 | "deadpool-postgres", 25 | "deadpool-redis", 26 | "tokio-postgres", 27 | "peace-database", 28 | "peace-objects/with_peace", 29 | "peace-objects/peace_api", 30 | "peace-utils/peace", 31 | ] 32 | 33 | [dependencies] 34 | askama = "0.10.5" 35 | bytes = "1.0" 36 | chrono = "0.4.19" 37 | colored = "2.0.0" 38 | config = "0.11" 39 | derivative = "2.2.0" 40 | dotenv = "0.15.0" 41 | env_logger = "0.8.3" 42 | field_names = "0.1.1" 43 | hashbrown = "0.11" 44 | json = "0.12.4" 45 | log = "0.4.14" 46 | ntex = "0.3" 47 | prometheus = { version = "0.12", features = ["process"] } 48 | reqwest = { version = "0.11", features = [ 49 | "rustls-tls", 50 | "json", 51 | ], default-features = false } 52 | serde = { version = "1.0", features = ["derive"] } 53 | serde_json = "1.0" 54 | serde_str = "0.1.0" 55 | tokio = { version = "1.9" } 56 | 57 | 58 | # Feature peace 59 | deadpool-postgres = { version = "0.9", optional = true } 60 | deadpool-redis = { version = "0.8", optional = true } 61 | tokio-postgres = { version = "0.7", features = [ 62 | "with-chrono-0_4", 63 | "with-serde_json-1", 64 | ], optional = true } 65 | 66 | peace-performance = { version = "0.4.0" } 67 | 68 | # Git 69 | peace-constants = { git = "https://github.com/Pure-Peace/Peace.git", branch = "main" } 70 | peace-database = { git = "https://github.com/Pure-Peace/Peace.git", branch = "main", features = [ 71 | "with_peace", 72 | ], optional = true } 73 | peace-objects = { git = "https://github.com/Pure-Peace/Peace.git", branch = "main", features = [ 74 | "osu_file_downloader", 75 | ], optional = true } 76 | peace-settings = { git = "https://github.com/Pure-Peace/Peace.git", branch = "main" } 77 | peace-utils = { git = "https://github.com/Pure-Peace/Peace.git", branch = "main", features = [ 78 | "web", 79 | "async_file", 80 | ] } 81 | 82 | # Local (download peace manual) 83 | # peace-constants = { path = "../../Peace/peace-constants" } 84 | # peace-database = { path = "../../Peace/peace-database", features = ["with_peace"], optional = true } 85 | # peace-objects = { path = "../../Peace/peace-objects", features = ["osu_file_downloader"], optional = true } 86 | # peace-settings = { path = "../../Peace/peace-settings" } 87 | # peace-utils = { path = "../../Peace/peace-utils", features = ["web", "async_file"] } 88 | 89 | 90 | # link-time-optimization 91 | # Enabling makes the compiled program size smaller and higher performance, 92 | # but it will extend compilation time. 93 | [profile.release] 94 | lto = true 95 | codegen-units = 1 96 | opt-level = "s" 97 | 98 | # link-time-optimization 99 | # Enabling makes the compiled program size smaller and higher performance, 100 | # but it will extend compilation time. 101 | [profile.bench] 102 | lto = true 103 | codegen-units = 1 104 | opt-level = "s" 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Pure-Peace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## A Fast osu! pp calculator web api written in Rust 2 | 3 | It is also the PP server of **[Peace](https://github.com/Pure-Peace/peace)**. 4 | 5 | **Pure-Rust** pp calculator based on [peace-performance](https://github.com/Pure-Peace/peace-performance) and [rosu-pp](https://github.com/MaxOhn/rosu-pp). 6 | 7 | ### Features 8 | 9 | - **Common**: 10 | - **request with you like: beatmap md5, beatmap id, beatmapset id + file name** 11 | - **beatmap cache** 12 | - **preload beatmaps** (WARING: May cause insufficient memory, if the number of maps is large enough) 13 | - **calculate beatmap MD5** 14 | - **auto request, download beatmap from osu!api** 15 | - **raw pp info: aim, spd, acc, str.** 16 | - **acc list: 95, 98, 99, 100 (request with &acc_list=1)** 17 | - **Oppai? Or a custom algorithm** 18 | - **auto-pp-recalculate (peace)** 19 | - If pp calculation fails (such as restarting pp-server), just save task to redis in the format of "`calc:{table(mode)}:{score_id}:{player_id}`":"`md5=xxx&mods=xx&mode=xx&n300=xx`". pp-server will auto recalculate these tasks, and notify peace to update the stats of these players. 20 | . 21 | - **pp calculate**: 22 | - osu!Standard 23 | - Catch the beat 24 | - Taiko 25 | - Mainia 26 | - (**Default Enabled**) feature **with_peace**: 27 | - beatmap database (needs setup with [Peace](https://github.com/Pure-Peace/Peace/tree/main/sql)) 28 | - auto pp recalculate 29 | - How to **enable**? 30 | - `Cargo.toml` features Set `default = ["with_peace"]` 31 | - **Will run as a pp server of Peace** 32 | - How to **disable**? 33 | - Set features `default = ["peace-objects/no_database"]` 34 | - **Not use peace database, run pp-server independently.** 35 | 36 | ## Examples 37 | 38 | **with md5** 39 | 40 | *Common* 41 | 42 | ``` 43 | /api/calc?md5=ccb1f31b5eeaf26d40f8c905293efc03 44 | ``` 45 | 46 | ```json 47 | { 48 | "acc_list": null, 49 | "message": "done", 50 | "mode": 0, 51 | "mods": 0, 52 | "pp": 522.0230712890625, 53 | "raw": { 54 | "acc": 128.93072509765625, 55 | "aim": 255.26805114746094, 56 | "spd": 127.87066650390625, 57 | "str": 0, 58 | "total": 522.0230712890625 59 | }, 60 | "stars": 7.084656715393066, 61 | "status": 1 62 | } 63 | ``` 64 | 65 | *Simple* 66 | 67 | ``` 68 | /api/calc?md5=ccb1f31b5eeaf26d40f8c905293efc03&simple=1 69 | ``` 70 | 71 | ```json 72 | { 73 | "acc_list": null, 74 | "message": "done", 75 | "mode": 0, 76 | "mods": 0, 77 | "pp": 522.0230712890625, 78 | "stars": 7.084656715393066, 79 | "status": 1 80 | } 81 | ``` 82 | 83 | *Acc list* 84 | 85 | ``` 86 | /api/calc?md5=ccb1f31b5eeaf26d40f8c905293efc03&acc_list=1 87 | ``` 88 | 89 | ```json 90 | { 91 | "acc_list": { 92 | "95": 311.07989501953125, 93 | "98": 389.848388671875, 94 | "99": 452.67974853515625, 95 | "100": 522.0230712890625 96 | }, 97 | "message": "done", 98 | "mode": 0, 99 | "mods": 0, 100 | "pp": 522.0230712890625, 101 | "raw": { 102 | "acc": 128.93072509765625, 103 | "aim": 255.26805114746094, 104 | "spd": 127.87066650390625, 105 | "str": 0, 106 | "total": 522.0230712890625 107 | }, 108 | "stars": 7.084656715393066, 109 | "status": 1 110 | } 111 | ``` 112 | 113 | **with bid (Can use without add osu!api keys)** 114 | 115 | ``` 116 | /api/calc?bid=2848898 117 | ``` 118 | 119 | ```json 120 | { 121 | "acc_list": null, 122 | "message": "done", 123 | "mode": 0, 124 | "mods": 0, 125 | "pp": 366.8739013671875, 126 | "raw": { 127 | "acc": 118.15778350830078, 128 | "aim": 122.00947570800781, 129 | "spd": 121.79961395263672, 130 | "str": 0, 131 | "total": 366.8739013671875 132 | }, 133 | "stars": 5.856814384460449, 134 | "status": 1 135 | } 136 | ``` 137 | 138 | **with sid + file name (need osu!api keys)** 139 | 140 | ``` 141 | /api/calc?sid=1378720&file_name=Tanchiky%20-%20Bridge%20(NyarkoO)%20[Extension].osu 142 | ``` 143 | 144 | This method is currently cannot use cache. 145 | 146 | ```json 147 | { 148 | "acc_list": null, 149 | "message": "done", 150 | "mode": 0, 151 | "mods": 0, 152 | "pp": 366.8739013671875, 153 | "raw": { 154 | "acc": 118.15778350830078, 155 | "aim": 122.00947570800781, 156 | "spd": 121.79961395263672, 157 | "str": 0, 158 | "total": 366.8739013671875 159 | }, 160 | "stars": 5.856814384460449, 161 | "status": 1 162 | } 163 | ``` 164 | 165 | ### Best performance (Fastest, but lower accuracy) 166 | 167 | Set Cargo.toml 168 | 169 | ```rust 170 | peace-performance = { ... } 171 | ``` 172 | 173 | to 174 | 175 | ```rust 176 | peace-performance = { ..., feature = "no_sliders_no_leniency" } 177 | ``` 178 | 179 | ## Note 180 | 181 | **This pp-server requires all `.osu` files use file MD5 as the name.** 182 | 183 | - **Rename .osu files to file md5:** 184 | 185 | - If you want **pp server** auto recalculate all `.osu` files MD5 name before started, set `recalculate_osu_file_md5 = true` in `config/pp-server/default.toml` 186 | - Or manual run this python script `rename_osu_files.py` in project (python3.8+). 187 | - If its Debug compile, python will more faster than Rust. 188 | 189 | - **Effect** 190 | - Calculating 191 | - ![p](screenshot/ef1.png) 192 | - After 193 | - ![p](screenshot/ef2.png) 194 | 195 | ### Setup 196 | 197 | 1. Set your `.osu` files dir path in `config/pp-server/default.toml` 198 | 2. Will let the `.osu` files name be the `md5` of the file 199 | 3. Set your osu!api keys in *.toml (if enabled feature `peace`, set it on your database) 200 | 201 | ### Debug 202 | 203 | ``` 204 | cargo run 205 | ``` 206 | 207 | ### Release 208 | 209 | ``` 210 | cargo run --release 211 | ``` 212 | 213 | **Cross compile (Win to Linux)** 214 | 215 | ``` 216 | cargo cross_linux_x86 217 | ``` 218 | 219 | ## MIT 220 | -------------------------------------------------------------------------------- /config/pp-server/default.toml: -------------------------------------------------------------------------------- 1 | # .osu files dir path (without last '/') 2 | # use '\\' instead of '\' in windows 3 | osu_files_dir = "example_data/beatmaps" 4 | 5 | # if true, pp server will recalculate all .osu files in dir before started. 6 | recalculate_osu_file_md5 = true 7 | 8 | # if true, pp server will Load all .osu files at start; 9 | # WARING: May cause insufficient memory if beatmap_cache_max > 9000 10 | preload_osu_files = true 11 | 12 | # max beatmap count in cache 13 | beatmap_cache_max = 200 14 | beatmap_cache_timeout = 3600 15 | 16 | # if true, will auto remove timeout beatmap cache each interval (seconds) 17 | auto_clean_cache = true 18 | auto_clean_interval = 300 19 | 20 | # Set peace_key in the pp server to the same value as here 21 | peace_key = "pp_server" 22 | peace_url = "http://127.0.0.1:8080" # without last "/" 23 | 24 | # pp server config 25 | [server] 26 | # It is recommended to set it to 127.0.0.1, 27 | # and then use nginx to expose part of the interface 28 | host = "127.0.0.1" 29 | port = 8088 30 | 31 | # auto pp recalculate task config (features peace) 32 | [auto_pp_recalculate] 33 | # each interval do once 34 | interval = 60 35 | # if pp calculate fails > max_retry, skip it 36 | max_retry = 5 37 | -------------------------------------------------------------------------------- /config/pp-server/development.toml: -------------------------------------------------------------------------------- 1 | # Development config 2 | # Run with: 3 | # Cargo run development 4 | 5 | # if not enable feature "peace", use this osu!api keys, 6 | # else, will get from peace database instead of use this. 7 | osu_api_keys = [] 8 | 9 | # optional, needs enable feature "peace" 10 | # If true, will check the database connection pools on created 11 | check_pools_on_created = true 12 | check_db_version_on_created = true 13 | 14 | # !!warn: If debug is enabled, 15 | # the part of the route used for testing will be loaded. 16 | # Please do not expose it in the production environment 17 | debug = true 18 | 19 | # Logger config 20 | [logger] 21 | # Level is selected from logger.mode 22 | level = "debug" 23 | 24 | # Ntex logger middleware format 25 | # Default is: '%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T' 26 | # If using reverse proxy, use %{X-Forwarded-For}i instead of %a 27 | # https://docs.rs/ntex/0.3.16/ntex/web/middleware/struct.Logger.html 28 | server_log_format = '%s - %a "%r" %b %T' 29 | 30 | # Logger will not record logs of these endpoints 31 | # For example, 32 | # i don't want logger to print logs about endpoint "/favicon.ico", so i fill it in 33 | exclude_endpoints = ["/favicon.ico"] 34 | exclude_endpoints_regex = ["/web*"] 35 | 36 | # Logger will not record logs from these modules 37 | exclude_modules = ["maxminddb::decoder", "hyper", "h2", "rustls"] 38 | 39 | # Logger mode is env_logger config 40 | # Support filter: "trace,mio::sys=info" 41 | # https://docs.rs/env_logger/0.8.1/env_logger/index.html 42 | [logger.mode] 43 | debug = "debug" 44 | info = "info" 45 | error = "error" 46 | warn = "warn" 47 | 48 | 49 | # Prometheus is an open-source monitoring metrics solution 50 | # It can help you monitor the status of databases, systems, web applications, and collect data 51 | # https://prometheus.io/ 52 | # And, with grafana, we can make real-time visualization charts! 53 | # https://grafana.com/ 54 | [prometheus] 55 | # Your application name 56 | namespace = "peace" 57 | 58 | # Data collection address, 59 | # Means you can access: http://host:port 60 | endpoint = "/metrics" 61 | 62 | # If true, logger will not log prometheus endpoint 63 | exclude_endpoint_log = true 64 | 65 | # optional, needs enable feature "peace" 66 | # Docs for postgres config: 67 | # https://docs.rs/deadpool-postgres/0.5.6/deadpool_postgres/config/struct.Config.html 68 | [postgres] 69 | host = "localhost" 70 | port = 5432 71 | user = "postgres" 72 | password = "123456" 73 | dbname = "peace" 74 | 75 | [postgres.pool] 76 | max_size = 16 77 | 78 | # optional, needs enable feature "peace" 79 | # Docs for redis config: 80 | # https://docs.rs/deadpool-redis/0.6.1/deadpool_redis/struct.Config.html 81 | [redis] 82 | # Format here: 83 | # redis://[:@][:port][/] 84 | url = "redis://127.0.0.1:6379" 85 | 86 | [redis.pool] 87 | max_size = 16 88 | -------------------------------------------------------------------------------- /config/pp-server/production.toml: -------------------------------------------------------------------------------- 1 | # Production config 2 | # Run with: 3 | # Cargo run --release production 4 | 5 | # if not enable feature "peace", use this osu!api keys, 6 | # else, will get from peace database instead of use this. 7 | osu_api_keys = [] 8 | 9 | # optional, needs enable feature "peace" 10 | # If true, will check the database connection pools on created 11 | check_pools_on_created = true 12 | check_db_version_on_created = true 13 | 14 | # !!warn: If debug is enabled, 15 | # the part of the route used for testing will be loaded. 16 | # Please do not expose it in the production environment 17 | debug = false 18 | 19 | # Logger config 20 | [logger] 21 | # Level is selected from logger.mode 22 | level = "info" 23 | 24 | # Ntex logger middleware format 25 | # Default is: '%a "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T' 26 | # If using reverse proxy, use %{X-Forwarded-For}i instead of %a 27 | # https://docs.rs/ntex/0.3.16/ntex/web/middleware/struct.Logger.html 28 | server_log_format = '%s - %a "%r" %b %T' 29 | 30 | # Logger will not record logs of these endpoints 31 | # For example, 32 | # i don't want logger to print logs about endpoint "/favicon.ico", so i fill it in 33 | exclude_endpoints = ["/favicon.ico"] 34 | exclude_endpoints_regex = ["/web*"] 35 | 36 | # Logger will not record logs from these modules 37 | exclude_modules = ["maxminddb::decoder", "hyper", "h2", "rustls", "reqwest"] 38 | 39 | # Logger mode is env_logger config 40 | # Support filter: "trace,mio::sys=info" 41 | # https://docs.rs/env_logger/0.8.1/env_logger/index.html 42 | [logger.mode] 43 | debug = "trace" 44 | info = "info" 45 | error = "error" 46 | warn = "warn" 47 | 48 | 49 | # Prometheus is an open-source monitoring metrics solution 50 | # It can help you monitor the status of databases, systems, web applications, and collect data 51 | # https://prometheus.io/ 52 | # And, with grafana, we can make real-time visualization charts! 53 | # https://grafana.com/ 54 | [prometheus] 55 | # Your application name 56 | namespace = "peace" 57 | 58 | # Data collection address, 59 | # Means you can access: http://host:port 60 | endpoint = "/metrics" 61 | 62 | # If true, logger will not log prometheus endpoint 63 | exclude_endpoint_log = true 64 | 65 | # optional, needs enable feature "peace" 66 | # Docs for postgres config: 67 | # https://docs.rs/deadpool-postgres/0.5.6/deadpool_postgres/config/struct.Config.html 68 | [postgres] 69 | host = "localhost" 70 | port = 5432 71 | user = "postgres" 72 | password = "123456" 73 | dbname = "peace" 74 | 75 | [postgres.pool] 76 | max_size = 16 77 | 78 | # optional, needs enable feature "peace" 79 | # Docs for redis config: 80 | # https://docs.rs/deadpool-redis/0.6.1/deadpool_redis/struct.Config.html 81 | [redis] 82 | # Format here: 83 | # redis://[:@][:port][/] 84 | url = "redis://127.0.0.1:6379" 85 | 86 | [redis.pool] 87 | max_size = 16 88 | -------------------------------------------------------------------------------- /example_data/beatmaps/ccb1f31b5eeaf26d40f8c905293efc03.osu: -------------------------------------------------------------------------------- 1 | osu file format v14 2 | 3 | [General] 4 | AudioFilename: audio.mp3 5 | AudioLeadIn: 0 6 | PreviewTime: 262170 7 | Countdown: 0 8 | SampleSet: Soft 9 | StackLeniency: 0.5 10 | Mode: 0 11 | LetterboxInBreaks: 0 12 | EpilepsyWarning: 1 13 | WidescreenStoryboard: 1 14 | 15 | [Editor] 16 | Bookmarks: 950,29560,49658,68574,88081,106407,127687,136259,146604,165520,177934,187392,196850,215766,230249,234682,253303,262466,268968,278278,293208,311533,330449,340203 17 | DistanceSpacing: 1.1 18 | BeatDivisor: 4 19 | GridSize: 32 20 | TimelineZoom: 3.599998 21 | 22 | [Metadata] 23 | Title:Gareki no Shuuon 24 | TitleUnicode:瓦礫の終音 25 | Artist:Shoujo Byou 26 | ArtistUnicode:少女病 27 | Creator:PurePeace 28 | Version:A Song That Tells A Story: Abend~ 29 | Source: 30 | Tags:mitsuki 少女病 しょうじょびょう the final tone of rubble Souhaku Sisferia 蒼白シスフェリア ピクセルビー 岸田 岸田教団 光収容 そうる透・村石雅行 japan jpop はなざわ かな 物語 story music 31 | BeatmapID:2476156 32 | BeatmapSetID:1188245 33 | 34 | [Difficulty] 35 | HPDrainRate:6 36 | CircleSize:4 37 | OverallDifficulty:9.2 38 | ApproachRate:9.7 39 | SliderMultiplier:1.9 40 | SliderTickRate:1 41 | 42 | [Events] 43 | //Background and Video events 44 | 0,0,"background.jpg",0,0 45 | //Break Periods 46 | 2,178134,182862 47 | //Storyboard Layer 0 (Background) 48 | //Storyboard Layer 1 (Fail) 49 | //Storyboard Layer 2 (Pass) 50 | //Storyboard Layer 3 (Foreground) 51 | //Storyboard Layer 4 (Overlay) 52 | //Storyboard Sound Samples 53 | 54 | [TimingPoints] 55 | 950,857.142857142857,4,2,2,5,1,0 56 | 25807,-100,4,2,2,10,0,0 57 | 26664,-100,4,2,2,15,0,0 58 | 27521,-100,4,2,2,25,0,0 59 | 28378,-100,4,2,2,35,0,0 60 | 29560,591.133004926108,4,2,2,40,1,0 61 | 29560,-142.857142857143,4,2,2,40,0,0 62 | 30742,-142.857142857143,4,2,2,40,0,0 63 | 31924,-200,4,2,2,40,0,0 64 | 35471,-133.333333333333,4,2,2,40,0,0 65 | 38426,-95.2380952380952,4,2,2,40,0,0 66 | 39018,-113.636363636364,4,2,2,40,0,0 67 | 41087,-101.010101010101,4,2,2,40,0,0 68 | 41382,-113.636363636364,4,2,2,40,0,0 69 | 44929,-400,4,2,2,40,0,0 70 | 46407,-100,4,2,2,45,0,0 71 | 46702,-90.9090909090909,4,2,2,45,0,0 72 | 46998,-83.3333333333333,4,2,2,50,0,0 73 | 47293,-76.9230769230769,4,2,2,50,0,0 74 | 47589,-71.4285714285714,4,2,2,55,0,0 75 | 47885,-66.6666666666667,4,2,2,55,0,0 76 | 48180,-62.5,4,2,2,60,0,0 77 | 48476,295.566502463054,4,2,3,65,1,0 78 | 48476,-83.3333333333333,4,2,3,65,0,0 79 | 49067,-100,4,2,3,50,0,0 80 | 49362,-100,4,2,3,60,0,0 81 | 49658,-83.3333333333333,4,2,3,75,0,1 82 | 56751,-50,4,2,3,75,0,1 83 | 56899,-83.3333333333333,4,2,3,75,0,1 84 | 66062,-66.6666666666667,4,2,3,75,0,1 85 | 66209,-76.9230769230769,4,2,3,75,0,0 86 | 68574,-142.857142857143,4,2,3,40,0,0 87 | 83648,-117.647058823529,4,2,3,40,0,0 88 | 84535,-90.9090909090909,4,2,3,50,0,0 89 | 85126,-83.3333333333333,4,2,3,60,0,1 90 | 86456,-83.3333333333333,4,2,3,75,0,1 91 | 87490,-66.6666666666667,4,2,3,75,0,0 92 | 87712,-90.9090909090909,4,2,3,75,0,0 93 | 87934,-100,4,2,3,75,0,0 94 | 88968,-100,4,2,3,75,0,0 95 | 89855,-83.3333333333333,4,2,3,75,0,0 96 | 90003,-100,4,2,3,75,0,0 97 | 91333,-100,4,2,3,75,0,0 98 | 91924,-83.3333333333333,4,2,3,75,0,0 99 | 92219,-76.9230769230769,4,2,3,75,0,0 100 | 92515,-90.9090909090909,4,2,3,75,0,0 101 | 93402,-100,4,2,3,75,0,0 102 | 93549,-90.9090909090909,4,2,3,75,0,0 103 | 94879,-83.3333333333333,4,2,3,75,0,0 104 | 95027,-90.9090909090909,4,2,3,75,0,0 105 | 98722,-90.9090909090909,4,2,3,75,0,0 106 | 99313,-83.3333333333333,4,2,3,75,0,0 107 | 99904,-100,4,2,3,75,0,0 108 | 100495,-100,4,2,3,60,0,0 109 | 101677,-90.9090909090909,4,2,3,60,0,0 110 | 104042,-90.9090909090909,4,2,3,70,0,0 111 | 105076,-71.4285714285714,4,2,3,70,0,0 112 | 105372,-66.6666666666667,4,2,3,55,0,0 113 | 105668,-66.6666666666667,4,2,3,65,0,0 114 | 105963,-100,4,2,3,75,0,0 115 | 106407,-66.6666666666667,4,2,3,75,0,1 116 | 106702,-83.3333333333333,4,2,3,75,0,1 117 | 114091,-90.9090909090909,4,2,3,75,0,1 118 | 114978,-83.3333333333333,4,2,3,75,0,1 119 | 115126,-100,4,2,3,75,0,1 120 | 115421,-83.3333333333333,4,2,3,75,0,1 121 | 115717,-100,4,2,3,75,0,1 122 | 115865,-111.111111111111,4,2,3,75,0,1 123 | 116086,-100,4,2,3,75,0,1 124 | 116604,-100,4,2,3,75,0,1 125 | 117934,-90.9090909090909,4,2,3,75,0,1 126 | 118229,-83.3333333333333,4,2,3,75,0,1 127 | 118451,-100,4,2,3,75,0,1 128 | 120446,-100,4,2,3,60,0,0 129 | 121776,-76.9230769230769,4,2,3,60,0,0 130 | 122072,-100,4,2,3,60,0,0 131 | 122958,-100,4,2,3,75,0,1 132 | 125027,-100,4,2,3,75,0,0 133 | 127687,-90.9090909090909,4,2,3,75,0,1 134 | 134781,-71.4285714285714,4,2,3,75,0,1 135 | 135003,-90.9090909090909,4,2,3,90,0,1 136 | 135076,-90.9090909090909,4,2,3,90,0,1 137 | 135815,-90.9090909090909,4,2,3,90,0,0 138 | 136111,-90.9090909090909,4,2,3,75,0,0 139 | 137145,-83.3333333333333,4,2,3,60,0,0 140 | 142909,-83.3333333333333,4,2,3,75,0,0 141 | 143352,-83.3333333333333,4,2,3,50,0,0 142 | 143500,-83.3333333333333,4,2,3,40,0,0 143 | 143648,-83.3333333333333,4,2,3,60,0,0 144 | 143796,-83.3333333333333,4,2,3,70,0,0 145 | 143943,-83.3333333333333,4,2,3,80,0,0 146 | 144091,-83.3333333333333,4,2,3,90,0,0 147 | 144239,-83.3333333333333,4,2,3,100,0,0 148 | 144535,-83.3333333333333,4,2,3,50,0,0 149 | 145126,-66.6666666666667,4,2,3,75,0,0 150 | 145274,-100,4,2,3,75,0,0 151 | 145421,-100,4,2,3,70,0,0 152 | 145569,-100,4,2,3,50,0,0 153 | 146604,-100,4,2,3,75,0,0 154 | 155766,-90.9090909090909,4,2,3,75,0,0 155 | 156062,-100,4,2,3,75,0,0 156 | 157244,-100,4,2,3,75,0,0 157 | 158426,-90.9090909090909,4,2,3,75,0,0 158 | 158722,-100,4,2,3,75,0,0 159 | 160495,-100,4,2,3,50,0,0 160 | 160791,-90.9090909090909,4,2,3,75,0,0 161 | 162121,-100,4,2,3,75,0,0 162 | 162269,-100,4,2,3,75,0,0 163 | 163155,-133.333333333333,4,2,3,75,0,0 164 | 163451,-90.9090909090909,4,2,3,75,0,0 165 | 165520,-100,4,2,3,55,0,0 166 | 171727,-90.9090909090909,4,2,3,60,0,1 167 | 171948,-100,4,2,3,5,0,1 168 | 172022,-90.9090909090909,4,2,3,65,0,1 169 | 172244,-100,4,2,3,5,0,1 170 | 172318,-90.9090909090909,4,2,3,70,0,1 171 | 172540,-100,4,2,3,5,0,1 172 | 172613,-153.846153846153,4,2,3,75,0,1 173 | 172909,-100,4,2,3,75,0,1 174 | 177934,-100,4,2,3,75,0,0 175 | 178229,-200,4,2,3,20,0,0 176 | 186357,-90.9090909090909,4,2,3,75,0,0 177 | 186948,-142.857142857143,4,2,3,75,0,0 178 | 187392,-142.857142857143,4,2,3,50,0,0 179 | 194485,-83.3333333333333,4,2,3,75,0,0 180 | 196407,-83.3333333333333,4,2,3,60,0,0 181 | 196850,-100,4,2,3,75,0,0 182 | 200988,-83.3333333333333,4,2,3,75,0,0 183 | 201209,-100,4,2,3,75,0,0 184 | 201579,-100,4,2,3,75,0,0 185 | 201801,-100,4,2,3,75,0,0 186 | 201875,-83.3333333333333,4,2,3,75,0,0 187 | 201948,-100,4,2,3,75,0,0 188 | 202466,-76.9230769230769,4,2,3,75,0,0 189 | 202761,-133.333333333333,4,2,3,75,0,0 190 | 202983,-100,4,2,3,75,0,0 191 | 204978,-83.3333333333333,4,2,3,75,0,0 192 | 205421,-100,4,2,3,75,0,0 193 | 205717,-100,4,2,3,60,0,0 194 | 206160,-71.4285714285714,4,2,3,60,0,0 195 | 206308,-100,4,2,3,75,0,0 196 | 209116,-111.111111111111,4,2,3,75,0,0 197 | 209707,-100,4,2,3,75,0,0 198 | 211037,-111.111111111111,4,2,3,50,0,0 199 | 211333,-111.111111111111,4,2,3,55,0,0 200 | 211628,-100,4,2,3,60,0,0 201 | 211924,-100,4,2,3,65,0,0 202 | 212219,-90.9090909090909,4,2,3,70,0,0 203 | 212515,-90.9090909090909,4,2,3,75,0,0 204 | 212810,-76.9230769230769,4,2,3,80,0,0 205 | 214436,-100,4,2,3,30,0,0 206 | 214732,-100,4,2,3,35,0,0 207 | 214879,-100,4,2,3,40,0,0 208 | 215027,-100,4,2,3,50,0,0 209 | 215175,-100,4,2,3,52,0,0 210 | 215323,-100,4,2,3,54,0,0 211 | 215471,-100,4,2,3,56,0,0 212 | 215618,-100,4,2,3,60,0,0 213 | 215766,-83.3333333333333,4,2,3,65,0,0 214 | 216062,-90.9090909090909,4,2,3,65,0,1 215 | 222269,-76.9230769230769,4,2,3,65,0,1 216 | 222564,-200,4,2,3,65,0,1 217 | 222786,-90.9090909090909,4,2,3,65,0,1 218 | 223451,-200,4,2,3,65,0,1 219 | 223673,-90.9090909090909,4,2,3,65,0,1 220 | 224338,-200,4,2,3,75,0,1 221 | 224633,-100,4,2,3,60,0,1 222 | 225224,-90.9090909090909,4,2,3,75,0,1 223 | 225668,-100,4,2,3,75,0,1 224 | 226702,-83.3333333333333,4,2,3,75,0,1 225 | 226850,-100,4,2,3,75,0,1 226 | 228771,-83.3333333333333,4,2,3,75,0,1 227 | 228919,-100,4,2,3,75,0,1 228 | 229067,-83.3333333333333,4,2,3,75,0,1 229 | 229806,-100,4,2,3,60,0,0 230 | 229953,-100,4,2,3,50,0,0 231 | 231727,-100,4,2,3,60,0,0 232 | 232318,-100,4,2,3,60,0,0 233 | 232540,-100,4,2,3,65,0,0 234 | 232835,-100,4,2,3,5,0,0 235 | 232909,-100,4,2,3,60,0,0 236 | 234091,-100,4,2,3,50,0,0 237 | 234239,-100,4,2,3,55,0,0 238 | 234387,-100,4,2,3,60,0,0 239 | 234535,-100,4,2,3,65,0,0 240 | 234682,-100,4,2,3,75,0,1 241 | 235865,-83.3333333333333,4,2,3,75,0,1 242 | 236012,-100,4,2,3,75,0,1 243 | 243549,-100,4,2,3,60,0,1 244 | 244141,-58.8235294117647,4,2,3,75,0,1 245 | 244288,-100,4,2,3,75,0,1 246 | 244436,-100,4,2,3,75,0,0 247 | 246209,-83.3333333333333,4,2,3,75,0,0 248 | 246431,-100,4,2,3,75,0,0 249 | 250052,-83.3333333333333,4,2,3,75,0,0 250 | 250643,-100,4,2,3,75,0,0 251 | 253303,-181.818181818181,4,2,3,75,0,0 252 | 253599,-83.3333333333333,4,2,4,70,0,1 253 | 257884,-50,4,2,4,70,0,1 254 | 258032,-76.9230769230769,4,2,4,70,0,1 255 | 258328,-62.5,4,2,4,80,0,1 256 | 259806,-71.4285714285714,4,2,4,80,0,1 257 | 260027,-83.3333333333333,4,2,4,80,0,1 258 | 260249,-71.4285714285714,4,2,3,80,0,1 259 | 260544,-71.4285714285714,4,2,3,75,0,1 260 | 261136,-58.8235294117647,4,2,3,75,0,1 261 | 261579,-76.9230769230769,4,2,3,75,0,1 262 | 262466,-100,4,2,3,55,0,0 263 | 265126,-100,4,2,3,60,0,0 264 | 266751,-100,4,2,3,75,0,0 265 | 267786,-100,4,2,3,20,0,1 266 | 267860,-100,4,2,3,24,0,1 267 | 267934,-100,4,2,3,28,0,1 268 | 268008,-100,4,2,3,32,0,1 269 | 268081,-100,4,2,3,36,0,1 270 | 268155,-100,4,2,3,40,0,1 271 | 268229,-100,4,2,3,44,0,1 272 | 268303,-100,4,2,3,48,0,1 273 | 268377,-83.3333333333333,4,2,3,48,0,1 274 | 268451,-100,4,2,3,48,0,1 275 | 268525,-100,4,2,3,52,0,1 276 | 268599,-100,4,2,3,56,0,1 277 | 268673,-100,4,2,3,60,0,1 278 | 268746,-100,4,2,3,64,0,1 279 | 268820,-100,4,2,3,68,0,1 280 | 268894,-100,4,2,3,72,0,0 281 | 268968,-83.3333333333333,4,2,3,75,0,1 282 | 269264,-100,4,2,3,75,0,1 283 | 269855,-83.3333333333333,4,2,3,75,0,1 284 | 270150,-100,4,2,3,75,0,1 285 | 271924,-71.4285714285714,4,2,3,75,0,1 286 | 272072,-100,4,2,3,75,0,1 287 | 275766,-142.857142857143,4,2,3,75,0,1 288 | 275988,-100,4,2,3,75,0,1 289 | 278278,-200,4,2,3,55,0,0 290 | 279040,295.566502463054,4,2,3,55,1,0 291 | 280813,-66.6666666666667,4,2,3,75,0,1 292 | 282882,-66.6666666666667,4,2,3,75,0,0 293 | 283160,295.566502463054,4,2,3,75,1,0 294 | 283160,-125,4,2,3,55,0,0 295 | 284342,295.566502463054,4,2,3,55,1,0 296 | 284649,295.566502463054,4,2,3,55,1,0 297 | 284944,-125,4,2,3,55,0,0 298 | 285240,-100,4,2,3,55,0,0 299 | 286717,295.566502463054,4,2,3,55,1,0 300 | 286717,-76.9230769230769,4,2,3,65,0,0 301 | 287308,-100,4,2,3,65,0,0 302 | 287899,-125,4,2,3,60,0,0 303 | 288194,-125,4,2,3,55,0,0 304 | 288490,-125,4,2,3,50,0,0 305 | 288785,-125,4,2,3,45,0,0 306 | 289081,-125,4,2,3,40,0,0 307 | 289377,-125,4,2,3,40,0,0 308 | 289672,-125,4,2,3,40,0,0 309 | 289968,-200,4,2,3,40,0,0 310 | 290244,289.855072463768,4,2,3,65,1,0 311 | 290244,-90.9090909090909,4,2,3,65,0,1 312 | 290388,-90.9090909090909,4,2,3,70,0,1 313 | 290533,-90.9090909090909,4,2,3,75,0,1 314 | 290678,-90.9090909090909,4,2,3,80,0,1 315 | 291113,309.278350515464,4,2,3,75,1,1 316 | 291423,298.507462686567,4,2,3,75,1,1 317 | 291572,-100,4,2,3,60,0,1 318 | 292020,-100,4,2,3,60,0,1 319 | 292169,-100,4,2,3,60,0,1 320 | 292467,-100,4,2,3,75,0,1 321 | 292617,295.566502463054,4,2,3,75,1,0 322 | 292617,-76.9230769230769,4,2,3,50,0,1 323 | 292764,-100,4,2,3,75,0,1 324 | 292912,-74.0740740740741,4,2,3,50,0,1 325 | 293060,-100,4,2,3,60,0,0 326 | 301188,-50,4,2,3,60,0,0 327 | 301336,-100,4,2,3,60,0,0 328 | 307099,-83.3333333333333,4,2,3,60,0,0 329 | 307321,-100,4,2,3,60,0,0 330 | 307690,-83.3333333333333,4,2,3,60,0,0 331 | 307912,-100,4,2,3,60,0,0 332 | 311533,-90.9090909090909,4,2,3,75,0,1 333 | 328084,-100,4,2,3,75,0,0 334 | 331631,-100,4,2,3,85,0,0 335 | 332814,-100,4,2,3,75,0,0 336 | 333996,-100,4,2,3,65,0,0 337 | 335178,-100,4,2,3,55,0,0 338 | 336360,-100,4,2,3,45,0,0 339 | 337543,-100,4,2,3,35,0,0 340 | 338725,-100,4,2,3,25,0,0 341 | 339464,-100,4,2,3,15,0,0 342 | 340203,-100,4,2,3,5,0,0 343 | 344 | 345 | [Colours] 346 | Combo1 : 172,172,255 347 | Combo2 : 246,246,246 348 | Combo3 : 172,209,255 349 | Combo4 : 255,255,232 350 | Combo5 : 179,255,255 351 | SliderTrackOverride : 157,206,255 352 | SliderBorder : 157,206,255 353 | 354 | [HitObjects] 355 | 0,283,950,6,0,B|79:274|126:342|126:342|159:266|238:257,1,285,2|2,0:0|0:0,0:0:0:0: 356 | 90,50,2664,2,0,B|115:41|137:44|159:58|173:77|178:92|181:112|178:129|172:144|162:159|142:179|104:190|104:190|138:205|146:241,1,285,2|0,0:0|0:0,0:0:0:0: 357 | 322,143,4378,2,0,B|363:197|367:262|367:262|323:324|323:324|371:390|371:390|440:346|440:346|422:274|422:274|448:214|518:174,1,570,2|2,0:0|0:0,0:0:0:0: 358 | 434,73,7378,5,0,0:0:0:0: 359 | 298,31,7807,2,0,B|285:63|314:103|314:103|259:114|230:167|230:167|173:137|162:65|162:65|110:98|91:142|97:182|136:217|136:217|132:253|93:270|93:270|115:316|76:358,1,665,2|0,0:0|0:0,0:0:0:0: 360 | 137,219,11235,6,0,B|219:202|232:90|232:90|262:138|304:157|304:157|346:155|368:132|368:132|394:162|411:171|431:177|452:178|467:175|483:168|497:156|503:135|501:117|487:101|468:94|444:99|435:121|440:136|451:146|470:147,1,570,2|2,0:0|0:0,0:0:0:0: 361 | 449,136,14235,5,0,0:0:0:0: 362 | 412,233,14664,2,0,B|421:292|353:334|353:334|332:272|332:272|276:242|270:177|270:177|221:234|221:234|159:238|137:292,1,475,2|0,0:0|0:0,0:0:0:0: 363 | 63,316,17235,6,0,P|107:308|144:276,1,95 364 | 206,200,18092,2,0,L|212:128,3,47.5 365 | 313,196,18949,2,0,L|317:245,3,47.5 366 | 433,214,19807,2,0,L|439:166,3,47.5 367 | 470,72,20664,5,0,0:0:0:0: 368 | 300,89,21092,2,0,B|255:75|255:75|207:90,1,95 369 | 258,337,21949,5,0,0:0:0:0: 370 | 335,267,22378,2,0,B|392:204|392:204|323:126|323:126|181:255|181:255|113:176|113:176|181:111,1,570,2|0,0:0|0:0,0:0:0:0: 371 | 256,192,25164,12,2,29235,0:0:0:0: 372 | 448,108,29560,6,0,B|401:120|368:94|368:94|342:98|342:98|308:71|247:86,1,199.499993911743 373 | 101,189,30742,2,0,B|91:163|57:158|57:158|118:131|170:190,1,166.249994926453,2|2,0:0|0:0,0:0:0:0: 374 | 318,280,31924,2,0,P|406:295|334:231,1,237.5 375 | 254,319,33697,1,0,0:0:0:0: 376 | 345,223,34289,2,0,P|344:146|271:125,1,142.5 377 | 145,65,35471,6,0,P|110:68|73:87,2,71.2500027179719 378 | 270,254,36653,2,0,P|233:252|202:238,1,71.2500027179719 379 | 308,227,37244,6,0,P|344:224|378:234,2,71.2500027179719 380 | 440,126,38131,1,0,0:0:0:0: 381 | 458,95,38279,1,0,0:0:0:0: 382 | 462,54,38426,2,0,P|418:33|354:34,1,99.7499969558717 383 | 204,122,39018,2,0,B|122:142|122:142|35:102,1,167.200005102539 384 | 291,216,40200,2,0,P|312:292|255:368,1,167.200005102539 385 | 175,228,41087,6,0,P|151:230|129:237,1,47.0249998564911 386 | 49,341,41382,2,0,B|108:345|166:379|166:379|155:352|164:323,1,167.200005102539 387 | 290,100,42564,2,0,B|248:139|266:196|266:196|238:169|204:173,1,167.200005102539 388 | 56,211,43747,1,0,0:0:0:0: 389 | 56,211,44929,6,0,P|77:206|110:207,1,47.5 390 | 426,276,46111,1,0,0:0:0:0: 391 | 441,305,46407,6,0,P|437:340|420:362,1,47.5,2|0,0:0|0:0,0:0:0:0: 392 | 385,202,46702,2,0,P|409:192|447:194,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 393 | 309,319,46998,2,0,P|289:297|280:267,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 394 | 232,155,47293,2,0,P|234:124|247:96,1,61.7499971733095,2|0,0:0|0:0,0:0:0:0: 395 | 406,13,47589,2,0,P|437:7|491:20,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 396 | 180,352,47884,6,0,P|147:335|125:306,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 397 | 53,198,48180,2,0,P|45:161|57:125,1,76,2|0,0:0|0:0,0:0:0:0: 398 | 171,0,48476,5,14,0:0:0:0: 399 | 188,44,49067,5,8,0:0:0:0: 400 | 190,91,49140,1,2,0:0:0:0: 401 | 184,138,49214,1,2,0:0:0:0: 402 | 166,183,49288,1,2,0:0:0:0: 403 | 138,228,49362,5,8,0:0:0:0: 404 | 193,239,49435,1,8,0:0:0:0: 405 | 243,264,49509,1,8,0:0:0:0: 406 | 284,302,49583,1,8,0:0:0:0: 407 | 323,342,49658,6,0,P|399:337|444:309,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 408 | 124,315,49953,2,0,P|170:348|247:356,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 409 | 54,154,50249,2,0,P|32:209|40:265,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 410 | 171,78,50544,2,0,P|132:39|63:20,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 411 | 157,186,50840,5,2,0:0:0:0: 412 | 331,132,50988,1,2,0:0:0:0: 413 | 286,275,51136,1,8,0:0:0:0: 414 | 220,87,51283,1,2,0:0:0:0: 415 | 440,64,51431,2,0,P|466:121|416:279,1,227.999993041992,6|8,0:0|0:0,0:0:0:0: 416 | 253,375,51875,5,2,0:0:0:0: 417 | 441,322,52022,2,0,P|477:264|487:194,1,113.999996520996,6|0,0:0|0:0,0:0:0:0: 418 | 440,64,52318,2,0,P|384:28|321:21,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 419 | 137,156,52613,1,2,0:0:0:0: 420 | 375,188,52761,1,2,0:0:0:0: 421 | 135,126,52909,1,8,0:0:0:0: 422 | 389,162,53057,1,2,0:0:0:0: 423 | 288,17,53205,6,0,P|226:-1|162:7,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 424 | 17,61,53500,5,8,0:0:0:0: 425 | 2,231,53648,1,2,0:0:0:0: 426 | 214,149,53796,2,0,P|157:150|103:169,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 427 | 268,345,54091,2,0,P|224:296|175:271,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 428 | 19,235,54387,1,6,0:0:0:0: 429 | 33,384,54535,1,2,0:0:0:0: 430 | 182,273,54682,5,8,0:0:0:0: 431 | 352,365,54830,1,2,0:0:0:0: 432 | 512,287,54978,2,0,P|513:231|480:175,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 433 | 409,71,55274,6,0,P|363:38|307:31,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 434 | 82,109,55569,1,2,0:0:0:0: 435 | 311,144,55717,1,2,0:0:0:0: 436 | 173,1,55865,1,8,0:0:0:0: 437 | 202,232,56012,1,2,0:0:0:0: 438 | 0,231,56160,2,0,P|50:207|107:211,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 439 | 273,352,56456,6,0,P|217:352|173:317,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 440 | 272,200,56751,6,0,P|274:127|229:184,1,190,2|0,0:0|0:0,0:0:0:0: 441 | 402,249,57047,2,0,P|425:196|423:140,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 442 | 291,10,57342,2,0,P|352:27|411:69,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 443 | 112,113,57638,2,0,P|139:63|183:28,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 444 | 298,34,57934,6,0,P|329:41|354:57,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 445 | 80,16,58081,1,8,0:0:0:0: 446 | 208,165,58229,2,0,B|266:178|266:178|342:142,1,113.999996520996,10|0,0:2|0:0,0:0:0:0: 447 | 91,240,58525,5,8,0:0:0:0: 448 | 255,369,58673,1,2,0:0:0:0: 449 | 432,272,58820,2,0,P|446:220|411:150,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 450 | 351,27,59116,6,0,P|281:19|219:45,1,113.999996520996,6|0,0:0|0:0,0:0:0:0: 451 | 13,195,59411,2,0,B|-24:140|20:83,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 452 | 246,155,59707,1,2,0:0:0:0: 453 | 85,0,59855,1,2,0:0:0:0: 454 | 131,169,60003,2,0,B|118:238|118:238|146:316|146:316|131:397,1,227.999993041992,8|2,0:0|0:0,0:0:0:0: 455 | 12,317,60446,5,2,0:0:0:0: 456 | 244,237,60594,2,0,P|306:274|328:324,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 457 | 503,223,60889,2,0,P|456:254|433:305,1,113.999996520996,0|2,0:0|0:0,0:0:0:0: 458 | 378,375,61185,2,0,L|374:261,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 459 | 364,31,61480,1,6,0:0:0:0: 460 | 512,91,61628,5,2,0:0:0:0: 461 | 265,183,61776,1,8,0:0:0:0: 462 | 281,7,61924,1,2,0:0:0:0: 463 | 184,75,62072,1,2,0:0:0:0: 464 | 364,123,62219,1,2,0:0:0:0: 465 | 127,193,62367,2,0,P|70:181|30:142,2,113.999996520996,8|0|2,0:0|0:0|0:0,0:0:0:0: 466 | 235,155,62810,5,2,0:0:0:0: 467 | 409,255,62958,2,0,P|407:312|368:364,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 468 | 213,137,63254,2,0,P|219:79|266:23,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 469 | 432,266,63549,2,0,B|447:321|392:382,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 470 | 216,264,63845,5,6,0:0:0:0: 471 | 423,180,63993,1,2,0:0:0:0: 472 | 191,250,64141,1,8,0:0:0:0: 473 | 426,160,64288,1,2,0:0:0:0: 474 | 258,363,64436,2,0,P|182:356|146:327,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 475 | 47,88,64732,2,0,B|161:55|161:55|270:92,1,227.999993041992,8|2,0:0|0:0,0:0:0:0: 476 | 352,31,65175,5,2,0:0:0:0: 477 | 444,179,65323,2,0,P|458:237|431:287,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 478 | 288,259,65618,1,12,0:0:0:0: 479 | 300,232,65692,1,0,0:0:0:0: 480 | 297,203,65766,1,0,0:0:0:0: 481 | 284,177,65840,1,0,0:0:0:0: 482 | 302,141,65914,5,12,0:0:0:0: 483 | 340,116,65988,1,0,0:0:0:0: 484 | 397,118,66062,1,0,0:0:0:0: 485 | 447,143,66136,1,0,0:0:0:0: 486 | 500,197,66209,5,12,0:0:0:0: 487 | 507,172,66505,5,12,0:0:0:0: 488 | 192,203,66653,1,12,0:0:0:0: 489 | 512,145,66948,5,12,0:0:0:0: 490 | 169,190,67096,1,12,0:0:0:0: 491 | 430,127,67392,6,0,L|293:144,1,123.499994346619,10|2,0:0|0:0,0:0:0:0: 492 | 87,165,67687,1,6,0:0:0:0: 493 | 360,304,67835,1,6,0:0:0:0: 494 | 225,32,67983,1,12,0:0:0:0: 495 | 163,326,68131,1,6,0:0:0:0: 496 | 163,326,68574,6,0,P|187:258|134:198,1,132.999995941162,6|2,0:0|0:0,0:0:0:0: 497 | 50,260,69017,1,2,0:0:0:0: 498 | 27,234,69165,2,0,B|4:191|4:191|42:96,2,132.999995941162,8|2|2,0:0|0:0|0:0,0:0:0:0: 499 | 163,326,69904,1,2,0:0:0:0: 500 | 197,348,70052,2,0,P|242:343|270:326,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 501 | 360,214,70347,2,0,P|304:163|232:165,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 502 | 96,249,70939,6,0,L|110:388,1,132.999995941162,2|2,0:0|0:0,0:0:0:0: 503 | 109,381,71382,1,2,0:0:0:0: 504 | 285,347,71530,2,0,B|330:355|330:355|386:334|354:267,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 505 | 211,208,72121,6,0,L|216:141,2,66.4999979705811,2|2|2,0:0|0:0|0:0,0:0:0:0: 506 | 297,63,72564,1,2,0:0:0:0: 507 | 152,113,72712,2,0,P|126:156|146:220,1,99.7499969558717,8|0,0:0|0:0,0:0:0:0: 508 | 86,306,73008,6,0,P|59:293|18:294,1,66.4999979705811,8|0,0:0|0:0,0:0:0:0: 509 | 227,382,73303,6,0,P|294:351|374:371,1,132.999995941162,6|2,0:0|0:0,0:0:0:0: 510 | 470,326,73746,1,2,0:0:0:0: 511 | 488,293,73894,2,0,P|493:230|431:199,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 512 | 395,60,74338,5,2,0:0:0:0: 513 | 347,156,74485,5,2,0:0:0:0: 514 | 319,130,74633,2,0,P|285:126|250:136,1,66.4999979705811,8|2,0:0|0:0,0:0:0:0: 515 | 219,29,74929,5,2,0:0:0:0: 516 | 171,195,75076,2,0,B|153:263|153:263|203:325,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 517 | 281,238,75520,5,2,0:0:0:0: 518 | 17,225,75668,2,0,P|2:290|39:350,1,132.999995941162,6|2,0:0|0:0,0:0:0:0: 519 | 127,383,76111,5,2,0:0:0:0: 520 | 136,138,76259,2,0,P|87:96|2:99,1,132.999995941162,8|8,0:0|0:0,0:0:0:0: 521 | 17,225,76702,5,2,0:0:0:0: 522 | 41,252,76776,1,2,0:0:0:0: 523 | 75,263,76850,1,2,0:0:0:0: 524 | 212,110,76998,1,8,0:0:0:0: 525 | 169,224,77145,6,0,P|165:270|187:307,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 526 | 301,186,77441,1,8,0:0:0:0: 527 | 308,300,77589,5,8,0:0:0:0: 528 | 437,205,77737,2,0,P|446:250|434:290,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 529 | 259,346,78032,6,0,P|246:274|303:233,1,132.999995941162,6|2,0:0|0:0,0:0:0:0: 530 | 301,186,78475,1,2,0:0:0:0: 531 | 212,110,78623,2,0,B|145:92|145:92|68:117,2,132.999995941162,8|2|2,0:0|0:0|0:0,0:0:0:0: 532 | 252,18,79362,5,8,0:0:0:0: 533 | 355,104,79510,2,0,P|352:146|333:171,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 534 | 246,232,79806,2,0,B|341:225|341:225|388:251,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 535 | 497,372,80397,6,0,B|469:331|469:331|487:228,1,132.999995941162,6|2,0:0|0:0,0:0:0:0: 536 | 432,142,80840,1,2,0:0:0:0: 537 | 374,243,80988,2,0,L|362:309,1,66.4999979705811,8|0,0:0|0:0,0:0:0:0: 538 | 251,371,81283,2,0,L|265:306,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 539 | 159,365,81579,5,2,0:0:0:0: 540 | 80,348,81727,1,8,0:0:0:0: 541 | 40,278,81875,1,2,0:0:0:0: 542 | 53,199,82022,1,2,0:0:0:0: 543 | 0,66,82170,2,0,B|3:103|-10:140|-10:140|13:110|66:113,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 544 | 214,85,82761,6,0,P|248:100|291:100,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 545 | 378,93,83057,2,0,P|409:88|441:62,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 546 | 153,5,83352,1,8,0:0:0:0: 547 | 223,183,83648,6,0,L|200:283,1,80.750001540184,2|2,0:0|0:0,0:0:0:0: 548 | 125,142,83943,1,2,0:0:0:0: 549 | 328,216,84091,2,0,P|377:213|423:185,2,80.750001540184,8|0|2,0:0|0:0|0:0,0:0:0:0: 550 | 83,328,84535,2,0,P|54:274|67:214,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 551 | 208,83,84830,2,0,P|270:76|325:100,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 552 | 404,301,85126,2,0,P|475:284|511:233,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 553 | 251,161,85421,5,0,0:0:0:0: 554 | 157,354,85569,2,0,P|103:346|49:307,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 555 | 238,181,85865,6,0,P|230:215|238:245,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 556 | 285,373,86012,1,12,0:0:0:0: 557 | 455,169,86160,2,0,P|389:94|248:79,1,227.999993041992,2|8,0:0|0:0,0:0:0:0: 558 | 41,120,86604,5,8,0:0:0:0: 559 | 251,161,86751,1,8,0:0:0:0: 560 | 132,6,86899,1,8,0:0:0:0: 561 | 158,259,87047,1,8,0:0:0:0: 562 | 15,123,87195,1,8,0:0:0:0: 563 | 56,377,87342,1,8,0:0:0:0: 564 | 191,338,87490,2,0,B|248:344|279:393|279:393|251:338|291:279,1,213.750008153916,6|0,0:0|0:0,0:0:0:0: 565 | 375,217,87786,2,0,P|378:166|345:115,1,104.500003189087,12|0,0:0|0:0,0:0:0:0: 566 | 249,195,88081,5,2,0:0:0:0: 567 | 496,153,88229,1,2,0:0:0:0: 568 | 459,297,88377,2,0,B|373:330|373:330|264:283,1,190,8|2,0:0|0:0,0:0:0:0: 569 | 232,69,88820,1,2,0:0:0:0: 570 | 346,0,88968,2,0,L|447:11,1,95,8|0,0:0|0:0,0:0:0:0: 571 | 360,127,89264,5,2,0:0:0:0: 572 | 246,189,89411,1,2,0:0:0:0: 573 | 53,124,89559,2,0,P|106:104|167:117,1,95,8|0,0:0|0:0,0:0:0:0: 574 | 320,265,89855,6,0,B|314:317|314:317|321:334|321:334|313:385,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 575 | 428,327,90150,1,8,0:0:0:0: 576 | 246,189,90298,2,0,P|193:179|142:200,1,95,2|0,0:0|0:0,0:0:0:0: 577 | 66,337,90594,1,2,0:0:0:0: 578 | 2,153,90742,2,0,P|2:106|50:62,1,95,8|2,0:0|0:0,0:0:0:0: 579 | 256,15,91037,6,0,B|243:94|243:94|264:85,1,95,2|0,0:0|0:0,0:0:0:0: 580 | 422,115,91333,6,0,L|362:109,1,47.5,8|0,0:0|0:0,0:0:0:0: 581 | 242,200,91480,2,0,L|172:194,2,47.5,2|0|0,0:0|0:0|0:0,0:0:0:0: 582 | 407,261,91776,1,2,0:0:0:0: 583 | 223,39,91924,6,0,L|231:115,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 584 | 438,243,92072,2,0,L|430:178,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 585 | 485,40,92219,2,0,B|400:68|400:68|308:32,1,185.249991519928,6|0,0:0|0:0,0:0:0:0: 586 | 223,39,92515,5,8,0:0:0:0: 587 | 105,110,92663,2,0,P|125:167|111:212,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 588 | 17,366,92958,6,0,P|31:336|56:323,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 589 | 153,297,93106,6,0,B|284:309,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 590 | 399,384,93402,1,2,0:0:0:0: 591 | 483,319,93549,1,2,0:0:0:0: 592 | 424,230,93697,2,0,B|337:227|337:227|296:216|276:170,1,156.750004783631,8|0,0:0|0:0,0:0:0:0: 593 | 260,64,93993,6,0,L|332:57,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 594 | 199,218,94141,2,0,L|139:209,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 595 | 25,248,94288,6,0,P|28:163|90:176,1,156.750004783631,8|0,0:0|0:0,0:0:0:0: 596 | 220,346,94584,2,0,B|236:354|245:377|245:377|264:258,1,156.750004783631,2|0,0:0|0:0,0:0:0:0: 597 | 195,114,94879,2,0,P|156:91|82:100,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 598 | 379,115,95175,1,2,0:0:0:0: 599 | 289,6,95323,1,2,0:0:0:0: 600 | 189,126,95471,2,0,B|173:182|173:182|202:235,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 601 | 425,334,95766,5,2,0:0:0:0: 602 | 414,305,95839,1,2,0:0:0:0: 603 | 393,280,95913,1,2,0:0:0:0: 604 | 365,265,95987,1,2,0:0:0:0: 605 | 334,257,96062,6,0,L|217:261,1,104.500003189087,12|2,0:0|0:0,0:0:0:0: 606 | 442,373,96283,1,2,0:0:0:0: 607 | 470,355,96357,2,0,B|483:328|483:328|475:226,1,104.500003189087,12|2,0:0|0:0,0:0:0:0: 608 | 439,40,96653,1,6,0:0:0:0: 609 | 365,265,96801,1,2,0:0:0:0: 610 | 304,8,96948,2,0,L|297:119,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 611 | 272,353,97244,5,8,0:0:0:0: 612 | 201,256,97392,1,2,0:0:0:0: 613 | 75,291,97540,2,0,B|68:372|68:372|96:340,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 614 | 201,256,97835,2,0,L|207:189,2,52.2500015945435,8|0|0,0:0|0:0|0:0,0:0:0:0: 615 | 65,113,98131,2,0,P|47:63|70:17,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 616 | 346,5,98426,5,8,0:0:0:0: 617 | 270,91,98574,1,2,0:0:0:0: 618 | 355,167,98722,2,0,L|487:148,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 619 | 201,256,99017,2,0,L|316:261,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 620 | 113,152,99313,2,0,P|58:237|94:354,1,227.999993041992,2|8,0:0|0:0,0:0:0:0: 621 | 399,374,99756,2,0,P|426:315|411:256,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 622 | 366,33,100052,1,2,0:0:0:0: 623 | 492,136,100200,2,0,B|398:142,1,95,8|2,0:0|0:0,0:0:0:0: 624 | 135,235,100495,1,2,0:0:0:0: 625 | 241,40,100643,1,8,0:0:0:0: 626 | 277,253,100791,1,8,0:0:0:0: 627 | 114,104,100939,1,8,0:0:0:0: 628 | 349,143,101086,1,8,0:0:0:0: 629 | 145,240,101234,1,8,0:0:0:0: 630 | 241,40,101382,1,8,0:0:0:0: 631 | 276,243,101530,1,8,0:0:0:0: 632 | 225,314,101677,6,0,L|115:321,2,104.500003189087,2|0|0,0:0|0:0|0:0,0:0:0:0: 633 | 336,116,102121,1,2,0:0:0:0: 634 | 441,267,102269,5,8,0:0:0:0: 635 | 332,79,102416,1,2,0:0:0:0: 636 | 210,281,102564,2,0,L|330:272,1,104.500003189087,0|2,0:0|0:0,0:0:0:0: 637 | 241,103,102860,5,0,2:0:0:0: 638 | 233,147,103008,1,2,2:0:0:0: 639 | 225,187,103155,1,0,2:0:0:0: 640 | 216,234,103303,1,2,2:0:0:0: 641 | 141,24,103451,2,0,B|56:29|56:29|77:37|82:53,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 642 | 16,123,103746,5,8,0:0:0:0: 643 | 247,63,103894,1,8,0:0:0:0: 644 | 210,281,104042,1,6,0:0:0:0: 645 | 109,170,104190,1,8,0:0:0:0: 646 | 129,152,104264,1,8,0:0:0:0: 647 | 154,142,104338,1,8,0:0:0:0: 648 | 423,118,104485,2,0,P|470:217|416:312,1,209.000006378174,10|8,0:0|0:0,0:3:0:0: 649 | 315,383,104929,6,0,P|298:364|291:339,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 650 | 210,211,105076,6,0,L|243:45,1,132.999995941162,8|0,0:0|0:0,0:0:0:0: 651 | 123,29,105372,6,0,P|87:32|53:41,1,71.2500027179719,6|0,0:0|0:0,0:0:0:0: 652 | 277,253,105668,2,0,P|312:253|346:265,1,71.2500027179719,6|0,0:0|0:0,0:0:0:0: 653 | 374,88,105963,6,0,B|388:38,5,47.5,6|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: 654 | 426,147,106407,6,0,L|283:136,1,142.500005435944,6|0,0:0|0:0,0:0:0:0: 655 | 128,201,106702,2,0,L|14:190,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 656 | 257,271,106998,2,0,P|267:326|255:382,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 657 | 113,305,107293,6,0,P|104:277|104:249,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 658 | 391,199,107441,2,0,P|401:234|402:266,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 659 | 105,351,107589,2,0,P|89:296|96:240,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 660 | 407,174,107884,2,0,P|422:245|405:292,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 661 | 251,270,108180,5,2,0:0:0:0: 662 | 227,102,108328,1,2,0:0:0:0: 663 | 423,176,108475,2,0,P|472:159|439:104,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 664 | 163,37,108771,6,0,P|216:16|274:28,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 665 | 127,265,109067,1,8,0:0:0:0: 666 | 276,173,109214,1,2,0:0:0:0: 667 | 46,114,109362,2,0,P|27:159|48:229,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 668 | 214,253,109658,2,0,L|288:245,2,56.9999982604981,8|0|0,0:0|0:0|0:0,0:0:0:0: 669 | 43,95,109953,6,0,P|25:115|9:143,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 670 | 188,80,110101,5,0,0:0:0:0: 671 | 188,80,110175,1,0,0:0:0:0: 672 | 188,80,110249,2,0,P|213:107|231:139,1,56.9999982604981,12|0,0:0|0:0,0:0:0:0: 673 | 372,336,110397,1,0,0:0:0:0: 674 | 420,134,110544,5,2,0:0:0:0: 675 | 191,340,110692,1,2,0:0:0:0: 676 | 175,347,110766,1,2,0:0:0:0: 677 | 159,354,110840,2,0,L|40:337,1,113.999996520996,12|2,0:0|0:0,0:0:0:0: 678 | 15,197,111136,6,0,P|18:141|67:91,1,113.999996520996,2|0,0:0|0:0,0:0:0:0: 679 | 233,44,111431,2,0,P|287:35|347:53,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 680 | 479,75,111727,5,2,0:0:0:0: 681 | 316,189,111875,2,0,P|267:175|185:174,1,113.999996520996,2|8,0:0|0:0,0:0:0:0: 682 | 394,284,112170,5,2,0:0:0:0: 683 | 266,370,112318,2,0,P|209:361|152:362,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 684 | 30,352,112613,1,8,0:0:0:0: 685 | 249,260,112761,2,0,L|379:262,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 686 | 512,127,113057,1,2,0:0:0:0: 687 | 492,106,113131,1,2,0:0:0:0: 688 | 471,86,113205,2,0,P|412:69|348:96,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 689 | 262,279,113500,6,0,B|228:237|254:182|254:182|244:158|244:158|263:109|234:62,1,227.999993041992,2|8,0:0|0:0,0:0:0:0: 690 | 135,41,113943,1,2,0:0:0:0: 691 | 331,229,114091,2,0,P|393:232|441:218,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 692 | 434,25,114387,5,8,0:0:0:0: 693 | 237,69,114535,2,0,P|197:116|204:159,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 694 | 212,362,114829,5,2,0:0:0:0: 695 | 44,188,114978,2,0,P|46:131|78:85,1,113.999996520996,12|2,0:0|0:0,0:0:0:0: 696 | 418,122,115274,5,6,0:0:0:0: 697 | 260,47,115421,1,2,0:0:0:0: 698 | 251,61,115495,1,2,0:0:0:0: 699 | 241,74,115569,2,0,P|238:118|257:143,1,56.9999982604981,12|2,0:0|0:0,0:0:0:0: 700 | 285,259,115717,2,0,P|296:282|333:302,1,47.5,2|0,0:0|0:0,0:0:0:0: 701 | 511,211,115865,2,0,P|513:168|507:126,1,85.4999973907472,2|0,0:0|0:0,0:0:0:0: 702 | 356,34,116160,2,0,L|350:81,1,47.5,8|0,0:0|0:0,0:0:0:0: 703 | 264,201,116308,6,0,P|354:165|439:208,1,190,2|2,0:0|0:0,0:0:0:0: 704 | 445,349,116751,1,8,0:0:0:0: 705 | 490,173,116899,1,2,0:0:0:0: 706 | 334,355,117047,1,2,0:0:0:0: 707 | 512,283,117195,1,2,0:0:0:0: 708 | 278,231,117342,2,0,P|269:187|289:117,1,95,8|0,0:0|0:0,0:0:0:0: 709 | 337,4,117638,5,2,0:0:0:0: 710 | 458,70,117786,1,2,0:0:0:0: 711 | 265,129,117934,2,0,P|212:111|158:113,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 712 | 75,281,118229,2,0,P|107:176|36:92,1,227.999993041992,2|8,0:0|0:0,0:0:0:0: 713 | 0,200,118673,5,2,0:0:0:0: 714 | 150,107,118820,2,0,P|189:96|244:96,1,95,2|2,0:0|0:0,0:0:0:0: 715 | 404,228,119116,1,8,0:0:0:0: 716 | 25,197,119264,5,2,0:0:0:0: 717 | 25,197,119338,1,2,0:0:0:0: 718 | 25,197,119411,1,8,0:0:0:0: 719 | 282,234,119559,1,8,0:0:0:0: 720 | 7,253,119707,5,8,0:0:0:0: 721 | 274,276,119855,1,8,0:0:0:0: 722 | 48,104,120003,5,8,0:0:0:0: 723 | 192,366,120150,1,8,0:0:0:0: 724 | 217,112,120298,5,8,0:0:0:0: 725 | 53,349,120446,2,0,B|64:341|90:318|90:318|78:163,1,190,6|2,0:0|0:0,0:0:0:0: 726 | 26,47,120889,2,0,P|69:29|117:29,1,95,2|0,0:0|0:0,0:0:0:0: 727 | 337,144,121185,2,0,P|291:154|245:145,1,95,8|0,0:0|0:0,0:0:0:0: 728 | 435,81,121480,6,0,P|454:124|449:171,1,95,2|0,0:0|0:0,0:0:0:0: 729 | 130,202,121776,2,0,P|126:115|161:196,1,246.999988693238,2|0,0:0|0:0,0:0:0:0: 730 | 314,119,122219,1,2,0:0:0:0: 731 | 328,138,122293,1,2,0:0:0:0: 732 | 338,160,122367,2,0,P|344:221|321:272,1,95,8|0,0:0|0:0,0:0:0:0: 733 | 222,331,122663,2,0,B|95:322|95:322|109:316,1,142.5,2|0,0:0|0:0,0:0:0:0: 734 | 248,128,122958,6,0,L|246:81,1,47.5,8|0,0:0|0:0,0:0:0:0: 735 | 436,274,123106,2,0,L|431:226,1,47.5,2|0,0:0|0:0,0:0:0:0: 736 | 398,86,123254,6,0,L|394:38,1,47.5,8|0,0:0|0:0,0:0:0:0: 737 | 250,293,123402,1,2,0:0:0:0: 738 | 250,293,123475,1,2,0:0:0:0: 739 | 250,293,123549,2,0,L|253:218,1,47.5,8|0,0:0|0:0,0:0:0:0: 740 | 132,96,123697,5,2,0:0:0:0: 741 | 132,96,123771,1,2,0:0:0:0: 742 | 132,96,123845,6,0,L|128:26,1,47.5,8|0,0:0|0:0,0:0:0:0: 743 | 199,318,123993,1,2,0:0:0:0: 744 | 78,131,124141,1,8,0:0:0:0: 745 | 33,312,124288,1,2,0:0:0:0: 746 | 33,312,124362,1,2,0:0:0:0: 747 | 33,312,124436,6,0,L|33:397,1,47.5,8|0,0:0|0:0,0:0:0:0: 748 | 22,106,124584,1,2,0:0:0:0: 749 | 125,282,124732,2,0,L|192:271,3,47.5,8|2|2|2,0:0|0:0|0:0|0:0,0:0:0:0: 750 | 260,190,125027,2,0,L|376:171,1,95,8|2,0:0|0:0,0:0:0:0: 751 | 351,58,125323,6,0,L|398:51,2,47.5,8|2|2,0:0|0:0|0:0,0:0:0:0: 752 | 167,31,125618,1,2,0:0:0:0: 753 | 421,251,125766,6,0,L|423:328,2,47.5,8|2|2,0:0|0:0|0:0,0:0:0:0: 754 | 442,37,126062,1,2,0:0:0:0: 755 | 204,252,126209,6,0,L|213:311,2,47.5,8|2|2,0:0|0:0|0:0,0:0:0:0: 756 | 182,56,126505,1,2,0:0:0:0: 757 | 340,247,126653,2,0,L|282:204,2,47.5,8|2|2,0:0|0:0|0:0,0:0:0:0: 758 | 220,84,126948,5,2,0:0:0:0: 759 | 220,84,127022,1,2,0:0:0:0: 760 | 220,84,127096,2,0,B|160:68|108:84|108:84|130:89|140:100,1,142.5,12|0,0:0|0:0,0:0:0:0: 761 | 280,304,127392,2,0,P|308:264|308:212,1,95,12|0,0:0|0:0,0:0:0:0: 762 | 121,205,127687,2,0,L|137:321,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 763 | 145,353,127983,5,8,0:0:0:0: 764 | 360,308,128131,6,0,P|375:277|374:253,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 765 | 288,152,128278,2,0,P|237:141|188:159,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 766 | 160,192,128574,5,8,0:0:0:0: 767 | 176,290,128722,6,0,P|173:315|181:340,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 768 | 129,373,128870,2,0,L|234:377,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 769 | 358,340,129165,5,8,0:0:0:0: 770 | 89,191,129313,6,0,P|61:196|32:217,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 771 | 96,296,129461,2,0,L|88:168,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 772 | 88,144,129756,5,8,0:0:0:0: 773 | 4,292,129904,1,2,0:0:0:0: 774 | 140,76,130052,6,0,P|84:60|28:80,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 775 | 5,100,130347,5,8,0:0:0:0: 776 | 176,152,130495,2,0,P|200:142|216:122,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 777 | 202,18,130643,2,0,L|218:142,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 778 | 240,292,130939,1,8,0:0:0:0: 779 | 312,164,131086,6,0,P|336:163|363:172,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 780 | 462,179,131234,2,0,P|409:187|350:163,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 781 | 420,100,131530,1,8,0:0:0:0: 782 | 420,272,131677,6,0,L|420:344,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 783 | 376,364,131825,2,0,L|252:352,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 784 | 228,344,132121,5,8,0:0:0:0: 785 | 370,242,132269,2,0,P|400:249|423:271,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 786 | 372,356,132416,2,0,L|251:340,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 787 | 220,336,132712,5,8,0:0:0:0: 788 | 381,232,132860,2,0,P|406:238|428:256,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 789 | 464,352,133008,2,0,L|478:248,1,104.500003189087,0|2,0:0|0:0,0:0:0:0: 790 | 496,96,133303,1,8,0:0:0:0: 791 | 274,57,133451,6,0,L|325:49,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 792 | 428,33,133599,2,0,L|304:52,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 793 | 224,68,133894,1,8,0:0:0:0: 794 | 384,368,134042,2,0,P|360:376|328:376,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 795 | 230,357,134190,2,0,L|338:377,1,104.500003189087,0|2,0:0|0:0,0:0:0:0: 796 | 500,284,134485,2,0,P|496:256|472:216,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 797 | 440,108,134633,2,0,P|408:96|364:92,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 798 | 224,68,134781,6,0,P|186:165|216:260,1,199.499993911743,12|0,0:0|0:0,0:0:0:0: 799 | 343,234,135076,2,0,P|316:235|291:240,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 800 | 88,336,135224,6,0,P|90:309|96:280,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 801 | 336,108,135372,2,0,P|341:138|342:159,1,52.2500015945435,12|0,0:0|0:0,0:0:0:0: 802 | 322,360,135520,6,0,P|329:332|330:304,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 803 | 108,128,135668,2,0,P|100:166|100:194,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 804 | 428,288,135815,6,0,P|456:176|420:80,1,209.000006378174,12|2,0:0|0:0,0:0:0:0: 805 | 301,37,136259,2,0,P|250:27|200:39,1,104.500003189087,12|2,0:0|0:0,0:0:0:0: 806 | 256,136,136554,6,0,L|256:240,1,104.500003189087,12|2,0:0|0:0,0:0:0:0: 807 | 256,260,136776,1,2,0:0:0:0: 808 | 256,280,136850,1,8,0:0:0:0: 809 | 368,184,136998,1,8,0:0:0:0: 810 | 76,112,137145,2,0,P|40:200|76:284,1,170.999994781494,6|0,0:0|0:0,0:0:0:0: 811 | 144,332,137441,6,0,L|280:320,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 812 | 459,28,137737,2,0,B|408:48|356:32|356:32|332:40|332:40|280:24|220:48,1,227.999993041992,6|12,0:0|0:0,0:0:0:0: 813 | 148,60,138180,5,2,0:0:0:0: 814 | 424,331,138328,2,0,P|471:229|448:120,1,227.999993041992,6|12,0:0|0:0,0:0:0:0: 815 | 72,148,138771,5,2,0:0:0:0: 816 | 72,148,138919,2,0,B|116:144|144:176|144:176|184:120|264:128|296:168,1,227.999993041992,6|12,0:0|0:0,0:0:0:0: 817 | 432,56,139362,5,2,0:0:0:0: 818 | 432,56,139510,2,0,P|392:112|424:272,1,227.999993041992,6|12,0:0|0:0,0:0:0:0: 819 | 488,328,139953,5,2,0:0:0:0: 820 | 480,336,140101,2,0,B|424:356|424:356|304:336,1,170.999994781494,6|0,0:0|0:0,0:0:0:0: 821 | 208,344,140397,6,0,P|240:288|236:224,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 822 | 266,33,140692,2,0,P|226:89|242:241,1,227.999993041992,6|8,0:0|0:0,0:0:0:0: 823 | 33,127,141136,5,2,0:0:0:0: 824 | 97,45,141283,2,0,B|189:53|189:53|280:29,1,170.999994781494,6|0,0:0|0:0,0:0:0:0: 825 | 420,96,141579,2,0,P|434:154|404:219,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 826 | 52,352,141875,2,0,P|12:247|36:138,1,227.999993041992,6|8,0:0|0:0,0:0:0:0: 827 | 356,356,142318,5,2,0:0:0:0: 828 | 356,356,142466,2,0,B|224:344,1,113.999996520996,12|0,0:0|0:0,0:0:0:0: 829 | 112,128,142761,2,0,L|236:140,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 830 | 384,120,142983,5,2,0:0:0:0: 831 | 384,120,143057,2,0,L|392:40,3,56.9999982604981,12|2|2|2,0:0|0:0|0:0|0:0,0:0:0:0: 832 | 128,264,143352,2,0,L|120:336,3,56.9999982604981,12|2|2|2,0:0|0:0|0:0|0:0,0:0:0:0: 833 | 304,64,143648,6,0,L|297:133,1,56.9999982604981,12|0,0:0|0:0,0:0:0:0: 834 | 199,218,143796,2,0,L|262:188,1,56.9999982604981,2|2,0:0|0:0,0:0:0:0: 835 | 386,234,143943,2,0,L|327:194,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 836 | 172,84,144091,2,0,L|254:141,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 837 | 286,328,144239,2,0,L|292:232,1,56.9999982604981,12|0,0:0|0:0,0:0:0:0: 838 | 448,92,144535,1,2,0:0:0:0: 839 | 448,108,144830,5,2,0:0:0:0: 840 | 40,248,145126,2,0,P|24:184|56:104,1,142.500005435944,8|2,0:0|0:0,0:0:0:0: 841 | 256,168,145421,2,0,B|248:183|264:199|256:215,1,47.5,6|0,0:0|0:0,0:0:0:0: 842 | 256,292,145569,6,0,B|256:312|256:312|253:321|253:321|256:339,5,47.5,8|8|2|8|8|8,0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: 843 | 361,127,146012,1,14,0:0:0:0: 844 | 350,81,146085,1,8,0:0:0:0: 845 | 320,44,146159,1,8,0:0:0:0: 846 | 278,24,146233,1,8,0:0:0:0: 847 | 230,24,146307,1,10,0:0:0:0: 848 | 188,45,146381,1,8,0:0:0:0: 849 | 159,82,146455,1,8,0:0:0:0: 850 | 149,128,146529,1,8,0:0:0:0: 851 | 161,175,146604,6,0,P|220:168|265:167,1,95,6|0,0:0|0:0,0:0:0:0: 852 | 353,263,146899,2,0,P|404:256|453:255,1,95,12|0,0:0|0:0,0:0:0:0: 853 | 304,352,147195,5,2,0:0:0:0: 854 | 161,175,147342,1,2,0:0:0:0: 855 | 168,372,147490,5,8,0:0:0:0: 856 | 272,104,147638,1,2,0:0:0:0: 857 | 92,32,147786,6,0,P|68:84|76:140,1,95,2|2,0:0|0:0,0:0:0:0: 858 | 448,252,148081,2,0,P|472:200|464:144,1,95,8|2,0:0|0:0,0:0:0:0: 859 | 428,68,148377,6,0,B|376:80|376:80|318:65,1,95,2|0,0:0|0:0,0:0:0:0: 860 | 84,316,148673,2,0,B|136:304|136:304|196:320,1,95,8|0,0:0|0:0,0:0:0:0: 861 | 360,308,148968,6,0,B|348:260|348:260|371:199,1,95,6|0,0:0|0:0,0:0:0:0: 862 | 144,64,149264,2,0,B|160:112|160:112|136:176,1,95,12|0,0:0|0:0,0:0:0:0: 863 | 64,108,149559,5,2,0:0:0:0: 864 | 256,192,149707,1,2,0:0:0:0: 865 | 232,0,149855,1,8,0:0:0:0: 866 | 432,268,150003,5,2,0:0:0:0: 867 | 316,100,150150,2,0,B|268:84|268:84|208:92,1,95,2|2,0:0|0:0,0:0:0:0: 868 | 56,196,150446,2,0,B|44:248|44:248|64:292,1,95,8|0,0:0|0:0,0:0:0:0: 869 | 312,251,150742,5,8,0:0:0:0: 870 | 319,251,150815,1,2,0:0:0:0: 871 | 326,252,150889,1,2,0:0:0:0: 872 | 334,254,150963,1,2,0:0:0:0: 873 | 342,254,151037,2,0,B|364:260|388:256|388:256|404:264|404:264|424:260|440:264,1,95,8|0,0:0|0:0,0:0:0:0: 874 | 228,188,151333,6,0,P|200:236|208:280,1,95,6|0,0:0|0:0,0:0:0:0: 875 | 316,352,151628,2,0,B|368:364|368:364|420:352,1,95,12|0,0:0|0:0,0:0:0:0: 876 | 316,168,151924,5,2,0:0:0:0: 877 | 208,360,152072,1,2,0:0:0:0: 878 | 400,268,152219,2,0,P|422:212|401:160,1,95,8|2,0:0|0:0,0:0:0:0: 879 | 208,128,152515,1,2,0:0:0:0: 880 | 412,76,152663,1,2,0:0:0:0: 881 | 252,212,152810,2,0,B|220:212|199:234|199:234|168:192|104:208,1,142.5,8|0,0:0|0:0,0:0:0:0: 882 | 264,300,153106,6,0,B|303:303|324:272|324:272|380:328|336:412,1,190,6|8,0:0|0:0,0:0:0:0: 883 | 132,292,153549,1,2,0:0:0:0: 884 | 396,224,153697,2,0,P|424:184|420:120,1,95,6|0,0:0|0:0,0:0:0:0: 885 | 76,40,153993,2,0,L|96:156,1,95,12|0,0:0|0:0,0:0:0:0: 886 | 172,124,154288,6,0,P|232:112|284:132,1,95,2|0,0:0|0:0,0:0:0:0: 887 | 436,248,154584,2,0,P|468:204|464:140,1,95,8|0,0:0|0:0,0:0:0:0: 888 | 472,140,154806,1,0,0:0:0:0: 889 | 472,116,154879,2,0,L|424:113,1,47.5,8|0,0:0|0:0,0:0:0:0: 890 | 216,236,155027,1,2,0:0:0:0: 891 | 224,256,155101,1,2,0:0:0:0: 892 | 232,276,155175,2,0,L|288:272,1,47.5,2|0,0:0|0:0,0:0:0:0: 893 | 279,112,155323,6,0,L|220:108,1,47.5,2|0,0:0|0:0,0:0:0:0: 894 | 148,100,155471,1,10,0:0:0:0: 895 | 316,32,155618,5,2,0:0:0:0: 896 | 336,44,155692,1,2,0:0:0:0: 897 | 352,64,155766,2,0,P|368:104|340:176,1,104.500003189087,10|2,0:0|0:0,0:0:0:0: 898 | 188,336,156062,2,0,L|28:320,1,142.5,6|0,0:0|0:0,0:0:0:0: 899 | 200,184,156357,6,0,L|320:168,1,95,12|2,0:0|0:0,0:0:0:0: 900 | 480,136,156653,1,2,0:0:0:0: 901 | 284,304,156801,1,2,0:0:0:0: 902 | 140,56,156948,2,0,B|116:248,1,190,8|2,0:0|0:0,0:0:0:0: 903 | 44,128,157392,1,2,0:0:0:0: 904 | 328,80,157540,6,0,P|328:144|296:184,1,95,8|2,0:0|0:0,0:0:0:0: 905 | 128,240,157835,2,0,P|179:212|240:216,1,95,0|2,0:0|0:0,0:0:0:0: 906 | 360,332,158131,2,0,P|312:300|293:255,1,95,8|0,0:0|0:0,0:0:0:0: 907 | 40,336,158426,6,0,L|144:344,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 908 | 384,248,158722,1,12,0:0:0:0: 909 | 160,132,158870,1,2,0:0:0:0: 910 | 232,336,159017,1,2,0:0:0:0: 911 | 396,136,159165,5,2,0:0:0:0: 912 | 188,44,159313,2,0,P|133:42|88:72,1,95,8|0,0:0|0:0,0:0:0:0: 913 | 316,84,159609,5,8,0:0:0:0: 914 | 353,50,159682,1,2,0:0:0:0: 915 | 403,39,159756,1,2,0:0:0:0: 916 | 452,54,159830,1,2,0:0:0:0: 917 | 487,91,159904,2,0,P|492:124|484:188,1,95,8|2,0:0|0:0,0:0:0:0: 918 | 160,128,160200,2,0,L|152:174,3,47.5,2|2|2|2,0:0|0:0|0:0|0:0,0:0:0:0: 919 | 34,233,160495,5,8,0:0:0:0: 920 | 50,280,160568,1,8,0:0:0:0: 921 | 88,313,160642,1,8,0:0:0:0: 922 | 138,317,160716,1,8,0:0:0:0: 923 | 184,304,160791,6,0,B|216:304|240:280|240:280|256:248|280:224,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 924 | 320,56,161086,2,0,B|304:80|302:119|302:119|318:159|312:184,1,104.500003189087,12|0,0:0|0:0,0:0:0:0: 925 | 432,304,161382,2,0,B|423:278|400:256|400:256|375:246|359:230,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 926 | 432,304,161677,6,0,P|476:196|428:96,1,209.000006378174,8|2,0:0|0:0,0:0:0:0: 927 | 388,60,162121,5,2,0:0:0:0: 928 | 280,248,162269,2,0,P|336:260|396:232,1,95,8|0,0:0|0:0,0:0:0:0: 929 | 232,72,162564,2,0,P|184:56|120:72,1,95,0|2,0:0|0:0,0:0:0:0: 930 | 32,240,162786,1,2,0:0:0:0: 931 | 32,240,162860,2,0,P|88:256|144:240,1,95,8|0,0:0|0:0,0:0:0:0: 932 | 380,152,163155,6,0,B|432:136|432:136|496:148,1,106.875004076958,6|0,0:0|0:0,0:0:0:0: 933 | 312,76,163451,2,0,B|288:160|288:160|320:244,1,156.750004783631,12|0,0:0|0:0,0:0:0:0: 934 | 444,348,163746,6,0,B|388:336|388:336|320:348,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 935 | 213,202,164042,2,0,P|277:226|333:218,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 936 | 72,92,164338,2,0,P|128:92|176:116,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 937 | 360,112,164633,5,8,0:0:0:0: 938 | 232,328,164781,1,8,0:0:0:0: 939 | 360,112,164929,1,2,0:0:0:0: 940 | 232,328,165076,1,8,0:0:0:0: 941 | 144,96,165224,1,8,0:0:0:0: 942 | 400,312,165372,1,8,0:0:0:0: 943 | 104,288,165520,6,0,L|240:256,1,95,2|0,0:0|0:0,0:0:0:0: 944 | 56,192,165815,1,8,0:0:0:0: 945 | 304,256,165963,2,0,L|440:224,1,95,0|2,0:0|0:0,0:0:0:0: 946 | 216,56,166258,1,2,0:0:0:0: 947 | 360,112,166407,2,0,L|472:88,1,95,8|0,0:0|0:0,0:0:0:0: 948 | 168,208,166702,6,0,L|264:184,1,95,2|2,0:0|0:0,0:0:0:0: 949 | 216,56,166998,1,8,0:0:0:0: 950 | 88,336,167145,2,0,L|208:304,1,95,2|0,0:0|0:0,0:0:0:0: 951 | 400,304,167441,1,2,0:0:0:0: 952 | 168,125,167589,2,0,P|121:114|75:122,1,95,8|0,0:0|0:0,0:0:0:0: 953 | 305,311,167884,6,0,L|401:303,1,95,2|0,0:0|0:0,0:0:0:0: 954 | 228,192,168180,1,8,0:0:0:0: 955 | 384,384,168328,2,0,L|484:372,1,95,2|2,0:0|0:0,0:0:0:0: 956 | 512,252,168623,5,2,0:0:0:0: 957 | 392,136,168771,2,0,P|338:129|297:145,1,95,8|2,0:0|0:0,0:0:0:0: 958 | 504,148,169067,1,2,0:0:0:0: 959 | 312,36,169214,1,2,0:0:0:0: 960 | 228,224,169362,5,8,0:0:0:0: 961 | 316,192,169510,1,2,0:0:0:0: 962 | 222,31,169658,1,2,0:0:0:0: 963 | 222,127,169806,1,2,0:0:0:0: 964 | 408,36,169953,1,8,0:0:0:0: 965 | 312,36,170101,1,0,0:0:0:0: 966 | 316,192,170249,6,0,P|372:200|436:176,1,95,2|0,0:0|0:0,0:0:0:0: 967 | 312,112,170544,1,8,0:0:0:0: 968 | 392,312,170692,2,0,P|440:320|504:304,1,95,2|0,0:0|0:0,0:0:0:0: 969 | 448,184,170988,1,2,0:0:0:0: 970 | 312,112,171136,2,0,P|265:104|218:112,1,95,8|0,0:0|0:0,0:0:0:0: 971 | 256,321,171431,5,2,0:0:0:0: 972 | 231,382,171727,2,0,P|190:330|237:258,1,156.750004783631,8|0,0:0|0:0,0:0:0:0: 973 | 276,259,172022,2,0,P|322:317|281:382,1,156.750004783631,8|0,0:0|0:0,0:0:0:0: 974 | 256,321,172318,2,0,B|240:288|256:241|256:241|272:208|255:163,1,156.750004783631,8|0,0:0|0:0,0:0:0:0: 975 | 256,72,172613,6,0,B|264:96|256:120|256:120|248:144|256:167,1,92.6249957599642,8|0,0:0|0:0,0:0:0:0: 976 | 136,104,172909,6,0,L|208:96,1,47.5,2|0,0:0|0:0,0:0:0:0: 977 | 376,144,173057,2,0,L|296:152,1,47.5,2|0,0:0|0:0,0:0:0:0: 978 | 64,80,173205,6,0,L|48:152,1,47.5,8|0,0:0|0:0,0:0:0:0: 979 | 120,320,173352,2,0,L|128:272,1,47.5,2|0,0:0|0:0,0:0:0:0: 980 | 256,72,173500,6,0,L|248:120,1,47.5,8|0,0:0|0:0,0:0:0:0: 981 | 312,320,173648,2,0,L|320:272,1,47.5,8|0,0:0|0:0,0:0:0:0: 982 | 400,96,173796,6,0,L|464:80,3,47.5,2|0|2|0,0:0|0:0|0:0|0:0,0:0:0:0: 983 | 160,88,174091,5,8,0:0:0:0: 984 | 119,119,174164,1,8,0:0:0:0: 985 | 103,168,174238,1,8,0:0:0:0: 986 | 117,218,174312,1,8,0:0:0:0: 987 | 156,251,174387,2,0,L|264:256,1,95,8|8,0:0|0:0,0:0:0:0: 988 | 184,168,174682,5,2,0:0:0:0: 989 | 424,272,174830,1,2,0:0:0:0: 990 | 296,120,174978,6,0,L|304:240,1,95,8|0,0:0|0:0,0:0:0:0: 991 | 432,120,175274,5,8,0:0:0:0: 992 | 80,296,175421,1,8,0:0:0:0: 993 | 176,40,175717,5,8,0:0:0:0: 994 | 248,328,175865,1,8,0:0:0:0: 995 | 328,32,176012,2,0,L|317:78,1,47.5,8|0,0:0|0:0,0:0:0:0: 996 | 194,263,176456,5,2,0:0:0:0: 997 | 253,95,176604,1,2,0:0:0:0: 998 | 303,265,176752,1,8,0:0:0:0: 999 | 163,157,176899,1,8,0:0:0:0: 1000 | 340,164,177047,1,8,0:0:0:0: 1001 | 194,263,177195,5,2,0:0:0:0: 1002 | 388,288,177342,2,0,P|460:172|388:48,1,285,8|0,0:0|0:0,0:0:0:0: 1003 | 256,184,177934,5,6,0:0:0:0: 1004 | 184,72,183402,6,0,P|256:49|332:83,1,142.5,2|2,0:0|0:0,0:0:0:0: 1005 | 360,160,184584,5,2,0:0:0:0: 1006 | 301,330,185027,2,0,B|255:340|255:340|204:328,1,95,2|2,0:0|0:0,0:0:0:0: 1007 | 448,256,185766,5,2,0:0:0:0: 1008 | 331,127,186357,6,0,B|256:108|256:108|172:132,1,156.750004783631,12|0,0:0|0:0,0:0:0:0: 1009 | 184,232,186653,2,0,P|243:185|249:90,1,156.750004783631,12|0,0:0|0:0,0:0:0:0: 1010 | 160,52,186948,2,0,L|86:74,2,66.4999979705811,12|0|0,0:0|0:0|0:0,0:0:0:0: 1011 | 464,120,187392,6,0,B|476:192|476:192|448:268,1,132.999995941162,12|2,0:0|0:0,0:0:0:0: 1012 | 340,264,187835,1,2,0:0:0:0: 1013 | 252,164,187983,2,0,P|316:151|390:191,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 1014 | 264,328,188426,2,0,L|176:316,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 1015 | 120,128,189165,6,0,B|130:201|130:201|98:269,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 1016 | 12,181,189609,1,2,0:0:0:0: 1017 | 184,64,189756,2,0,B|256:72,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 1018 | 432,328,190347,6,0,P|369:346|308:321,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 1019 | 379,193,190791,2,0,L|373:275,1,66.4999979705811,2|0,0:0|0:0,0:0:0:0: 1020 | 104,352,191530,6,0,B|123:284|123:284|108:204,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 1021 | 16,284,191973,1,0,0:0:0:0: 1022 | 204,384,192121,2,0,P|244:381|294:363,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 1023 | 176,180,192416,2,0,P|141:187|107:202,1,66.4999979705811 1024 | 212,276,192712,5,8,0:0:0:0: 1025 | 472,96,193008,2,0,L|464:168,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 1026 | 356,56,193303,5,0,0:0:0:0: 1027 | 384,256,193451,1,8,0:0:0:0: 1028 | 176,180,193599,6,0,L|264:168,1,66.4999979705811,2|2,0:0|0:0,0:0:0:0: 1029 | 148,48,193894,6,0,P|82:36|16:64,1,132.999995941162,8|2,0:0|0:0,0:0:0:0: 1030 | 120,288,194485,2,0,L|256:256,1,113.999996520996,14|0,0:0|0:0,0:0:0:0: 1031 | 56,180,194781,5,0,0:0:0:0: 1032 | 308,272,194929,2,0,L|420:240,1,113.999996520996,14|0,0:0|0:0,0:0:0:0: 1033 | 48,64,195224,5,0,0:0:0:0: 1034 | 132,152,195372,2,0,L|244:120,1,113.999996520996,14|0,0:0|0:0,0:0:0:0: 1035 | 472,48,195668,1,2,0:0:0:0: 1036 | 424,248,195815,1,8,0:0:0:0: 1037 | 424,248,195889,1,8,0:0:0:0: 1038 | 424,248,195963,2,0,P|434:183|418:135,1,113.999996520996,8|8,0:0|0:0,0:0:0:0: 1039 | 136,96,196259,2,0,P|128:136|128:168,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1040 | 128,208,196407,5,8,0:0:0:0: 1041 | 135,264,196480,1,8,0:0:0:0: 1042 | 166,311,196554,1,8,0:0:0:0: 1043 | 214,340,196628,1,8,0:0:0:0: 1044 | 271,346,196702,1,8,0:0:0:0: 1045 | 324,327,196776,1,8,0:0:0:0: 1046 | 368,288,196850,6,0,P|378:241|368:184,1,95,2|0,0:0|0:0,0:0:0:0: 1047 | 312,16,197145,1,8,0:0:0:0: 1048 | 431,84,197293,1,2,0:0:0:0: 1049 | 431,84,197367,1,2,0:0:0:0: 1050 | 431,84,197441,2,0,B|313:104|313:104|241:92,1,190,2|8,0:0|0:0,0:0:0:0: 1051 | 312,16,197884,5,2,0:0:0:0: 1052 | 410,161,198032,2,0,B|380:260|380:260|400:344,1,190,2|8,0:0|0:0,0:0:0:0: 1053 | 488,268,198475,1,2,0:0:0:0: 1054 | 240,156,198623,2,0,P|191:154|146:171,1,95,2|0,0:0|0:0,0:0:0:0: 1055 | 305,355,198919,6,0,L|400:343,1,95,8|0,0:0|0:0,0:0:0:0: 1056 | 240,48,199214,2,0,B|145:35|145:35|44:72,1,190,2|8,0:0|0:0,0:0:0:0: 1057 | 116,240,199658,1,2,0:0:0:0: 1058 | 152,248,199732,1,2,0:0:0:0: 1059 | 188,236,199806,1,2,0:0:0:0: 1060 | 260,180,199953,6,0,P|241:162|194:149,1,47.5,2|0,0:0|0:0,0:0:0:0: 1061 | 100,324,200101,2,0,P|157:338|212:316,1,95,8|2,0:0|0:0,0:0:0:0: 1062 | 463,295,200397,2,0,B|368:270|368:270|264:304,1,190,6|12,0:0|0:0,0:0:0:0: 1063 | 394,380,200840,1,2,0:0:0:0: 1064 | 426,372,200914,1,2,0:0:0:0: 1065 | 461,351,200988,2,0,P|465:322|461:287,1,56.9999982604981,6|0,0:0|0:0,0:0:0:0: 1066 | 472,184,201136,2,0,P|456:156|424:132,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1067 | 303,152,201283,6,0,B|352:169|352:169|457:133,1,142.5,12|0,0:0|0:0,0:0:0:0: 1068 | 356,64,201579,2,0,B|307:47|307:47|202:83,1,142.5,2|0,0:0|0:0,0:0:0:0: 1069 | 21,176,201875,6,0,P|17:204|29:232,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1070 | 64,304,202022,2,0,L|184:288,1,95,2|0,0:0|0:0,0:0:0:0: 1071 | 268,384,202318,1,2,0:0:0:0: 1072 | 432,84,202466,2,0,B|288:80,1,123.499994346619,8|0,0:0|0:0,0:0:0:0: 1073 | 368,368,202761,6,0,P|420:352|448:300,1,106.875004076958,2|0,0:0|0:0,0:0:0:0: 1074 | 153,140,203057,2,0,P|106:144|60:156,1,95,8|2,0:0|0:0,0:0:0:0: 1075 | 334,32,203352,2,0,P|381:34|428:44,1,95,2|2,0:0|0:0,0:0:0:0: 1076 | 369,254,203648,2,0,P|321:254|274:262,1,95,8|2,0:0|0:0,0:0:0:0: 1077 | 116,276,203943,6,0,P|100:320|88:380,1,95,2|0,0:0|0:0,0:0:0:0: 1078 | 60,156,204239,2,0,P|80:208|92:268,1,95,8|0,0:0|0:0,0:0:0:0: 1079 | 376,236,204535,5,2,0:0:0:0: 1080 | 224,16,204682,1,2,0:0:0:0: 1081 | 168,206,204830,6,0,P|199:201|224:202,1,47.5,8|0,0:0|0:0,0:0:0:0: 1082 | 394,75,204978,2,0,B|442:87|478:59|478:59|458:107|502:139|458:195,1,227.999993041992,12|2,0:0|0:0,0:0:0:0: 1083 | 116,376,205421,2,0,B|119:343|119:343|109:312|109:312|111:282,1,95,10|0,0:0|0:0,0:0:0:0: 1084 | 88,246,205717,5,10,0:0:0:0: 1085 | 71,201,205790,1,8,0:0:0:0: 1086 | 68,154,205864,1,8,0:0:0:0: 1087 | 79,108,205938,1,8,0:0:0:0: 1088 | 104,68,206012,1,10,0:0:0:0: 1089 | 138,35,206086,1,8,0:0:0:0: 1090 | 180,18,206160,2,0,P|216:21|246:37,1,66.4999979705811,8|8,0:0|0:0,0:0:0:0: 1091 | 195,94,206308,6,0,B|266:67|266:67|384:96,1,190,10|12,0:0|0:0,0:0:0:0: 1092 | 428,192,206751,1,2,0:0:0:0: 1093 | 272,360,206899,2,0,B|239:288|239:288|264:175,1,190,2|12,0:0|0:0,0:0:0:0: 1094 | 340,272,207342,1,2,0:0:0:0: 1095 | 172,364,207490,2,0,B|123:348|81:379|81:379|40:331|-29:335,1,190,2|12,0:0|0:0,0:0:0:0: 1096 | 95,277,207934,1,2,0:0:0:0: 1097 | 340,272,208081,6,0,B|345:223|345:223|326:173,1,95,2|2,0:0|0:0,0:0:0:0: 1098 | 168,32,208377,2,0,B|118:27|118:27|72:41,1,95,12|2,0:0|0:0,0:0:0:0: 1099 | 52,64,208599,1,2,0:0:0:0: 1100 | 32,92,208673,2,0,L|48:200,1,95,2|2,0:0|0:0,0:0:0:0: 1101 | 155,329,208968,2,0,P|176:321|204:298,1,47.5,12|0,0:0|0:0,0:0:0:0: 1102 | 328,192,209116,6,0,B|192:176|192:176|216:184|232:208,1,170.999994781494,2|2,0:0|0:0,0:0:0:0: 1103 | 440,332,209559,2,0,P|464:284|448:232,1,85.4999973907472,12|2,0:0|0:0,0:0:0:0: 1104 | 296,92,209855,1,8,0:0:0:0: 1105 | 360,352,210003,1,8,0:0:0:0: 1106 | 440,92,210150,1,8,0:0:0:0: 1107 | 256,328,210298,1,8,0:0:0:0: 1108 | 375,184,210446,6,0,P|380:207|374:230,1,47.5,10|0,0:0|0:0,0:0:0:0: 1109 | 136,56,210742,6,0,L|208:48,1,47.5,10|0,0:0|0:0,0:0:0:0: 1110 | 176,316,211037,6,0,L|88:304,1,85.4999973907472,8|0,0:0|0:0,0:0:0:0: 1111 | 28,268,211333,2,0,L|120:256,1,85.4999973907472 1112 | 220,208,211628,6,0,L|120:193,1,95 1113 | 4,164,211924,2,0,L|104:152,1,95 1114 | 288,76,212219,2,0,L|183:57,1,104.500003189087 1115 | 0,32,212515,2,0,L|128:16,1,104.500003189087 1116 | 412,20,212810,6,0,P|431:44|440:74,2,61.7499971733095,8|0|0,0:0|0:0|0:0,0:0:0:0: 1117 | 83,162,213106,6,0,L|144:172,1,61.7499971733095,2|0,0:0|0:0,0:0:0:0: 1118 | 288,76,213254,2,0,L|349:86,1,61.7499971733095,2|0,0:0|0:0,0:0:0:0: 1119 | 200,384,213402,6,0,P|223:364|237:336,1,61.7499971733095,8|0,0:0|0:0,0:0:0:0: 1120 | 319,258,213549,2,0,L|288:76,1,185.249991519928,8|0,0:0|0:0,0:0:0:0: 1121 | 66,316,213845,6,0,P|4:238|17:141,1,185.249991519928,8|0,0:0|0:0,0:0:0:0: 1122 | 42,126,214141,1,8,0:0:0:0: 1123 | 136,352,214288,1,8,0:0:0:0: 1124 | 249,9,214436,2,0,P|277:0|311:3,5,47.5,8|2|2|2|2|2,0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: 1125 | 336,20,214879,5,6,0:0:0:0: 1126 | 371,43,214952,1,2,0:0:0:0: 1127 | 393,79,215026,1,8,0:0:0:0: 1128 | 397,121,215100,1,8,0:0:0:0: 1129 | 383,161,215174,1,10,0:0:0:0: 1130 | 356,194,215249,1,8,0:0:0:0: 1131 | 342,234,215323,5,8,0:0:0:0: 1132 | 349,278,215397,1,8,0:0:0:0: 1133 | 371,316,215471,1,10,0:0:0:0: 1134 | 410,341,215544,1,8,0:0:0:0: 1135 | 457,342,215618,1,8,0:0:0:0: 1136 | 497,311,215692,5,8,0:0:0:0: 1137 | 429,252,215766,2,0,B|341:234|341:234|245:264,1,170.999994781494,12|0,0:0|0:0,0:0:0:0: 1138 | 168,352,216062,2,0,P|116:352|67:334,1,104.500003189087,12|0,0:0|0:0,0:0:0:0: 1139 | 185,151,216357,2,0,P|215:142|257:151,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1140 | 492,221,216505,1,2,0:0:0:0: 1141 | 324,306,216653,2,0,P|305:287|295:263,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1142 | 400,176,216801,5,2,0:0:0:0: 1143 | 398,214,216875,1,0,0:0:0:0: 1144 | 376,244,216948,2,0,L|256:216,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 1145 | 4,140,217244,2,0,L|129:117,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1146 | 280,0,217540,1,2,0:0:0:0: 1147 | 220,292,217687,1,2,0:0:0:0: 1148 | 232,316,217761,5,2,0:0:0:0: 1149 | 244,340,217835,2,0,B|300:344|300:344|364:312,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1150 | 215,48,218131,2,0,B|116:31|116:31|7:67,1,209.000006378174,2|8,0:0|0:0,0:0:0:0: 1151 | 100,256,218574,5,2,0:0:0:0: 1152 | 308,92,218722,2,0,P|311:40|281:0,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1153 | 252,0,218943,1,2,0:0:0:0: 1154 | 228,12,219017,1,8,0:0:0:0: 1155 | 324,236,219165,2,0,B|384:220|428:260|428:260|464:200|528:204,1,209.000006378174,6|2,0:0|0:0,0:0:0:0: 1156 | 184,92,219609,6,0,P|130:81|56:80,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1157 | 440,24,219904,2,0,P|396:13|336:8,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1158 | 304,24,220126,5,2,0:0:0:0: 1159 | 280,52,220200,2,0,P|279:83|284:108,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1160 | 500,208,220347,2,0,P|501:177|492:144,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1161 | 410,103,220495,1,6,0:0:0:0: 1162 | 302,234,220643,1,2,0:0:0:0: 1163 | 400,356,220791,2,0,P|454:357|497:327,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1164 | 384,180,221086,5,2,0:0:0:0: 1165 | 210,133,221234,1,2,0:0:0:0: 1166 | 109,268,221382,2,0,L|123:351,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1167 | 186,372,221530,2,0,L|325:359,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1168 | 321,147,221825,2,0,P|286:139|251:146,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1169 | 165,37,221973,2,0,P|192:29|216:28,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1170 | 467,91,222121,2,0,L|459:148,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1171 | 424,260,222269,6,0,B|412:356|412:356|403:333|380:312,1,123.499994346619,6|2,0:0|0:0,0:0:0:0: 1172 | 409,166,222490,1,2,0:0:0:0: 1173 | 436,162,222564,2,0,P|476:177|497:205,1,71.25,8|0,0:0|0:0,0:0:0:0: 1174 | 507,232,222860,5,8,0:0:0:0: 1175 | 160,328,223008,1,8,0:0:0:0: 1176 | 372,40,223303,1,8,0:0:0:0: 1177 | 392,384,223451,2,0,L|340:312,1,71.25,8|0,0:0|0:0,0:0:0:0: 1178 | 180,98,223746,1,8,0:0:0:0: 1179 | 504,216,223894,2,0,L|384:262,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1180 | 160,340,224190,1,8,0:0:0:0: 1181 | 368,32,224338,6,0,P|380:76|368:120,1,71.25,8|0,0:0|0:0,0:0:0:0: 1182 | 40,200,224633,1,8,0:0:0:0: 1183 | 80,176,224706,1,8,0:0:0:0: 1184 | 127,173,224780,1,8,0:0:0:0: 1185 | 172,189,224854,1,8,0:0:0:0: 1186 | 197,219,224929,1,8,0:0:0:0: 1187 | 240,238,225003,1,8,0:0:0:0: 1188 | 287,235,225077,1,8,0:0:0:0: 1189 | 328,212,225150,1,8,0:0:0:0: 1190 | 372,192,225224,6,0,P|362:250|396:303,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 1191 | 493,199,225520,1,8,0:0:0:0: 1192 | 427,52,225668,2,0,B|279:63|279:63|305:70|321:88,1,190,2|2,0:0|0:0,0:0:0:0: 1193 | 285,263,226111,2,0,P|296:309|328:344,1,95,8|2,0:0|0:0,0:0:0:0: 1194 | 460,238,226407,6,0,P|482:197|472:152,1,95,2|0,0:0|0:0,0:0:0:0: 1195 | 208,336,226702,2,0,B|116:324|116:324|132:332|131:338,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 1196 | 310,147,226998,6,0,L|302:19,1,95,2|0,0:0|0:0,0:0:0:0: 1197 | 56,144,227293,2,0,L|64:256,1,95,8|0,0:0|0:0,0:0:0:0: 1198 | 142,259,227589,5,2,0:0:0:0: 1199 | 88,16,227737,1,2,0:0:0:0: 1200 | 144,240,227884,2,0,L|249:226,1,95,8|0,0:0|0:0,0:0:0:0: 1201 | 279,374,228180,5,2,0:0:0:0: 1202 | 309,112,228328,1,2,0:0:0:0: 1203 | 264,368,228475,2,0,P|296:312|288:264,1,95,8|0,0:0|0:0,0:0:0:0: 1204 | 64,322,228771,6,0,L|176:348,1,113.999996520996,8|8,0:0|0:0,0:0:0:0: 1205 | 380,238,228993,1,0,0:0:0:0: 1206 | 388,248,229067,2,0,B|400:281|400:281|388:324,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1207 | 204,260,229214,2,0,B|192:228|192:228|200:188,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1208 | 321,105,229362,5,8,0:0:0:0: 1209 | 291,339,229511,1,8,0:0:0:0: 1210 | 104,256,229658,5,8,0:0:0:0: 1211 | 419,159,229806,2,0,P|476:232|453:321,1,190,12|2,0:0|0:0,0:0:0:0: 1212 | 281,356,230249,2,0,L|188:330,1,95,2|0,0:0|0:0,0:0:0:0: 1213 | 41,111,230544,2,0,B|29:99|9:95|9:95|161:79,1,190,8|2,0:0|0:0,0:0:0:0: 1214 | 220,0,230988,5,2,0:0:0:0: 1215 | 252,218,231136,2,0,P|275:306|220:379,1,190,2|0,0:0|0:0,0:0:0:0: 1216 | 312,380,231727,2,0,P|366:365|395:335,1,95,8|0,0:0|0:0,0:0:0:0: 1217 | 309,197,232022,2,0,L|214:205,1,95,2|2,0:0|0:0,0:0:0:0: 1218 | 88,312,232318,5,8,0:0:0:0: 1219 | 440,288,232466,1,2,0:0:0:0: 1220 | 112,224,232613,5,8,0:0:0:0: 1221 | 424,200,232761,1,2,0:0:0:0: 1222 | 144,128,232909,5,8,0:0:0:0: 1223 | 392,104,233057,1,2,0:0:0:0: 1224 | 120,320,233204,6,0,L|113:257,1,47.5,8|2,0:0|0:0,0:0:0:0: 1225 | 392,336,233352,2,0,L|402:257,1,47.5,2|2,0:0|0:0,0:0:0:0: 1226 | 96,232,233500,6,0,L|90:174,1,47.5,8|2,0:0|0:0,0:0:0:0: 1227 | 416,248,233648,2,0,L|422:176,1,47.5,2|2,0:0|0:0,0:0:0:0: 1228 | 64,144,233796,6,0,L|56:83,1,47.5,8|2,0:0|0:0,0:0:0:0: 1229 | 448,160,233944,2,0,L|453:97,1,47.5,2|2,0:0|0:0,0:0:0:0: 1230 | 104,24,234092,6,0,P|107:53|118:80,1,47.5,12|2,0:0|0:0,0:0:0:0: 1231 | 184,328,234239,1,8,0:0:0:0: 1232 | 141,272,234313,1,8,0:0:0:0: 1233 | 138,201,234387,1,8,0:0:0:0: 1234 | 176,142,234461,1,8,0:0:0:0: 1235 | 241,115,234535,1,8,0:0:0:0: 1236 | 309,129,234609,1,8,0:0:0:0: 1237 | 358,180,234682,2,0,P|352:232|339:280,1,95,14|0,0:0|0:0,0:0:0:0: 1238 | 128,280,234978,5,8,0:0:0:0: 1239 | 356,172,235126,1,2,0:0:0:0: 1240 | 357,139,235200,1,2,0:0:0:0: 1241 | 339,109,235274,1,2,0:0:0:0: 1242 | 128,24,235421,1,2,0:0:0:0: 1243 | 192,160,235569,1,8,0:0:0:0: 1244 | 412,44,235717,2,0,P|433:76|437:105,1,47.5,2|0,0:0|0:0,0:0:0:0: 1245 | 466,176,235865,6,0,B|426:188|426:188|342:168,1,113.999996520996,2|2,0:0|0:0,0:0:0:0: 1246 | 216,104,236160,1,8,0:0:0:0: 1247 | 86,264,236308,6,0,P|135:262|170:231,1,95,2|2,0:0|0:0,0:0:0:0: 1248 | 72,51,236604,1,2,0:0:0:0: 1249 | 0,256,236751,1,8,0:0:0:0: 1250 | 180,313,236899,5,2,0:0:0:0: 1251 | 219,298,236973,1,2,0:0:0:0: 1252 | 239,262,237047,2,0,B|229:228|208:189|208:189|204:209|192:219,1,95,2|2,0:0|0:0,0:0:0:0: 1253 | 0,283,237342,1,8,0:0:0:0: 1254 | 148,72,237490,2,0,P|193:61|240:69,1,95,2|2,0:0|0:0,0:0:0:0: 1255 | 166,369,237786,2,0,P|131:336|117:291,1,95,2|8,0:0|0:0,0:0:0:0: 1256 | 349,116,238081,2,0,P|347:149|339:177,1,47.5,2|0,0:0|0:0,0:0:0:0: 1257 | 321,200,238229,1,2,0:0:0:0: 1258 | 301,236,238302,1,2,0:0:0:0: 1259 | 297,280,238376,1,2,0:0:0:0: 1260 | 313,320,238450,1,2,0:0:0:0: 1261 | 345,352,238525,6,0,L|461:360,1,95,8|0,0:0|0:0,0:0:0:0: 1262 | 349,116,238820,1,2,0:0:0:0: 1263 | 305,125,238894,1,2,0:0:0:0: 1264 | 263,137,238968,1,2,0:0:0:0: 1265 | 225,145,239042,1,2,0:0:0:0: 1266 | 186,153,239115,1,8,0:0:0:0: 1267 | 186,239,239264,2,0,P|209:218|223:192,1,47.5,2|0,0:0|0:0,0:0:0:0: 1268 | 112,136,239411,6,0,L|116:248,1,95,2|2,0:0|0:0,0:0:0:0: 1269 | 120,360,239707,1,8,0:0:0:0: 1270 | 396,354,239855,1,2,0:0:0:0: 1271 | 376,136,240003,2,0,L|380:200,1,47.5,2|0,0:0|0:0,0:0:0:0: 1272 | 389,327,240150,1,2,0:0:0:0: 1273 | 104,124,240298,2,0,L|94:228,1,95,8|2,0:0|0:0,0:0:0:0: 1274 | 244,92,240594,5,2,0:0:0:0: 1275 | 208,328,240742,1,2,0:0:0:0: 1276 | 376,136,240889,1,8,0:0:0:0: 1277 | 388,380,241037,1,2,0:0:0:0: 1278 | 300,208,241185,1,2,0:0:0:0: 1279 | 464,352,241333,1,2,0:0:0:0: 1280 | 376,136,241480,1,8,0:0:0:0: 1281 | 292,372,241628,1,2,0:0:0:0: 1282 | 136,270,241776,6,0,P|114:312|119:359,1,95,2|2,0:0|0:0,0:0:0:0: 1283 | 320,294,242072,6,0,P|346:283|370:258,1,47.5,8|0,0:0|0:0,0:0:0:0: 1284 | 392,229,242219,2,0,L|375:131,1,95,2|2,0:0|0:0,0:0:0:0: 1285 | 150,60,242515,1,2,0:0:0:0: 1286 | 166,100,242589,1,2,0:0:0:0: 1287 | 202,124,242663,1,8,0:0:0:0: 1288 | 380,44,242810,2,0,P|343:48|312:67,1,47.5,2|0,0:0|0:0,0:0:0:0: 1289 | 68,344,242958,5,2,0:0:0:0: 1290 | 108,356,243031,1,2,0:0:0:0: 1291 | 152,356,243105,1,2,0:0:0:0: 1292 | 192,340,243179,1,2,0:0:0:0: 1293 | 224,312,243254,6,0,P|222:283|216:259,1,47.5,8|0,0:0|0:0,0:0:0:0: 1294 | 146,66,243402,2,0,P|118:54|80:49,1,47.5,2|0,0:0|0:0,0:0:0:0: 1295 | 444,80,243549,5,2,0:0:0:0: 1296 | 458,99,243623,1,2,0:0:0:0: 1297 | 467,117,243697,1,2,0:0:0:0: 1298 | 476,146,243770,5,2,0:0:0:0: 1299 | 485,196,243844,1,8,0:0:0:0: 1300 | 477,247,243918,1,10,0:0:0:0: 1301 | 449,297,243992,1,10,0:0:0:0: 1302 | 406,342,244066,1,10,0:0:0:0: 1303 | 332,365,244141,2,0,B|249:365|205:332|205:332|223:325|236:312,1,161.500003080368,2|0,0:0|0:0,0:0:0:0: 1304 | 236,117,244436,6,0,P|261:112|305:116,1,47.5,12|0,0:0|0:0,0:0:0:0: 1305 | 455,131,244584,1,2,0:0:0:0: 1306 | 344,9,244732,1,2,0:0:0:0: 1307 | 365,354,244879,1,2,0:0:0:0: 1308 | 361,223,245027,2,0,L|356:115,1,95,8|2,0:0|0:0,0:0:0:0: 1309 | 196,181,245323,5,2,0:0:0:0: 1310 | 468,196,245471,1,2,0:0:0:0: 1311 | 229,337,245618,2,0,P|190:310|174:265,1,95,8|2,0:0|0:0,0:0:0:0: 1312 | 219,36,245914,1,2,0:0:0:0: 1313 | 56,136,246062,1,2,0:0:0:0: 1314 | 267,188,246209,2,0,L|323:180,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1315 | 128,320,246357,2,0,L|71:327,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1316 | 56,136,246505,6,0,P|112:120|162:143,1,95,2|2,0:0|0:0,0:0:0:0: 1317 | 254,96,246801,1,8,0:0:0:0: 1318 | 384,336,246948,1,2,0:0:0:0: 1319 | 424,324,247022,1,2,0:0:0:0: 1320 | 452,292,247096,2,0,P|452:250|428:188,1,95,2|2,0:0|0:0,0:0:0:0: 1321 | 456,64,247392,2,0,P|408:48|362:45,1,95,8|2,0:0|0:0,0:0:0:0: 1322 | 144,40,247687,6,0,P|96:47|35:62,1,95,2|2,0:0|0:0,0:0:0:0: 1323 | 20,84,247909,1,2,0:0:0:0: 1324 | 4,116,247983,2,0,P|6:148|11:179,1,47.5,8|0,0:0|0:0,0:0:0:0: 1325 | 120,272,248131,2,0,P|117:301|112:340,1,47.5,2|0,0:0|0:0,0:0:0:0: 1326 | 104,180,248278,5,2,0:0:0:0: 1327 | 144,169,248351,1,2,0:0:0:0: 1328 | 187,171,248425,1,2,0:0:0:0: 1329 | 227,185,248499,1,2,0:0:0:0: 1330 | 260,212,248573,1,8,0:0:0:0: 1331 | 364,360,248722,2,0,P|386:355|407:344,1,47.5,2|0,0:0|0:0,0:0:0:0: 1332 | 480,248,248870,6,0,B|408:244|408:244|341:267,1,142.5,2|0,0:0|0:0,0:0:0:0: 1333 | 232,324,249165,2,0,B|182:322|182:322|128:303,1,95,8|2,0:0|0:0,0:0:0:0: 1334 | 120,272,249387,1,2,0:0:0:0: 1335 | 108,236,249461,2,0,L|32:244,2,63.3333333333333,2|2|2,0:0|0:0|0:0,0:0:0:0: 1336 | 216,148,249756,2,0,L|278:156,2,63.3333333333333,8|2|2,0:0|0:0|0:0,0:0:0:0: 1337 | 72,56,250052,6,0,L|-8:48,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1338 | 216,12,250200,2,0,L|304:24,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1339 | 52,104,250347,6,0,L|108:107,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1340 | 224,64,250495,2,2,L|280:73,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1341 | 380,136,250643,6,0,L|372:200,3,47.5,2|2|2|2,0:0|0:0|0:0|0:0,0:0:0:0: 1342 | 508,64,250939,6,0,L|500:124,1,47.5,8|2,0:0|0:0,0:0:0:0: 1343 | 288,296,251086,1,2,0:0:0:0: 1344 | 252,280,251160,1,2,0:0:0:0: 1345 | 228,248,251234,2,0,L|244:132,1,95,8|2,0:0|0:0,0:0:0:0: 1346 | 100,188,251530,6,0,L|160:180,1,47.5,8|0,0:0|0:0,0:0:0:0: 1347 | 400,288,251677,1,2,0:0:0:0: 1348 | 400,328,251751,1,2,0:0:0:0: 1349 | 380,360,251825,2,0,L|324:368,1,47.5,8|0,0:0|0:0,0:0:0:0: 1350 | 142,378,251973,2,0,L|146:330,1,47.5,8|0,0:0|0:0,0:0:0:0: 1351 | 150,257,252121,5,8,0:0:0:0: 1352 | 464,36,252416,5,8,0:0:0:0: 1353 | 480,76,252489,1,2,0:0:0:0: 1354 | 480,120,252563,1,2,0:0:0:0: 1355 | 472,160,252637,1,2,0:0:0:0: 1356 | 440,192,252712,6,0,P|393:186|336:180,1,95,8|2,0:0|0:0,0:0:0:0: 1357 | 296,112,252934,1,2,0:0:0:0: 1358 | 272,124,253008,2,0,P|252:176|272:232,1,95,8|2,0:0|0:0,0:0:0:0: 1359 | 368,72,253229,5,2,0:0:0:0: 1360 | 356,52,253303,2,0,P|324:36|274:33,1,78.3750023918153,8|0,0:0|0:0,0:0:0:0: 1361 | 184,272,253599,2,0,P|108:288|44:256,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 1362 | 328,328,253894,2,0,P|400:344|452:320,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 1363 | 136,200,254190,5,8,0:0:0:0: 1364 | 373,242,254338,1,0,0:0:0:0: 1365 | 114,176,254485,1,8,0:0:0:0: 1366 | 394,266,254633,1,0,0:0:0:0: 1367 | 362,80,254781,5,8,0:0:0:0: 1368 | 118,303,254928,1,0,0:0:0:0: 1369 | 160,76,255076,5,8,0:0:0:0: 1370 | 360,316,255224,1,0,0:0:0:0: 1371 | 372,72,255372,5,8,0:0:0:0: 1372 | 108,312,255520,1,0,0:0:0:0: 1373 | 148,68,255668,5,8,0:0:0:0: 1374 | 368,324,255816,1,0,0:0:0:0: 1375 | 256,66,255963,6,0,P|251:129|215:176,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 1376 | 52,96,256259,5,8,0:0:0:0: 1377 | 428,188,256407,1,0,0:0:0:0: 1378 | 52,288,256554,5,8,0:0:0:0: 1379 | 436,188,256702,1,0,0:0:0:0: 1380 | 44,96,256850,5,8,0:0:0:0: 1381 | 444,188,256998,1,0,0:0:0:0: 1382 | 44,288,257145,5,8,0:0:0:0: 1383 | 452,188,257293,1,0,0:0:0:0: 1384 | 36,92,257441,5,8,0:0:0:0: 1385 | 460,184,257589,1,0,0:0:0:0: 1386 | 169,342,257737,5,8,0:0:0:0: 1387 | 304,304,257884,2,0,P|328:256|320:200,1,95,2|0,0:0|0:0,0:0:0:0: 1388 | 184,144,258032,2,0,B|176:184|192:216,1,61.7499971733095,8|0,0:0|0:0,0:0:0:0: 1389 | 472,184,258180,2,0,B|477:158|464:112,1,61.7499971733095,2|0,0:0|0:0,0:0:0:0: 1390 | 344,40,258328,2,0,P|362:121|328:192,1,152,12|0,0:0|0:0,0:0:0:0: 1391 | 32,344,258623,2,0,P|88:264|160:248,1,152,8|0,0:0|0:0,0:0:0:0: 1392 | 304,304,258919,1,8,0:0:0:0: 1393 | 128,48,259067,1,0,0:0:0:0: 1394 | 136,336,259214,1,8,0:0:0:0: 1395 | 264,112,259362,5,0,0:0:0:0: 1396 | 328,328,259510,1,8,0:0:0:0: 1397 | 88,176,259658,1,0,0:0:0:0: 1398 | 444,124,259806,2,0,P|416:136|360:128,1,66.4999979705811,8|0,0:0|0:0,0:0:0:0: 1399 | 216,24,259953,2,0,P|256:16|289:21,1,66.4999979705811 1400 | 296,216,260101,1,8,0:0:0:0: 1401 | 8,120,260249,2,0,P|72:88|144:104,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: 1402 | 488,142,260544,6,0,B|431:163|370:132|370:132|345:144|345:144|284:114|215:149,1,265.999991882324,12|2,0:0|0:0,0:0:0:0: 1403 | 75,193,260988,1,2,0:0:0:0: 1404 | 297,361,261136,2,0,B|282:297|320:239|320:239|303:190|303:190|344:114|290:32,1,323.000006160736,12|2,0:0|0:0,0:0:0:0: 1405 | 58,198,261579,5,2,0:0:0:0: 1406 | 418,79,261727,2,0,P|490:127|415:95,1,246.999988693238,12|2,0:0|0:0,0:0:0:0: 1407 | 54,247,262170,5,2,0:0:0:0: 1408 | 64,220,264091,6,0,L|54:271,1,47.5,12|0,0:0|0:0,0:0:0:0: 1409 | 432,300,265126,2,0,P|410:248|356:213,1,95,10|0,0:0|0:0,0:0:0:0: 1410 | 194,217,265421,2,0,P|245:224|293:206,1,95,6|0,0:0|0:0,0:0:0:0: 1411 | 367,46,265717,2,0,P|341:91|342:142,1,95,2|0,0:0|0:0,0:0:0:0: 1412 | 469,160,266012,6,0,P|442:89|511:118,1,190,12|0,0:0|0:0,0:0:0:0: 1413 | 340,319,266456,2,0,P|292:321|248:305,1,95,6|0,0:0|0:0,0:0:0:0: 1414 | 336,190,266751,1,8,0:0:0:0: 1415 | 448,280,266899,2,0,P|460:328|441:374,1,95,2|2,0:0|0:0,0:0:0:0: 1416 | 194,217,267195,6,0,L|144:212,3,47.5,8|0|2|0,0:0|0:0|0:0|0:0,0:0:0:0: 1417 | 392,120,267490,2,0,L|448:124,3,47.5,8|0|2|0,0:0|0:0|0:0|0:0,0:0:0:0: 1418 | 89,344,267786,5,14,0:0:0:0: 1419 | 43,301,267859,1,10,0:0:0:0: 1420 | 17,250,267933,1,8,0:0:0:0: 1421 | 10,192,268007,1,8,0:0:0:0: 1422 | 24,135,268081,1,8,0:0:0:0: 1423 | 56,86,268155,1,8,0:0:0:0: 1424 | 104,53,268229,1,8,0:0:0:0: 1425 | 160,38,268303,1,8,0:0:0:0: 1426 | 218,42,268377,1,12,0:0:0:0: 1427 | 274,66,268451,5,8,0:0:0:0: 1428 | 507,239,268525,1,8,0:0:0:0: 1429 | 479,295,268598,1,8,0:0:0:0: 1430 | 433,338,268672,1,8,0:0:0:0: 1431 | 376,360,268746,1,8,0:0:0:0: 1432 | 312,360,268820,1,8,0:0:0:0: 1433 | 254,336,268894,1,8,0:0:0:0: 1434 | 211,293,268968,6,0,B|191:229|191:229|217:155,1,113.999996520996,6|0,0:0|0:0,0:0:0:0: 1435 | 472,88,269264,2,0,B|377:76,1,95,8|0,0:0|0:0,0:0:0:0: 1436 | 336,232,269559,1,2,0:0:0:0: 1437 | 256,112,269707,1,2,0:0:0:0: 1438 | 256,112,269781,1,0,0:0:0:0: 1439 | 256,112,269855,6,0,P|216:105|193:114,1,56.9999982604981,8|0,0:0|0:0,0:0:0:0: 1440 | 112,192,270003,2,0,P|79:191|44:179,1,56.9999982604981,2|0,0:0|0:0,0:0:0:0: 1441 | 352,48,270150,6,0,P|368:112|352:160,1,95,2|0,0:0|0:0,0:0:0:0: 1442 | 105,368,270446,2,0,L|200:356,1,95,8|0,0:0|0:0,0:0:0:0: 1443 | 504,256,270742,2,0,B|400:240,1,95,2|2,0:0|0:0,0:0:0:0: 1444 | 200,40,271037,2,0,L|220:132,1,95,8|2,0:0|0:0,0:0:0:0: 1445 | 336,264,271333,6,0,L|324:312,1,47.5,6|0,0:0|0:0,0:0:0:0: 1446 | 504,168,271480,2,0,L|400:152,1,95,2|8,0:0|0:0,0:0:0:0: 1447 | 200,40,271776,1,2,0:0:0:0: 1448 | 162,231,271924,2,0,P|195:287|184:352,1,132.999995941162,2|2,0:0|0:0,0:0:0:0: 1449 | 40,304,272219,6,0,P|87:298|103:290,1,47.5,8|0,0:0|0:0,0:0:0:0: 1450 | 144,142,272367,2,0,P|191:144|232:168,1,95,2|2,0:0|0:0,0:0:0:0: 1451 | 336,264,272663,5,2,0:0:0:0: 1452 | 348,284,272737,1,2,0:0:0:0: 1453 | 360,304,272810,2,0,P|357:335|348:364,1,47.5,8|0,0:0|0:0,0:0:0:0: 1454 | 512,144,272958,2,0,P|505:124|500:96,1,47.5,2|0,0:0|0:0,0:0:0:0: 1455 | 482,24,273106,5,2,0:0:0:0: 1456 | 304,48,273254,1,2,0:0:0:0: 1457 | 284,64,273328,1,2,0:0:0:0: 1458 | 264,80,273402,2,0,L|160:68,1,95,8|2,0:0|0:0,0:0:0:0: 1459 | 218,269,273697,6,0,P|233:313|224:360,1,95,6|0,0:0|0:0,0:0:0:0: 1460 | 128,200,273993,2,0,L|33:212,1,95,8|0,0:0|0:0,0:0:0:0: 1461 | 80,112,274288,1,2,0:0:0:0: 1462 | 40,336,274436,1,2,0:0:0:0: 1463 | 72,352,274510,1,2,0:0:0:0: 1464 | 108,356,274584,2,0,P|139:347|172:332,1,47.5,8|0,0:0|0:0,0:0:0:0: 1465 | 320,264,274732,6,0,B|336:224|336:224|328:152,1,95,2|2,0:0|0:0,0:0:0:0: 1466 | 184,224,275027,1,2,0:0:0:0: 1467 | 376,384,275175,2,0,P|424:352|434:313,1,95,8|2,0:0|0:0,0:0:0:0: 1468 | 340,92,275471,1,2,0:0:0:0: 1469 | 464,152,275618,1,2,0:0:0:0: 1470 | 208,96,275766,2,0,P|220:45|262:11,1,99.7499969558717,8|0,0:0|0:0,0:0:0:0: 1471 | 504,56,276062,6,0,L|393:38,1,95,6|2,0:0|0:0,0:0:0:0: 1472 | 320,264,276357,2,0,L|413:247,1,95,8|2,0:0|0:0,0:0:0:0: 1473 | 69,28,276653,2,0,L|32:132,1,95,8|8,0:0|0:0,0:0:0:0: 1474 | 32,156,276875,1,8,0:0:0:0: 1475 | 44,192,276948,2,0,P|72:184|100:181,1,47.5,8|0,0:0|0:0,0:0:0:0: 1476 | 400,272,277096,6,0,P|408:328|400:376,1,95,10|0,0:0|0:0,0:0:0:0: 1477 | 256,184,277392,5,10,0:0:0:0: 1478 | 112,271,277540,6,0,P|103:317|108:365,1,95,8|0,0:0|0:0,0:0:0:0: 1479 | 176,64,277835,5,10,0:0:0:0: 1480 | 328,64,277983,1,2,0:0:0:0: 1481 | 256,288,278131,5,8,0:0:0:0: 1482 | 184,72,278278,6,4,P|256:56|336:80,1,142.5,8|2,0:0|0:0,0:0:0:0: 1483 | 348,298,279040,2,0,B|284:292|284:292|256:284|256:284|228:292|228:292|159:295,1,190,8|2,0:0|0:0,0:0:0:0: 1484 | 353,299,279483,5,8,0:0:0:0: 1485 | 254,148,279631,1,10,0:0:0:0: 1486 | 159,295,279778,1,2,0:0:0:0: 1487 | 159,295,280074,5,2,0:0:0:0: 1488 | 353,299,280222,1,8,0:0:0:0: 1489 | 254,148,280369,1,2,0:0:0:0: 1490 | 256,324,280517,1,8,0:0:0:0: 1491 | 379,66,280665,1,8,0:0:0:0: 1492 | 133,70,280813,2,0,L|260:332,1,285.000010871888,12|0,0:0|0:0,0:0:0:0: 1493 | 257,326,281256,6,0,L|387:50,1,285.000010871888,12|2,0:2|0:0,0:0:0:0: 1494 | 388,40,281700,6,0,B|255:59|255:59|107:31,1,285.000010871888,12|0,0:2|0:0,0:0:0:0: 1495 | 229,234,282143,2,0,P|260:131|277:234,1,285.000010871888,12|10,0:2|0:0,0:0:0:0: 1496 | 104,312,282512,1,2,0:0:0:0: 1497 | 68,276,282586,2,0,P|40:217|68:130,1,142.500005435944,12|2,0:2|0:0,0:0:0:0: 1498 | 428,104,282808,1,2,0:0:0:0: 1499 | 464,140,282882,2,0,B|448:304,1,142.500005435944,14|0,0:2|0:0,0:0:0:0: 1500 | 184,74,283160,6,0,P|257:47|324:74,1,152,12|2,0:0|0:0,0:0:0:0: 1501 | 253,187,283603,1,2,0:0:0:0: 1502 | 56,152,283751,2,0,B|20:80|20:80|52:8,1,152,12|2,0:0|0:0,0:0:0:0: 1503 | 140,0,284194,1,2,0:0:0:0: 1504 | 448,148,284342,2,0,P|452:196|412:252,1,95,6|0,0:0|0:0,0:0:0:0: 1505 | 156,384,284649,2,0,P|108:380|40:352,1,95,2|2,0:0|0:0,0:0:0:0: 1506 | 4,228,284870,5,2,0:0:0:0: 1507 | 20,212,284944,2,0,B|80:196|80:196|136:212,1,114,12|0,0:0|0:0,0:0:0:0: 1508 | 344,360,285240,2,0,P|292:372|248:356,1,95,2|2,0:0|0:0,0:0:0:0: 1509 | 129,210,285535,5,12,0:0:0:0: 1510 | 428,176,285831,5,10,0:0:0:0: 1511 | 296,236,285979,2,0,L|304:284,2,47.5,8|0|0,0:0|0:0|0:0,0:0:0:0: 1512 | 328,124,286274,5,8,0:0:0:0: 1513 | 192,184,286422,2,0,L|208:300,1,95,8|2,0:0|0:0,0:0:0:0: 1514 | 416,52,286717,2,0,P|328:24|232:72,1,185.249991519928,8|0,0:0|0:0,0:0:0:0: 1515 | 72,183,287012,6,0,B|128:199|128:199|211:179,1,123.499994346619,8|2,0:0|0:0,0:0:0:0: 1516 | 164,128,287234,1,2,0:0:0:0: 1517 | 140,104,287308,2,0,B|129:207,1,95,8|2,0:0|0:0,0:0:0:0: 1518 | 208,336,287529,1,2,0:0:0:0: 1519 | 236,356,287603,2,0,B|343:335,1,95,8|0,0:0|0:0,0:0:0:0: 1520 | 452,184,287899,6,0,P|416:136|348:136,1,114,6|0,0:0|0:0,0:0:0:0: 1521 | 272,246,288194,2,0,B|319:263|319:263|384:250,1,114,2|0,0:0|0:0,0:0:0:0: 1522 | 176,220,288490,6,0,P|120:204|44:232,1,114,8|0,0:0|0:0,0:0:0:0: 1523 | 260,152,288785,2,0,P|284:114|293:62,1,76,2|8,0:0|0:0,0:0:0:0: 1524 | 184,0,289081,6,0,P|204:4|240:0,1,38,2|0,0:0|0:0,0:0:0:0: 1525 | 367,28,289229,6,0,B|319:-12|257:3|257:3|223:4,1,152,8|2,0:0|0:0,0:0:0:0: 1526 | 452,200,289672,2,0,B|468:144|468:144|453:90,1,114,8|0,0:0|0:0,0:0:0:0: 1527 | 373,202,289968,2,0,P|409:195|444:197,1,71.25,2|0,0:0|0:0,0:0:0:0: 1528 | 176,84,290244,6,0,P|180:112|176:140,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1529 | 72,208,290388,2,0,P|71:234|75:260,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1530 | 192,260,290533,6,0,P|228:256|264:260,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1531 | 68,104,290678,2,0,P|44:100|12:104,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1532 | 248,24,290823,1,8,0:0:0:0: 1533 | 256,32,290896,1,0,0:0:0:0: 1534 | 264,40,290968,1,8,0:0:0:0: 1535 | 272,48,291041,1,0,0:0:0:0: 1536 | 280,56,291113,6,0,P|316:52|356:56,1,47.5,8|0,0:0|0:0,0:0:0:0: 1537 | 512,85,291267,2,0,L|508:137,1,47.5,8|0,0:0|0:0,0:0:0:0: 1538 | 433,208,291423,2,0,L|425:276,1,47.5,12|0,0:0|0:0,0:0:0:0: 1539 | 342,304,291572,5,8,0:0:0:0: 1540 | 301,325,291646,1,8,0:0:0:0: 1541 | 254,324,291721,1,8,0:0:0:0: 1542 | 213,302,291795,1,8,0:0:0:0: 1543 | 187,262,291870,2,0,L|200:208,1,47.5,8|8,0:0|0:0,0:0:0:0: 1544 | 364,32,292020,5,12,0:0:0:0: 1545 | 402,58,292094,1,8,0:0:0:0: 1546 | 425,100,292169,1,8,0:0:0:0: 1547 | 426,147,292243,1,8,0:0:0:0: 1548 | 405,189,292319,1,8,0:0:0:0: 1549 | 366,215,292393,1,8,0:0:0:0: 1550 | 343,256,292468,1,8,0:0:0:0: 1551 | 342,304,292542,1,8,0:0:0:0: 1552 | 363,346,292617,6,0,B|400:328|428:340|428:340|496:304|448:232,1,185.249991519928,6|0,0:0|0:0,0:0:0:0: 1553 | 336,128,292912,2,0,P|268:112|200:136,1,128.250000489235,12|0,0:0|0:0,0:0:0:0: 1554 | 101,176,293208,5,2,0:0:0:0: 1555 | 286,263,293355,1,2,0:0:0:0: 1556 | 436,140,293503,2,0,P|431:93|387:53,1,95,8|2,0:0|0:0,0:0:0:0: 1557 | 336,16,293725,1,2,0:0:0:0: 1558 | 312,0,293799,2,0,L|196:14,1,95,2|2,0:0|0:0,0:0:0:0: 1559 | 127,51,294094,5,8,0:0:0:0: 1560 | 219,304,294242,2,0,B|283:328|283:328|411:292,1,190,2|2,0:0|0:0,0:0:0:0: 1561 | 451,220,294685,2,8,P|455:173|435:131,1,95,8|0,0:0|0:0,0:0:0:0: 1562 | 269,29,294981,2,0,L|174:35,1,95,2|0,0:0|0:0,0:0:0:0: 1563 | 427,51,295277,6,0,B|368:40|368:40|288:56,1,142.5,8|0,0:0|0:0,0:0:0:0: 1564 | 280,224,295572,1,2,0:0:0:0: 1565 | 357,133,295720,1,2,0:0:0:0: 1566 | 117,119,295868,2,0,P|97:161|106:207,1,95,8|0,0:0|0:0,0:0:0:0: 1567 | 294,381,296163,1,2,0:0:0:0: 1568 | 190,307,296311,1,2,0:0:0:0: 1569 | 158,323,296385,1,2,0:0:0:0: 1570 | 134,323,296459,2,0,P|102:315|62:291,3,47.5,8|0|2|0,0:0|0:0|0:0|0:0,0:0:0:0: 1571 | 408,240,296754,5,2,0:0:0:0: 1572 | 408,240,296828,1,2,0:0:0:0: 1573 | 408,240,296902,1,2,0:0:0:0: 1574 | 408,224,296976,1,2,0:0:0:0: 1575 | 416,176,297050,5,8,0:0:0:0: 1576 | 414,132,297123,1,8,0:0:0:0: 1577 | 400,93,297197,1,8,0:0:0:0: 1578 | 374,59,297272,1,8,0:0:0:0: 1579 | 339,33,297346,2,0,L|224:40,1,95,6|0,0:0|0:0,0:0:0:0: 1580 | 192,144,297641,2,0,L|80:136,1,95,8|0,0:0|0:0,0:0:0:0: 1581 | 120,232,297937,5,2,0:0:0:0: 1582 | 264,112,298084,1,2,0:0:0:0: 1583 | 53,62,298232,2,0,P|21:98|12:148,1,95,8|0,0:0|0:0,0:0:0:0: 1584 | 144,344,298528,6,0,L|264:320,1,95,2|2,0:0|0:0,0:0:0:0: 1585 | 424,352,298823,6,0,L|344:328,1,47.5,8|0,0:0|0:0,0:0:0:0: 1586 | 208,288,298971,1,2,0:0:0:0: 1587 | 208,288,299045,1,2,0:0:0:0: 1588 | 208,288,299119,2,0,L|328:264,1,95,6|2,0:0|0:0,0:0:0:0: 1589 | 416,176,299415,2,0,P|368:136|304:144,1,95,8|0,0:0|0:0,0:0:0:0: 1590 | 225,45,299710,6,0,L|83:57,1,142.5,2|0,0:0|0:0,0:0:0:0: 1591 | 48,237,300006,2,0,P|29:304|62:365,1,142.5,8|0,0:0|0:0,0:0:0:0: 1592 | 207,287,300301,6,0,L|302:280,1,95,2|0,0:0|0:0,0:0:0:0: 1593 | 416,384,300597,2,0,L|522:364,1,95,8|0,0:0|0:0,0:0:0:0: 1594 | 480,232,300892,1,8,0:0:0:0: 1595 | 490,187,300965,1,2,0:0:0:0: 1596 | 476,142,301039,1,2,0:0:0:0: 1597 | 443,109,301113,1,2,0:0:0:0: 1598 | 400,92,301188,2,0,B|392:82|392:82|378:96|378:96|374:64|374:64|362:98|362:98|349:86|349:86|343:98|343:98|314:125|246:107,1,190,8|0,0:0|0:0,0:0:0:0: 1599 | 112,104,301483,5,8,0:0:0:0: 1600 | 112,104,301556,1,2,0:0:0:0: 1601 | 112,104,301630,1,2,0:0:0:0: 1602 | 112,104,301704,1,2,0:0:0:0: 1603 | 112,104,301779,2,0,B|120:192|120:192|96:176,1,95,8|0,0:0|0:0,0:0:0:0: 1604 | 48,16,302075,2,0,P|117:-1|184:32,1,142.5,6|0,0:0|0:0,0:0:0:0: 1605 | 232,96,302370,5,8,0:0:0:0: 1606 | 157,196,302518,2,0,P|125:233|108:277,1,95,2|2,0:0|0:0,0:0:0:0: 1607 | 7,197,302814,1,2,0:0:0:0: 1608 | 245,324,302961,2,0,P|292:324|336:344,1,95,8|0,0:0|0:0,0:0:0:0: 1609 | 115,354,303257,1,2,0:0:0:0: 1610 | 295,254,303405,1,2,0:0:0:0: 1611 | 431,384,303552,2,0,P|454:343|449:296,1,95,8|2,0:0|0:0,0:0:0:0: 1612 | 380,106,303848,6,0,L|476:98,1,95,2|2,0:0|0:0,0:0:0:0: 1613 | 256,16,304070,1,2,0:0:0:0: 1614 | 256,16,304144,2,0,L|160:24,1,95,8|0,0:0|0:0,0:0:0:0: 1615 | 149,260,304439,2,0,P|149:172|101:116,1,142.5,2|0,0:0|0:0,0:0:0:0: 1616 | 21,226,304735,6,0,L|141:202,1,95,8|0,0:0|0:0,0:0:0:0: 1617 | 267,299,305030,2,0,L|371:275,1,95,2|2,0:0|0:0,0:0:0:0: 1618 | 464,56,305326,2,0,P|394:34|320:56,1,142.5,8|0,0:0|0:0,0:0:0:0: 1619 | 488,248,305621,2,0,P|512:196|496:144,1,95,6|2,0:0|0:0,0:0:0:0: 1620 | 291,110,305917,5,8,0:0:0:0: 1621 | 302,258,306065,1,2,0:0:0:0: 1622 | 50,263,306213,1,2,0:0:0:0: 1623 | 120,120,306360,1,2,0:0:0:0: 1624 | 88,136,306434,1,2,0:0:0:0: 1625 | 72,168,306508,1,8,0:0:0:0: 1626 | 196,69,306656,2,0,P|180:51|160:40,1,47.5,2|0,0:0|0:0,0:0:0:0: 1627 | 344,280,306804,6,0,P|416:288|488:232,1,142.5,6|0,0:0|0:0,0:0:0:0: 1628 | 262,254,307099,2,0,L|271:374,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: 1629 | 379,361,307395,1,2,0:0:0:0: 1630 | 274,139,307543,1,2,0:0:0:0: 1631 | 160,336,307690,2,0,P|104:320|72:264,1,113.999996520996,8|2,0:0|0:0,0:0:0:0: 1632 | 64,248,307912,1,2,0:0:0:0: 1633 | 64,216,307986,6,0,L|88:88,1,95,2|2,0:0|0:0,0:0:0:0: 1634 | 304,33,308282,2,0,L|295:127,1,95,8|2,0:0|0:0,0:0:0:0: 1635 | 432,296,308577,2,0,P|472:264|480:208,1,95,2|2,0:0|0:0,0:0:0:0: 1636 | 408,112,308873,1,8,0:0:0:0: 1637 | 240,208,309020,5,2,0:0:0:0: 1638 | 248,224,309094,1,2,0:0:0:0: 1639 | 256,240,309168,2,0,L|408:224,1,142.5,6|0,0:0|0:0,0:0:0:0: 1640 | 216,48,309464,6,0,P|160:56|128:88,1,95,8|0,0:0|0:0,0:0:0:0: 1641 | 160,272,309759,1,2,0:0:0:0: 1642 | 200,136,309907,1,2,0:0:0:0: 1643 | 87,174,310055,2,0,L|-20:209,1,95,8|2,0:0|0:0,0:0:0:0: 1644 | 240,352,310277,1,2,0:0:0:0: 1645 | 264,336,310350,2,0,L|256:264,1,47.5,2|0,0:0|0:0,0:0:0:0: 1646 | 58,284,310498,5,2,0:0:0:0: 1647 | 66,300,310572,1,2,0:0:0:0: 1648 | 74,316,310646,2,0,L|88:376,1,47.5,8|0,0:0|0:0,0:0:0:0: 1649 | 328,232,310794,1,2,0:0:0:0: 1650 | 320,216,310868,1,2,0:0:0:0: 1651 | 312,200,310942,2,0,L|292:128,1,47.5,12|0,0:0|0:0,0:0:0:0: 1652 | 145,76,311089,5,8,0:0:0:0: 1653 | 472,128,311237,1,8,0:0:0:0: 1654 | 499,166,311310,1,8,0:0:0:0: 1655 | 511,211,311384,1,8,0:0:0:0: 1656 | 505,258,311458,1,8,0:0:0:0: 1657 | 487,300,311533,6,0,L|368:312,1,104.500003189087,2|0,0:0|0:0,0:0:0:0: 1658 | 143,236,311828,2,0,L|255:228,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1659 | 344,8,312124,1,2,0:0:0:0: 1660 | 384,144,312272,1,2,0:0:0:0: 1661 | 376,176,312346,1,2,0:0:0:0: 1662 | 368,208,312419,5,8,0:0:0:0: 1663 | 282,63,312567,1,2,0:0:0:0: 1664 | 188,197,312715,2,2,P|177:247|200:293,1,104.500003189087 1665 | 448,122,313011,2,0,P|453:70|428:25,1,104.500003189087,8|2,0:0|0:0,0:0:0:0: 1666 | 237,113,313306,6,0,L|83:142,1,156.750004783631,6|0,0:0|0:0,0:0:0:0: 1667 | 410,265,313602,2,0,L|512:243,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1668 | 320,152,313897,1,2,0:0:0:0: 1669 | 176,296,314045,1,2,0:0:0:0: 1670 | 183,129,314193,1,8,0:0:0:0: 1671 | 338,287,314341,1,2,0:0:0:0: 1672 | 406,96,314488,2,0,P|370:60|320:48,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1673 | 446,298,314784,2,0,L|464:192,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1674 | 496,112,315080,1,2,0:0:0:0: 1675 | 320,248,315227,1,2,0:0:0:0: 1676 | 304,272,315301,1,2,0:0:0:0: 1677 | 287,294,315375,2,0,L|175:278,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1678 | 64,80,315671,6,2,L|192:96,1,104.500003189087 1679 | 352,32,315892,1,2,0:0:0:0: 1680 | 352,32,315966,2,0,L|329:165,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1681 | 78,173,316262,6,0,B|65:228|65:228|86:277,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 1682 | 339,172,316557,2,0,P|291:156|219:172,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1683 | 392,336,316853,1,2,0:0:0:0: 1684 | 464,192,317001,2,0,P|469:166|463:141,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1685 | 320,248,317149,6,0,L|313:352,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1686 | 488,288,317444,1,2,0:0:0:0: 1687 | 376,168,317592,1,2,0:0:0:0: 1688 | 352,152,317666,1,2,0:0:0:0: 1689 | 328,136,317740,6,0,L|272:144,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1690 | 168,267,317887,2,0,L|240:259,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1691 | 128,176,318035,6,0,B|0:184|0:184|16:192|24:208,1,156.750004783631,2|0,0:0|0:0,0:0:0:0: 1692 | 152,376,318331,2,0,L|286:364,1,104.500003189087,12|0,0:0|0:0,0:0:0:0: 1693 | 496,192,318626,2,0,P|504:152|456:88,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1694 | 376,32,318848,5,2,0:0:0:0: 1695 | 360,24,318922,2,0,L|248:32,1,104.500003189087,12|0,0:0|0:0,0:0:0:0: 1696 | 124,0,319217,2,0,L|19:6,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1697 | 249,240,319439,1,2,0:0:0:0: 1698 | 265,224,319513,2,0,B|257:112,1,104.500003189087,12|2,0:0|0:0,0:0:0:0: 1699 | 22,326,319809,5,8,0:0:0:0: 1700 | 61,340,319882,1,2,0:0:0:0: 1701 | 102,341,319956,1,2,0:0:0:0: 1702 | 142,329,320030,1,2,0:0:0:0: 1703 | 176,305,320104,2,0,L|168:195,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1704 | 488,56,320400,5,8,0:0:0:0: 1705 | 448,43,320473,1,2,0:0:0:0: 1706 | 407,42,320547,1,2,0:0:0:0: 1707 | 367,54,320621,1,2,0:0:0:0: 1708 | 333,78,320695,2,0,L|356:204,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1709 | 324,337,320991,6,0,P|250:360|178:335,1,156.750004783631,6|0,0:0|0:0,0:0:0:0: 1710 | 50,242,321286,2,0,L|34:138,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1711 | 467,112,321582,1,2,0:0:0:0: 1712 | 456,237,321730,1,2,0:0:0:0: 1713 | 256,224,321878,2,0,P|240:176|264:112,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1714 | 221,27,322173,5,2,0:0:0:0: 1715 | 158,248,322321,1,2,0:0:0:0: 1716 | 456,240,322469,2,0,L|472:136,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1717 | 216,32,322764,1,2,0:0:0:0: 1718 | 178,49,322837,1,2,0:0:0:0: 1719 | 148,79,322911,1,2,0:0:0:0: 1720 | 132,117,322985,1,2,0:0:0:0: 1721 | 60,177,323060,6,0,P|121:175|182:222,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1722 | 277,361,323355,2,0,P|258:299|271:253,1,104.500003189087,6|2,0:0|0:0,0:0:0:0: 1723 | 266,140,323651,2,0,P|281:90|314:50,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1724 | 48,104,323947,6,0,L|152:90,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1725 | 408,160,324242,2,0,L|344:152,1,52.2500015945435,8|0,0:0|0:0,0:0:0:0: 1726 | 312,24,324390,2,0,L|368:32,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1727 | 216,296,324538,5,2,0:0:0:0: 1728 | 173,298,324611,1,2,0:0:0:0: 1729 | 133,286,324685,1,2,0:0:0:0: 1730 | 99,262,324759,1,2,0:0:0:0: 1731 | 76,227,324833,2,0,L|112:104,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1732 | 472,184,325129,5,2,0:0:0:0: 1733 | 490,221,325202,1,2,0:0:0:0: 1734 | 494,262,325276,1,2,0:0:0:0: 1735 | 484,303,325350,1,2,0:0:0:0: 1736 | 462,338,325424,2,0,L|346:369,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1737 | 176,200,325720,6,0,P|232:188|288:208,1,104.500003189087,6|0,0:0|0:0,0:0:0:0: 1738 | 336,248,326016,2,0,L|328:304,2,52.2500015945435,8|0|0,0:0|0:0|0:0,0:0:0:0: 1739 | 512,88,326311,6,0,P|461:76|410:87,1,104.500003189087,2|2,0:0|0:0,0:0:0:0: 1740 | 216,16,326607,2,0,P|272:8|344:32,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1741 | 464,168,326902,6,0,B|447:299|447:299|440:288|416:272,1,156.750004783631,2|0,0:0|0:0,0:0:0:0: 1742 | 272,336,327198,2,0,P|344:336|400:352,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: 1743 | 252,148,327493,5,8,0:0:0:0: 1744 | 224,117,327566,1,2,0:0:0:0: 1745 | 188,97,327640,1,2,0:0:0:0: 1746 | 148,91,327714,1,2,0:0:0:0: 1747 | 109,97,327788,1,8,0:0:0:0: 1748 | 73,117,327862,1,2,0:0:0:0: 1749 | 49,150,327937,2,0,L|112:168,1,52.2500015945435,2|0,0:0|0:0,0:0:0:0: 1750 | 252,208,328084,5,8,0:0:0:0: 1751 | 356,39,328380,1,8,0:0:0:0: 1752 | 49,150,328528,1,8,0:0:0:0: 1753 | 212,57,328823,5,8,0:0:0:0: 1754 | 408,352,328971,2,0,L|425:155,1,190,8|0,0:0|0:0,0:0:0:0: 1755 | 496,200,329415,1,8,0:0:0:0: 1756 | 288,64,329562,6,0,P|293:95|288:120,1,47.5,8|0,0:0|0:0,0:0:0:0: 1757 | 224,320,329710,2,0,P|219:287|224:256,1,47.5,8|0,0:0|0:0,0:0:0:0: 1758 | 96,96,329858,6,0,P|91:133|96:168,1,47.5,8|0,0:0|0:0,0:0:0:0: 1759 | 416,288,330006,2,0,P|424:256|416:208,1,47.5,8|0,0:0|0:0,0:0:0:0: 1760 | 256,120,330301,6,0,P|256:143|256:167,1,47.5,2|0,0:0|0:0,0:0:0:0: 1761 | 256,207,330449,1,12,0:0:0:0: 1762 | 256,192,330523,12,0,340203,0:0:0:0: 1763 | -------------------------------------------------------------------------------- /rename_osu_files.py: -------------------------------------------------------------------------------- 1 | import os 2 | import hashlib 3 | import time 4 | 5 | 6 | def file_hash(file_path: str, hash_method) -> str: 7 | if not os.path.isfile(file_path): 8 | print(f'{file_path} not a file, skip.') 9 | return '' 10 | h = hash_method() 11 | with open(file_path, 'rb') as f: 12 | while b := f.read(8192): 13 | h.update(b) 14 | return h.hexdigest() 15 | 16 | 17 | def str_hash(content: str, hash_method, encoding: str = 'UTF-8') -> str: 18 | return hash_method(content.encode(encoding)).hexdigest() 19 | 20 | 21 | def file_md5(file_path: str) -> str: 22 | return file_hash(file_path, hashlib.md5) 23 | 24 | 25 | def file_sha256(file_path: str) -> str: 26 | return file_hash(file_path, hashlib.sha256) 27 | 28 | 29 | def file_sha512(file_path: str) -> str: 30 | return file_hash(file_path, hashlib.sha512) 31 | 32 | 33 | def file_sha384(file_path: str) -> str: 34 | return file_hash(file_path, hashlib.sha384) 35 | 36 | 37 | def file_sha1(file_path: str) -> str: 38 | return file_hash(file_path, hashlib.sha1) 39 | 40 | 41 | def file_sha224(file_path: str) -> str: 42 | return file_hash(file_path, hashlib.sha224) 43 | 44 | 45 | def str_md5(content: str, encoding: str = 'UTF-8') -> str: 46 | return str_hash(content, hashlib.md5, encoding) 47 | 48 | 49 | def str_sha256(content: str, encoding: str = 'UTF-8') -> str: 50 | return str_hash(content, hashlib.sha256, encoding) 51 | 52 | 53 | def str_sha512(content: str, encoding: str = 'UTF-8') -> str: 54 | return str_hash(content, hashlib.sha512, encoding) 55 | 56 | 57 | def str_sha384(content: str, encoding: str = 'UTF-8') -> str: 58 | return str_hash(content, hashlib.sha384, encoding) 59 | 60 | 61 | def str_sha1(content: str, encoding: str = 'UTF-8') -> str: 62 | return str_hash(content, hashlib.sha1, encoding) 63 | 64 | 65 | def str_sha224(content: str, encoding: str = 'UTF-8') -> str: 66 | return str_hash(content, hashlib.sha224, encoding) 67 | 68 | 69 | def recalculate_osu_files_md5(): 70 | dir_path = input('input your .osu files dir path here: ') 71 | print('listing dir...') 72 | start = time.time() 73 | file_list = os.listdir() 74 | file_count = len(file_list) 75 | 76 | print( 77 | f'done, time spent: {time.time() - start}s; file count: {file_count}; starting...') 78 | done = 0 79 | removed = 0 80 | start = time.time() 81 | for file_name in file_list: 82 | full_path = dir_path + file_name 83 | last_name = os.path.splitext(file_name)[-1] 84 | md5 = file_md5(full_path) 85 | if md5: 86 | try: 87 | os.rename(full_path, dir_path + md5 + last_name) 88 | done += 1 89 | except: 90 | os.remove(full_path) 91 | removed += 1 92 | print( 93 | f'\rdone {done}/{file_count}, removed: {removed}; time spent: {time.time() - start}s', end='') 94 | 95 | 96 | if __name__ == '__main__': 97 | recalculate_osu_files_md5() 98 | -------------------------------------------------------------------------------- /screenshot/ef1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pure-Peace/pp-server/f1654325678e31b6649506af570a9b21adfa1e6d/screenshot/ef1.png -------------------------------------------------------------------------------- /screenshot/ef2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pure-Peace/pp-server/f1654325678e31b6649506af570a9b21adfa1e6d/screenshot/ef2.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | 5 | pub mod objects; 6 | pub mod renders; 7 | pub mod routes; 8 | pub mod settings; 9 | pub mod utils; 10 | 11 | use ntex::web::types::Data; 12 | use objects::{glob::Glob, PPserver}; 13 | 14 | #[ntex::main] 15 | async fn main() { 16 | // Create local settings 17 | let cfg = settings::LocalConfig::init(); 18 | 19 | #[cfg(feature = "with_peace")] 20 | // Create database object includes postgres and redis pool 21 | let database = peace_database::Database::new( 22 | &cfg.data.postgres, 23 | &cfg.data.redis, 24 | cfg.data.check_db_version_on_created, 25 | cfg.data.check_pools_on_created, 26 | ) 27 | .await; 28 | 29 | // Create Glob object 30 | let glob = Data::new( 31 | Glob::init( 32 | &cfg, 33 | #[cfg(feature = "with_peace")] 34 | &database, 35 | ) 36 | .await, 37 | ); 38 | 39 | // Create and start 40 | let mut server = PPserver::new(glob.clone()); 41 | 42 | let _err = server.start().await; 43 | } 44 | -------------------------------------------------------------------------------- /src/objects/caches.rs: -------------------------------------------------------------------------------- 1 | use { 2 | chrono::{DateTime, Local}, 3 | hashbrown::HashMap, 4 | ntex::web::types::Data, 5 | std::sync::atomic::AtomicI32, 6 | tokio::sync::RwLock, 7 | }; 8 | 9 | use peace_objects::beatmaps::CommonBeatmapCaches; 10 | use peace_performance::Beatmap as PPbeatmap; 11 | 12 | use crate::settings::model::LocalConfigData; 13 | 14 | #[derive(Clone)] 15 | pub struct PPbeatmapCache { 16 | pub beatmap: Data, 17 | pub time: DateTime, 18 | } 19 | 20 | impl PPbeatmapCache { 21 | #[inline(always)] 22 | pub fn new(beatmap: PPbeatmap) -> Self { 23 | Self { 24 | beatmap: Data::new(beatmap), 25 | time: Local::now(), 26 | } 27 | } 28 | 29 | #[inline(always)] 30 | pub fn get(&self) -> Data { 31 | self.beatmap.clone() 32 | } 33 | } 34 | 35 | pub struct Caches { 36 | pub beatmap_cache: CommonBeatmapCaches, 37 | pub pp_beatmap_cache: RwLock>, 38 | pub config: LocalConfigData, 39 | } 40 | 41 | impl Caches { 42 | pub fn new(config: LocalConfigData) -> Self { 43 | Self { 44 | beatmap_cache: CommonBeatmapCaches { 45 | md5: RwLock::new(HashMap::with_capacity(500)), 46 | bid: RwLock::new(HashMap::with_capacity(500)), 47 | sid: RwLock::new(HashMap::with_capacity(500)), 48 | length: AtomicI32::new(0), 49 | }, 50 | pp_beatmap_cache: RwLock::new(HashMap::with_capacity(200)), 51 | config, 52 | } 53 | } 54 | 55 | #[inline(always)] 56 | pub async fn cache_pp_beatmap(&self, md5: String, pp_beatmap_cache: PPbeatmapCache) { 57 | let mut cw = self.pp_beatmap_cache.write().await; 58 | if cw.len() as i32 > self.config.beatmap_cache_max { 59 | debug!("[pp_beatmap_cache] Cache exceed max limit."); 60 | return; 61 | }; 62 | cw.insert(md5, pp_beatmap_cache); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/objects/calculator.rs: -------------------------------------------------------------------------------- 1 | use crate::objects::caches::{Caches, PPbeatmapCache}; 2 | use crate::Glob; 3 | 4 | use { 5 | bytes::Bytes, 6 | ntex::web::types::Data, 7 | serde::Deserialize, 8 | serde_json::{json, Value}, 9 | std::{cmp::PartialEq, time::Instant}, 10 | tokio::fs::File, 11 | }; 12 | 13 | use peace_objects::beatmaps::traits::{BeatmapCacheStorage, MyBeatmapCache}; 14 | use peace_performance::{AnyPP, Beatmap as PPbeatmap, FruitsPP, ManiaPP, OsuPP, PpResult, TaikoPP}; 15 | 16 | macro_rules! set_calculator { 17 | ($target:ident.$attr:ident, $calculator:ident) => { 18 | match $target.$attr { 19 | Some($attr) => $calculator.$attr($attr), 20 | None => $calculator, 21 | } 22 | }; 23 | ($target:ident.$attr:ident, $func:ident, $calculator:ident) => { 24 | match $target.$attr { 25 | Some($attr) => $calculator.$func($attr), 26 | None => $calculator, 27 | } 28 | }; 29 | } 30 | 31 | #[derive(PartialEq)] 32 | pub enum GetBeatmapError { 33 | FileNotFound, 34 | ParseError, 35 | } 36 | 37 | impl GetBeatmapError { 38 | #[inline(always)] 39 | pub fn error_message(&self) -> &'static str { 40 | match self { 41 | Self::FileNotFound => "cannot find .osu file", 42 | Self::ParseError => "cannot parse .osu file", 43 | } 44 | } 45 | 46 | #[inline(always)] 47 | pub fn error_status(&self) -> i32 { 48 | match self { 49 | Self::FileNotFound => -1, 50 | Self::ParseError => -2, 51 | } 52 | } 53 | } 54 | 55 | #[derive(Debug, Clone, Deserialize)] 56 | pub struct CalcData { 57 | pub md5: Option, 58 | pub bid: Option, 59 | pub sid: Option, 60 | pub file_name: Option, 61 | pub mode: Option, 62 | pub mods: Option, 63 | pub n50: Option, 64 | pub n100: Option, 65 | pub n300: Option, 66 | pub katu: Option, 67 | pub acc: Option, 68 | pub passed_obj: Option, 69 | pub combo: Option, 70 | pub miss: Option, 71 | pub score: Option, 72 | pub simple: Option, 73 | pub acc_list: Option, 74 | pub no_miss: Option, 75 | } 76 | 77 | #[inline(always)] 78 | pub async fn calculate_pp(beatmap: &PPbeatmap, data: &CalcData) -> PpResult { 79 | // Get target mode calculator 80 | let c = mode_calculator(data.mode.unwrap_or(4), &beatmap); 81 | let c = set_calculator!(data.mods, c); 82 | // Irrelevant for osu!mania 83 | let c = set_calculator!(data.combo, c); 84 | // Irrelevant for osu!mania and osu!taiko 85 | let c = set_calculator!(data.n50, c); 86 | // Irrelevant for osu!mania 87 | let c = set_calculator!(data.n100, c); 88 | // Irrelevant for osu!mania 89 | let c = set_calculator!(data.n300, c); 90 | // Only relevant for osu!ctb 91 | let c = set_calculator!(data.katu, n_katu, c); 92 | // Irrelevant for osu!mania 93 | let c = set_calculator!(data.miss, misses, c); 94 | let c = set_calculator!(data.passed_obj, passed_objects, c); 95 | // Only relevant for osu!mania 96 | let mut c = set_calculator!(data.score, c); 97 | // Irrelevant for osu!mania 98 | if let Some(acc) = data.acc { 99 | c.set_accuracy(acc) 100 | }; 101 | 102 | // Calculate pp 103 | c.calculate().await 104 | } 105 | 106 | #[inline(always)] 107 | pub async fn calculate_acc_list(beatmap: &PPbeatmap, data: &CalcData) -> Value { 108 | let c = mode_calculator(data.mode.unwrap_or(4), &beatmap); 109 | let mut c = match data.mods { 110 | Some(mods) => c.mods(mods), 111 | None => c, 112 | }; 113 | 114 | let acc_100 = { 115 | c.set_accuracy(100.0); 116 | c.calculate().await 117 | }; 118 | let acc_99 = { 119 | c.set_accuracy(99.0); 120 | c.calculate().await 121 | }; 122 | let acc_98 = { 123 | c.set_accuracy(98.0); 124 | c.calculate().await 125 | }; 126 | let acc_95 = { 127 | c.set_accuracy(95.0); 128 | c.calculate().await 129 | }; 130 | 131 | json!({ 132 | "95": acc_95.pp(), 133 | "98": acc_98.pp(), 134 | "99": acc_99.pp(), 135 | "100": acc_100.pp(), 136 | }) 137 | } 138 | 139 | #[inline(always)] 140 | pub fn mode_calculator(mode: u8, beatmap: &PPbeatmap) -> AnyPP { 141 | match mode { 142 | 0 => AnyPP::Osu(OsuPP::new(beatmap)), 143 | 1 => AnyPP::Taiko(TaikoPP::new(beatmap)), 144 | 2 => AnyPP::Fruits(FruitsPP::new(beatmap)), 145 | 3 => AnyPP::Mania(ManiaPP::new(beatmap)), 146 | _ => AnyPP::new(beatmap), 147 | } 148 | } 149 | 150 | #[inline(always)] 151 | pub async fn get_beatmap( 152 | mut md5: Option, 153 | mut bid: Option, 154 | sid: Option, 155 | file_name: Option, 156 | glob: &Glob, 157 | ) -> Option> { 158 | let b = glob 159 | .caches 160 | .beatmap_cache 161 | .get(md5.as_ref(), bid, sid, file_name.as_ref()) 162 | .await; 163 | if let Some(b) = b { 164 | #[cfg(feature = "with_peace")] 165 | let expire = glob.config.read().await.data.beatmaps.cache_expires; 166 | #[cfg(not(feature = "with_peace"))] 167 | let expire = glob.local_config.data.beatmap_cache_timeout as i64; 168 | 169 | if !b.is_expired(expire) { 170 | if let Some(b) = &b.beatmap { 171 | md5 = Some(b.md5.clone()); 172 | bid = Some(b.id); 173 | } 174 | } 175 | }; 176 | 177 | if let Ok(b) = get_beatmap_from_local( 178 | md5.as_ref(), 179 | bid, 180 | &glob.local_config.data.osu_files_dir, 181 | &glob.caches, 182 | ) 183 | .await 184 | { 185 | return Some(b); 186 | }; 187 | get_beatmap_from_api(md5.as_ref(), bid, sid, file_name.as_ref(), glob).await 188 | } 189 | 190 | #[inline(always)] 191 | pub async fn get_beatmap_from_local( 192 | md5: Option<&String>, 193 | bid: Option, 194 | dir: &String, 195 | caches: &Data, 196 | ) -> Result, GetBeatmapError> { 197 | // Try get from beatmap cache 198 | if let Some(md5) = md5 { 199 | if let Some(c) = caches.pp_beatmap_cache.read().await.get(md5) { 200 | let b = c.get(); 201 | debug!("[calculate_pp] Get beatmap {}({:?}) from cache.", md5, bid); 202 | return Ok(b); 203 | }; 204 | 205 | // Try read .osu file 206 | let file = match File::open(format!("{}/{}.osu", dir, md5)).await { 207 | Ok(file) => file, 208 | Err(_) => { 209 | info!("[calculate_pp] Cannot find .osu file, md5: '{}'", md5); 210 | return Err(GetBeatmapError::FileNotFound); 211 | } 212 | }; 213 | 214 | // Try parse .osu file 215 | match PPbeatmap::parse(file).await { 216 | Ok(b) => { 217 | let c = PPbeatmapCache::new(b); 218 | let b = c.get(); 219 | caches.cache_pp_beatmap(md5.to_string(), c).await; 220 | return Ok(b); 221 | } 222 | Err(err) => { 223 | error!( 224 | "[calculate_pp] Cannot parse beatmap file, md5: '{}', err: {:?}", 225 | md5, err 226 | ); 227 | return Err(GetBeatmapError::ParseError); 228 | } 229 | }; 230 | }; 231 | 232 | if let Some(bid) = bid { 233 | if let Some(c) = caches 234 | .pp_beatmap_cache 235 | .read() 236 | .await 237 | .get(&format!("bid_{}", bid)) 238 | { 239 | let b = c.get(); 240 | debug!("[calculate_pp] Get beatmap {:?}({}) from cache.", md5, bid); 241 | return Ok(b); 242 | }; 243 | }; 244 | 245 | Err(GetBeatmapError::FileNotFound) 246 | } 247 | 248 | #[inline(always)] 249 | pub async fn get_beatmap_from_api( 250 | request_md5: Option<&String>, 251 | bid: Option, 252 | sid: Option, 253 | file_name: Option<&String>, 254 | glob: &Glob, 255 | ) -> Option> { 256 | let start = Instant::now(); 257 | let bid = if bid.is_none() { 258 | #[cfg(feature = "with_peace")] 259 | let expires = glob.config.read().await.data.beatmaps.cache_expires; 260 | #[cfg(feature = "with_peace")] 261 | let osu_api = glob.osu_api.read().await; 262 | 263 | #[cfg(not(feature = "with_peace"))] 264 | let expires = glob.local_config.data.beatmap_cache_timeout as i64; 265 | #[cfg(not(feature = "with_peace"))] 266 | let osu_api = &glob.osu_api; 267 | peace_objects::beatmaps::Beatmap::get( 268 | request_md5, 269 | None, 270 | sid, 271 | file_name, 272 | &osu_api, 273 | #[cfg(feature = "with_peace")] 274 | glob.database.get_ref(), 275 | true, 276 | &glob.caches.beatmap_cache, 277 | expires, 278 | ) 279 | .await? 280 | .id 281 | } else { 282 | bid.unwrap() 283 | }; 284 | // Download beatmap, and try parse it 285 | #[cfg(feature = "with_peace")] 286 | let osu_api = glob.osu_api.read().await; 287 | #[cfg(not(feature = "with_peace"))] 288 | let osu_api = &glob.osu_api; 289 | 290 | let (b, new_md5, bytes) = match osu_api.get_pp_beatmap(bid).await { 291 | Ok((b, new_md5, bytes)) => (b, new_md5, bytes), 292 | Err(err) => { 293 | warn!( 294 | "[calculate_pp] Cannot get .osu file from osu!api, err: {:?}", 295 | err 296 | ); 297 | return None; 298 | } 299 | }; 300 | 301 | // Save .osu file locally 302 | write_osu_file( 303 | bytes, 304 | format!("{}/{}.osu", glob.local_config.data.osu_files_dir, new_md5), 305 | ) 306 | .await; 307 | 308 | // Cache it 309 | let c = PPbeatmapCache::new(b); 310 | let b = c.get(); 311 | glob.caches 312 | .cache_pp_beatmap(new_md5.clone(), c.clone()) 313 | .await; 314 | glob.caches 315 | .cache_pp_beatmap(format!("bid_{}", bid), c) 316 | .await; 317 | 318 | // Check .osu file is same md5 319 | if request_md5.is_some() && request_md5.unwrap() != &new_md5 { 320 | warn!("[calculate_pp] Success get .osu file from api, but md5 not eq."); 321 | return None; 322 | } 323 | 324 | info!( 325 | "[calculate_pp] Success get .osu file from api, bid: {:?}, md5: {:?}; time spent: {:?}", 326 | bid, 327 | request_md5, 328 | start.elapsed() 329 | ); 330 | 331 | Some(b) 332 | } 333 | 334 | #[inline(always)] 335 | pub async fn write_osu_file(bytes: Bytes, path: String) -> bool { 336 | match tokio::fs::write(path, bytes).await { 337 | Ok(_) => true, 338 | Err(err) => { 339 | warn!( 340 | "[calculate_pp] Failed to write into .osu file locally, err: {:?}", 341 | err 342 | ); 343 | return false; 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/objects/glob.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "with_peace")] 2 | use tokio::sync::RwLock; 3 | 4 | use ntex::web::types::Data; 5 | use peace_objects::osu_api::OsuApi; 6 | 7 | use super::Caches; 8 | use crate::renders::MainPage; 9 | use crate::settings::LocalConfig; 10 | 11 | #[cfg(feature = "with_peace")] 12 | use peace_database::Database; 13 | #[cfg(feature = "with_peace")] 14 | use peace_objects::peace_api::PeaceApi; 15 | #[cfg(feature = "with_peace")] 16 | use peace_settings::bancho::BanchoConfig; 17 | #[cfg(feature = "with_peace")] 18 | use peace_utils::web::lock_wrapper as lw; 19 | 20 | pub struct Glob { 21 | #[cfg(feature = "with_peace")] 22 | pub osu_api: Data>, 23 | #[cfg(not(feature = "with_peace"))] 24 | pub osu_api: Data, 25 | 26 | #[cfg(feature = "with_peace")] 27 | pub peace_api: Data, 28 | 29 | pub caches: Data, 30 | pub render_main_page: Data, 31 | pub local_config: LocalConfig, 32 | 33 | #[cfg(feature = "with_peace")] 34 | pub database: Data, 35 | #[cfg(feature = "with_peace")] 36 | pub config: Data>, 37 | } 38 | 39 | impl Glob { 40 | pub async fn init( 41 | local_config: &LocalConfig, 42 | #[cfg(feature = "with_peace")] database: &Database, 43 | ) -> Self { 44 | // Create... 45 | #[cfg(feature = "with_peace")] 46 | let cfg = BanchoConfig::create(&database).await.unwrap(); 47 | #[cfg(feature = "with_peace")] 48 | let osu_api = lw(OsuApi::new(cfg.data.server.osu_api_keys.clone()).await); 49 | #[cfg(feature = "with_peace")] 50 | let config = lw(cfg); 51 | #[cfg(feature = "with_peace")] 52 | let peace_api = Data::new(PeaceApi::new( 53 | local_config.data.peace_key.clone(), 54 | local_config.data.peace_url.clone(), 55 | )); 56 | 57 | #[cfg(not(feature = "with_peace"))] 58 | let osu_api = Data::new(OsuApi::new(local_config.data.osu_api_keys.clone()).await); 59 | 60 | let render_main_page = Data::new(MainPage::new()); 61 | let caches = Data::new(Caches::new(local_config.data.clone())); 62 | 63 | Glob { 64 | osu_api, 65 | #[cfg(feature = "with_peace")] 66 | database: Data::new(database.clone()), 67 | #[cfg(feature = "with_peace")] 68 | peace_api, 69 | caches, 70 | render_main_page, 71 | #[cfg(feature = "with_peace")] 72 | config, 73 | local_config: local_config.clone(), 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/objects/mod.rs: -------------------------------------------------------------------------------- 1 | mod caches; 2 | mod server; 3 | 4 | pub use caches::*; 5 | pub use server::PPserver; 6 | pub mod calculator; 7 | pub mod glob; 8 | -------------------------------------------------------------------------------- /src/objects/server.rs: -------------------------------------------------------------------------------- 1 | use { 2 | chrono::Local, 3 | colored::Colorize, 4 | ntex::{ 5 | server::Server, 6 | web::{types::Data, App, HttpServer}, 7 | }, 8 | prometheus::{opts, IntCounterVec}, 9 | std::time::{Duration, Instant}, 10 | tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, 11 | }; 12 | 13 | #[cfg(feature = "with_peace")] 14 | use { 15 | crate::objects::calculator::{self, CalcData}, 16 | ntex::web::types::Query, 17 | }; 18 | 19 | use tokio::sync::Mutex; 20 | 21 | use crate::{ 22 | settings::model::LocalConfigData, 23 | Glob, {routes, utils}, 24 | }; 25 | 26 | pub struct PPserver { 27 | pub addr: String, 28 | pub glob: Data, 29 | // pub prometheus: PrometheusMetrics, 30 | pub counter: IntCounterVec, 31 | pub server: Option, 32 | pub sender: UnboundedSender>, 33 | pub receiver: Data>>>, 34 | pub start_time: Option, 35 | } 36 | 37 | impl PPserver { 38 | pub fn new(glob: Data) -> Self { 39 | let sets = &glob.local_config.data; 40 | let addr = glob 41 | .local_config 42 | .cfg 43 | .get("server.addr") 44 | .unwrap_or("127.0.0.1:8088".to_string()); 45 | 46 | // Prometheus 47 | let counter = Self::prom_init(&addr, sets); 48 | let (sender, receiver) = unbounded_channel(); 49 | 50 | Self { 51 | addr, 52 | glob, 53 | // prometheus, 54 | counter, 55 | server: None, 56 | sender, 57 | receiver: Data::new(Mutex::new(receiver)), 58 | start_time: None, 59 | } 60 | } 61 | 62 | pub async fn run_server(&mut self) { 63 | // Run server 64 | info!("{}", "Starting http server...".bold().bright_blue()); 65 | let server = { 66 | let glob_cloned = self.glob.clone(); 67 | let s = self.glob.local_config.data.clone(); 68 | let counter = self.counter.clone(); 69 | let sender = Data::new(self.sender.clone()); 70 | // let prom = self.prometheus.clone(); 71 | HttpServer::new(move || { 72 | // App 73 | App::new() 74 | .wrap(peace_utils::web::make_logger( 75 | &s.logger.server_log_format, 76 | s.prom.exclude_endpoint_log, 77 | &s.prom.endpoint, 78 | &s.logger.exclude_endpoints, 79 | &s.logger.exclude_endpoints_regex, 80 | )) 81 | // TODO: prometheus 82 | /* .wrap(prom.clone()) 83 | .wrap( 84 | Cors::default() 85 | .allow_any_origin() 86 | .allow_any_header() 87 | .allow_any_method() 88 | .supports_credentials(), 89 | ) */ 90 | .app_data(sender.clone()) 91 | .app_data(glob_cloned.clone()) 92 | .data(counter.clone()) 93 | .configure(|service_cfg| routes::init(service_cfg, &s)) 94 | }) 95 | .shutdown_timeout(2) 96 | .keep_alive(120) 97 | .bind(&self.addr) 98 | .unwrap() 99 | .run() 100 | }; 101 | let _ = self.sender.send(Some(server)); 102 | self.start_time = Some(self.started()); 103 | } 104 | 105 | pub async fn start(&mut self) -> std::io::Result<()> { 106 | let config = &self.glob.local_config.data; 107 | // Should preload or not 108 | if config.preload_osu_files { 109 | utils::preload_osu_files( 110 | &config.osu_files_dir, 111 | config.beatmap_cache_max, 112 | &self.glob.caches, 113 | ) 114 | .await; 115 | }; 116 | 117 | self.start_auto_cache_clean(config.auto_clean_interval, config.beatmap_cache_timeout) 118 | .await; 119 | #[cfg(feature = "with_peace")] 120 | self.start_auto_pp_recalculate( 121 | config.auto_pp_recalculate.interval, 122 | config.auto_pp_recalculate.max_retry, 123 | ) 124 | .await; 125 | 126 | self.run_server().await; 127 | // Wait for stopped 128 | self.stopped().await 129 | } 130 | 131 | #[inline(always)] 132 | // Auto cache clean 133 | pub async fn start_auto_cache_clean(&self, interval: u64, timeout: u64) { 134 | let caches = self.glob.caches.clone(); 135 | let duration = Duration::from_secs(interval); 136 | tokio::task::spawn(async move { 137 | loop { 138 | tokio::time::sleep(duration).await; 139 | let start = Instant::now(); 140 | let mut ready_to_clean = Vec::new(); 141 | let now = Local::now().timestamp(); 142 | 143 | // Collect cache if timeout 144 | let pp_beatmap_cache = caches.pp_beatmap_cache.read().await; 145 | for (k, v) in pp_beatmap_cache.iter() { 146 | if now - v.time.timestamp() > timeout as i64 { 147 | ready_to_clean.push(k.clone()); 148 | } 149 | } 150 | // release read lock 151 | drop(pp_beatmap_cache); 152 | 153 | // Clean timeout cache 154 | if ready_to_clean.len() > 0 { 155 | debug!("[auto_cache_clean] Timeout cache founded, will clean them..."); 156 | let mut pp_beatmap_cache = caches.pp_beatmap_cache.write().await; 157 | for k in ready_to_clean { 158 | pp_beatmap_cache.remove(&k); 159 | } 160 | debug!( 161 | "[auto_cache_clean] task done, time spent: {:?}", 162 | start.elapsed() 163 | ); 164 | } 165 | } 166 | }); 167 | } 168 | 169 | #[cfg(feature = "with_peace")] 170 | #[inline(always)] 171 | /// Auto pp recalculate (When pp calculation fails, join the queue and try to recalculate) 172 | pub async fn start_auto_pp_recalculate(&self, interval: u64, max_retry: i32) { 173 | let duration = Duration::from_secs(interval); 174 | let database = self.glob.database.clone(); 175 | let glob = self.glob.clone(); 176 | tokio::task::spawn(async move { 177 | loop { 178 | let mut process: i32 = 0; 179 | let mut failed: i32 = 0; 180 | let mut update_user_tasks = Vec::new(); 181 | let start = Instant::now(); 182 | // Try get tasks from redis 183 | let keys: Option> = match database.redis.query("KEYS", "calc:*").await { 184 | Ok(k) => Some(k), 185 | Err(err) => { 186 | error!( 187 | "[auto_pp_recalculate] Failed to get redis keys, err: {:?}", 188 | err 189 | ); 190 | None 191 | } 192 | }; 193 | if let Some(keys) = keys { 194 | let total = keys.len(); 195 | if total > 0 { 196 | debug!( 197 | "[auto_pp_recalculate] {} task founded! start recalculate!", 198 | keys.len() 199 | ); 200 | for key in keys { 201 | process += 1; 202 | debug!("[auto_pp_recalculate] task{}/{}: {}", process, total, key); 203 | 204 | let k = key.split(":").collect::>(); 205 | if k.len() != 4 { 206 | warn!( 207 | "[auto_pp_recalculate] Invalid key(key length): {}, remove it;", 208 | key 209 | ); 210 | failed += 1; 211 | let _ = database.redis.del(key).await; 212 | continue; 213 | }; 214 | 215 | // Get key info 216 | let table = k[1]; 217 | let score_id = match k[2].parse::() { 218 | Ok(i) => i, 219 | Err(err) => { 220 | warn!("[auto_pp_recalculate] Invalid key(score_id): {}, remove it; err: {:?}", key, err); 221 | failed += 1; 222 | let _ = database.redis.del(key).await; 223 | continue; 224 | } 225 | }; 226 | let player_id = match k[3].parse::() { 227 | Ok(i) => i, 228 | Err(err) => { 229 | warn!("[auto_pp_recalculate] Invalid key(player_id): {}, remove it; err: {:?}", key, err); 230 | failed += 1; 231 | let _ = database.redis.del(key).await; 232 | continue; 233 | } 234 | }; 235 | 236 | // Get key data 237 | let s = match database.redis.get::(&key).await { 238 | Ok(s) => s, 239 | Err(err) => { 240 | warn!("[auto_pp_recalculate] Invalid key(data): {}, remove it; err: {:?}", key, err); 241 | failed += 1; 242 | let _ = database.redis.del(key).await; 243 | continue; 244 | } 245 | }; 246 | let values = s.split(":").collect::>(); 247 | if values.len() != 2 { 248 | warn!("[auto_pp_recalculate] Invalid key(values length): {}, remove it", key); 249 | failed += 1; 250 | let _ = database.redis.del(key).await; 251 | continue; 252 | } 253 | let try_count = match values[0].parse::() { 254 | Ok(i) => i, 255 | Err(err) => { 256 | warn!("[auto_pp_recalculate] Invalid key(try_count): {}, remove it; err: {:?}", key, err); 257 | failed += 1; 258 | let _ = database.redis.del(key).await; 259 | continue; 260 | } 261 | }; 262 | if try_count >= max_retry { 263 | warn!("[auto_pp_recalculate] key {} over max_retry, skip it;", key); 264 | // failed += 1; 265 | process -= 1; 266 | // Don't remove, we should check why 267 | // let _ = database.redis.del(key).await; 268 | continue; 269 | }; 270 | let data = match Query::::from_query(values[1]) { 271 | Ok(Query(d)) => d, 272 | Err(err) => { 273 | warn!("[auto_pp_recalculate] Invalid key(calc data parse): {}, remove it; err: {:?}", key, err); 274 | failed += 1; 275 | let _ = database.redis.del(key).await; 276 | continue; 277 | } 278 | }; 279 | 280 | // get beatmap 281 | let beatmap = match calculator::get_beatmap( 282 | data.md5.clone(), 283 | data.bid, 284 | data.sid, 285 | data.file_name.clone(), 286 | &glob, 287 | ) 288 | .await 289 | { 290 | Some(b) => b, 291 | None => { 292 | warn!("[auto_pp_recalculate] Failed to get beatmap, key: {}, data: {:?}; try_count: {}", key, data, try_count); 293 | failed += 1; 294 | let _ = database 295 | .redis 296 | .set(&key, format!("{}:{}", try_count + 1, values[1])) 297 | .await; 298 | continue; 299 | } 300 | }; 301 | // calculate. 302 | let r = calculator::calculate_pp(&beatmap, &data).await; 303 | 304 | // Save it 305 | match database.pg.query_first( 306 | &format!(r#"UPDATE "game_scores"."{}" SET pp_v2 = $1, pp_v2_raw = $2, stars = $3 WHERE "id" = $4 RETURNING "status", "map_md5""#, table), &[ 307 | &r.pp(), &serde_json::json!({ 308 | "aim": r.raw.aim, 309 | "spd": r.raw.spd, 310 | "str": r.raw.str, 311 | "acc": r.raw.acc, 312 | "total": r.raw.total, 313 | }), &r.stars(), &score_id 314 | ]).await { 315 | Ok(row) => { 316 | let mode_val = data.mode.unwrap_or(0); 317 | let mode = peace_constants::GameMode::parse(mode_val).unwrap(); 318 | 319 | let status = row.get::<'_, _, i16>("status"); 320 | let map_md5 = row.get::<'_, _, String>("map_md5"); 321 | // PassedAndTop 322 | let status = if status == 1 { 323 | match database.pg.execute( 324 | &format!( 325 | r#"UPDATE "game_scores"."{}" SET "status" = 1 WHERE 326 | user_id = $1 AND 327 | "map_md5" = $2 AND 328 | "status" = 2 AND 329 | pp_v2 <= $3"#, table), &[ 330 | &player_id, &map_md5, &r.pp 331 | ]).await { 332 | Ok(updated) => { 333 | if updated > 0 { 334 | let _ = database.pg.execute(&format!(r#"UPDATE "game_scores"."{}" SET "status" = 2 WHERE "id" = $1"#, table), &[&score_id]).await; 335 | let _ = glob.peace_api.simple_get(&format!("api/v1/recreate_score_table/{}/{}", &map_md5, mode_val)).await; 336 | 2 337 | } else { 338 | status 339 | } 340 | }, 341 | Err(err) => { 342 | error!("[auto_pp_recalculate] Failed to update scores status, err: {:?}; map_md5: {}, user_id: {}", err, map_md5, player_id); 343 | 0 344 | } 345 | } 346 | } else { 347 | status 348 | }; 349 | if status == 2 { 350 | match peace_utils::peace::player_calculate_pp_acc(player_id, &mode.full_name(), &database).await { 351 | Some(result) => { 352 | if peace_utils::peace::player_save_pp_acc(player_id, &mode, result.pp, result.acc, &database).await { 353 | let update_info = peace_constants::api::UpdateUserTask { 354 | player_id, 355 | mode: mode_val, 356 | recalc: false 357 | }; 358 | // Prevent repeated update the same user in the same mode 359 | if !update_user_tasks.contains(&update_info) { 360 | update_user_tasks.push(update_info); 361 | } 362 | } else { 363 | error!("[auto_pp_recalculate] Failed to save player {} pp and acc!", player_id) 364 | } 365 | }, 366 | None => error!("[auto_pp_recalculate] Failed to calculate player {} pp and acc!", player_id) 367 | }; 368 | }; 369 | debug!("[auto_pp_recalculate] key {} calculate done", key); 370 | // Remove this recalc task from redis 371 | let _ = database.redis.del(key).await; 372 | 373 | continue; 374 | }, 375 | Err(err) => { 376 | error!("[auto_pp_recalculate] Failed to save calculate result, key: {}, err: {:?}", key, err); 377 | failed += 1; 378 | let _ = database 379 | .redis 380 | .set(&key, format!("{}:{}", try_count + 1, values[1])) 381 | .await; 382 | continue; 383 | } 384 | }; 385 | } 386 | info!( 387 | "[auto_pp_recalculate] task done, time spent: {:?}; success({}) / total({}) failed({})", 388 | start.elapsed(), update_user_tasks.len(), process, failed 389 | ); 390 | } 391 | }; 392 | // If some users should updates, send it to peace 393 | if update_user_tasks.len() > 0 { 394 | debug!("[auto_pp_recalculate] send peace to update these users..."); 395 | let start = Instant::now(); 396 | glob.peace_api 397 | .post("api/v1/update_user_stats", &update_user_tasks) 398 | .await; 399 | debug!( 400 | "[auto_pp_recalculate] done! time spent: {:?}", 401 | start.elapsed() 402 | ); 403 | } 404 | tokio::time::sleep(duration).await; 405 | } 406 | }); 407 | } 408 | 409 | /// Server started 410 | pub fn started(&self) -> Instant { 411 | // Server started 412 | let text = format!("Server is Running at http://{}", self.addr) 413 | .bold() 414 | .green(); 415 | info!("{}", text); 416 | Instant::now() 417 | } 418 | 419 | /// Server stopped 420 | pub async fn stopped(&self) -> std::io::Result<()> { 421 | let server = self.receiver.lock().await.recv().await.unwrap().unwrap(); 422 | // Waiting for server stopped 423 | let rx = self.receiver.clone(); 424 | let srv = server.clone(); 425 | tokio::task::spawn(async move { 426 | if let Some(_) = rx.lock().await.recv().await { 427 | warn!("Received shutdown signal, stop server..."); 428 | srv.stop(true).await 429 | } 430 | }); 431 | let err = server.await; 432 | let title = format!("Server has Stopped!").bold().yellow(); 433 | let time_string = format!( 434 | "Server running time: {:?}\n", 435 | self.start_time.unwrap().elapsed() 436 | ) 437 | .bold() 438 | .bright_blue(); 439 | warn!("{} \n\n {}", title, time_string); 440 | err 441 | } 442 | 443 | pub fn prom_init(addr: &String, sets: &LocalConfigData) -> IntCounterVec { 444 | // Ready prometheus 445 | println!( 446 | "> {}", 447 | format!("Prometheus endpoint: {}", sets.prom.endpoint).green() 448 | ); 449 | println!( 450 | "> {}", 451 | format!("Prometheus namespace: {}", sets.prom.namespace).green() 452 | ); 453 | println!( 454 | "> {}\n", 455 | format!( 456 | "Prometheus metrics address: http://{}{}", 457 | addr, sets.prom.endpoint 458 | ) 459 | .bold() 460 | .green() 461 | ); 462 | 463 | // Labels 464 | let mut labels = std::collections::HashMap::new(); 465 | labels.insert("job".to_string(), sets.prom.namespace.to_string()); 466 | 467 | // Counter 468 | let counter_opts = opts!("counter", "some random counter").namespace("api"); 469 | let counter = IntCounterVec::new(counter_opts, &["endpoint", "method", "status"]).unwrap(); 470 | 471 | // Init prometheus 472 | /* let prometheus = PrometheusMetrics::new( 473 | &sets.prom.namespace, 474 | Some(&sets.prom.endpoint), 475 | Some(labels), 476 | ); 477 | prometheus 478 | .registry 479 | .register(Box::new(counter.clone())) 480 | .unwrap(); */ 481 | 482 | counter 483 | } 484 | } 485 | -------------------------------------------------------------------------------- /src/renders/mod.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | 3 | #[derive(Template, Clone)] 4 | #[template(path = "main_page.html")] 5 | pub struct MainPage {} 6 | 7 | impl MainPage { 8 | pub fn new() -> Self { 9 | MainPage {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/routes/api.rs: -------------------------------------------------------------------------------- 1 | use { 2 | askama::Template, 3 | ntex::web::{ 4 | get, 5 | types::{Data, Query}, 6 | HttpRequest, HttpResponse, 7 | }, 8 | peace_performance::PpResult, 9 | serde_json::json, 10 | serde_json::Value::Null, 11 | std::time::Instant, 12 | }; 13 | 14 | use crate::{ 15 | objects::calculator::{self, CalcData}, 16 | Glob, 17 | }; 18 | 19 | /// GET "/api" 20 | #[get("")] 21 | pub async fn index(glob: Data) -> HttpResponse { 22 | HttpResponse::Ok() 23 | .content_type("text/html") 24 | .body(glob.render_main_page.render().unwrap()) 25 | } 26 | 27 | // calculate pp (used by peace) 28 | #[get("/calc")] 29 | pub async fn calculate_pp(req: HttpRequest, glob: Data) -> HttpResponse { 30 | let get_raw = |result: &PpResult| { 31 | json!({ 32 | "aim": result.raw.aim.unwrap_or(0.0), 33 | "spd": result.raw.spd.unwrap_or(0.0), 34 | "acc": result.raw.acc.unwrap_or(0.0), 35 | "str": result.raw.str.unwrap_or(0.0), 36 | "total": result.raw.total, 37 | }) 38 | }; 39 | let failed = |status, message| { 40 | HttpResponse::Ok() 41 | .content_type("application/json") 42 | .body(json!( 43 | { 44 | "status": status, 45 | "message": message, 46 | "pp": null 47 | } 48 | )) 49 | }; 50 | let start = Instant::now(); 51 | 52 | // Parse query data 53 | let mut data = match Query::::from_query(&req.query_string()) { 54 | Ok(Query(q)) => q, 55 | Err(err) => { 56 | return failed(0, err.to_string().as_str()); 57 | } 58 | }; 59 | 60 | // We need any one of these 61 | if data.md5.is_none() && data.bid.is_none() && data.sid.is_none() { 62 | return failed( 63 | 0, 64 | "invalid requests, we must have one of: (md5, bid, sid + filename)", 65 | ); 66 | }; 67 | 68 | let mut md5 = data.md5.clone(); 69 | let bid = data.bid; 70 | 71 | // If we have md5 input 72 | if let Some(ref mut md5) = md5 { 73 | // Check md5 74 | if md5.len() != 32 { 75 | return failed(0, "invalid md5"); 76 | } 77 | // Safe it 78 | *md5 = peace_utils::common::safe_string(md5.clone()); 79 | }; 80 | 81 | // get beatmap 82 | let beatmap = 83 | match calculator::get_beatmap(md5.clone(), bid, data.sid, data.file_name.clone(), &glob) 84 | .await 85 | { 86 | Some(b) => b, 87 | None => return failed(0, "cannot found beatmap"), 88 | }; 89 | 90 | // Get it, calculate. 91 | let result = calculator::calculate_pp(&beatmap, &data).await; 92 | 93 | let mut value = json!({ 94 | "status": 1, 95 | "message": "done", 96 | "mode": result.mode, 97 | "mods": result.mods, 98 | "pp": result.pp(), 99 | "stars": result.attributes.stars(), 100 | "acc_list": Null 101 | }); 102 | 103 | // If need, calculate acc list.. 104 | if data.acc_list.is_some() && data.acc_list.unwrap() > 0 { 105 | value["acc_list"] = calculator::calculate_acc_list(&beatmap, &data).await; 106 | }; 107 | 108 | // If need, calculate no_miss 109 | if data.no_miss.is_some() && data.no_miss.unwrap() > 0 { 110 | data.miss = Some(0); 111 | let no_miss_result = calculator::calculate_pp(&beatmap, &data).await; 112 | let json = json!({ 113 | "pp": no_miss_result.pp(), 114 | "raw": get_raw(&no_miss_result) 115 | }); 116 | value["no_miss"] = json; 117 | }; 118 | 119 | let end = start.elapsed(); 120 | info!( 121 | "[calculate_pp] Beatmap {:?}({:?}) calculate done in: {:?}", 122 | md5, bid, end 123 | ); 124 | 125 | if data.simple.is_some() && data.simple.unwrap() > 0 { 126 | HttpResponse::Ok() 127 | .content_type("application/json") 128 | .body(value) 129 | } else { 130 | value["raw"] = get_raw(&result); 131 | HttpResponse::Ok() 132 | .content_type("application/json") 133 | .body(value) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/routes/debug.rs: -------------------------------------------------------------------------------- 1 | use { 2 | ntex::{ 3 | server::Server, 4 | web::{get, types::Data, HttpResponse}, 5 | }, 6 | std::time::Instant, 7 | tokio::sync::mpsc::UnboundedSender, 8 | }; 9 | 10 | use crate::objects::Caches; 11 | 12 | /// GET "/" 13 | #[get("/")] 14 | pub async fn index() -> HttpResponse { 15 | let contents = r#" 16 | 17 | 18 | 19 | Hello! 20 | 21 | 22 |

Hello!

23 |

Hi from Rust

24 | 25 | "#; 26 | HttpResponse::Ok().body(contents) 27 | } 28 | 29 | /// GET "/server_stop" 30 | #[get("/server_stop")] 31 | pub async fn server_stop(sender: Data>>) -> HttpResponse { 32 | let start = Instant::now(); 33 | let _ = sender.send(None); 34 | let end = start.elapsed(); 35 | HttpResponse::Ok().body(format!("server_stop done in: {:?}", end)) 36 | } 37 | 38 | /// GET "/clear_cache" 39 | #[get("/clear_cache")] 40 | pub async fn clear_cache(caches: Data) -> HttpResponse { 41 | let start = Instant::now(); 42 | caches.pp_beatmap_cache.write().await.clear(); 43 | let end = start.elapsed(); 44 | HttpResponse::Ok().body(format!("clear_cache done in: {:?}", end)) 45 | } 46 | -------------------------------------------------------------------------------- /src/routes/default.rs: -------------------------------------------------------------------------------- 1 | use askama::Template; 2 | use ntex::web::{get, types::Data, HttpResponse}; 3 | 4 | use crate::objects::glob::Glob; 5 | 6 | /// GET "/" 7 | #[get("/")] 8 | pub async fn index(glob: Data) -> HttpResponse { 9 | HttpResponse::Ok() 10 | .content_type("text/html") 11 | .body(glob.render_main_page.render().unwrap()) 12 | } 13 | -------------------------------------------------------------------------------- /src/routes/mod.rs: -------------------------------------------------------------------------------- 1 | mod api; 2 | mod debug; 3 | mod default; 4 | 5 | use ntex::web::{scope, ServiceConfig}; 6 | 7 | use crate::settings::model::LocalConfigData; 8 | 9 | /// Function that will be called on new Application to configure routes for this module 10 | /// Initial all routes 11 | pub fn init(cfg: &mut ServiceConfig, settings: &LocalConfigData) { 12 | init_default(cfg); 13 | init_api(cfg); 14 | 15 | // !warning: only debug! 16 | if settings.debug == true { 17 | init_debug(cfg) 18 | } 19 | } 20 | 21 | /// Routes for api 22 | fn init_api(cfg: &mut ServiceConfig) { 23 | use api::*; 24 | cfg.service(scope("/api").service(index).service(calculate_pp)); 25 | } 26 | 27 | fn init_debug(cfg: &mut ServiceConfig) { 28 | use debug::*; 29 | cfg.service(index); 30 | cfg.service(server_stop); 31 | cfg.service(clear_cache); 32 | } 33 | 34 | /// Routes for default 35 | fn init_default(cfg: &mut ServiceConfig) { 36 | use default::*; 37 | cfg.service(index); 38 | } 39 | -------------------------------------------------------------------------------- /src/settings/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod model; 2 | 3 | pub const BANNER: &str = r#" 4 | 5 | _____ _____ _____ _____ _____ __ __ _____ _____ 6 | / _ \/ _ \ ___ / ___>/ __\/ _ \/ | \/ __\/ _ \ 7 | | __/| __/<___>|___ || __|| _ <\ | /| __|| _ < 8 | \__/ \__/ <_____/\_____/\__|\_/ \___/ \_____/\__|\_/ 9 | 10 | 11 | "#; 12 | const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); 13 | 14 | use colored::Colorize; 15 | 16 | #[derive(Debug, Clone)] 17 | pub struct LocalConfig { 18 | pub env: String, 19 | pub cfg: config::Config, 20 | pub data: model::LocalConfigData, 21 | } 22 | 23 | impl LocalConfig { 24 | pub fn new() -> Result { 25 | // Print banner 26 | println!("{}", BANNER.green()); 27 | 28 | // Start loading 29 | println!( 30 | " version: {}. Taking off!\n", 31 | VERSION.unwrap_or("unknown").green() 32 | ); 33 | println!("{}", "> Start loading local config!".green()); 34 | let env = peace_settings::local::utils::load_env(); 35 | let cfg = peace_settings::local::utils::load_settings( 36 | env.clone(), 37 | peace_constants::common::PP_SERVER_LOCAL_CONFIG_DIR, 38 | )?; 39 | let data: model::LocalConfigData = cfg.clone().try_into()?; 40 | println!( 41 | "{}", 42 | "> Configuration loaded successfully!\n".bold().green() 43 | ); 44 | // You can deserialize (and thus freeze) the entire configuration as cfg.try_into() 45 | Ok(LocalConfig { env, cfg, data }) 46 | } 47 | 48 | pub fn init() -> Self { 49 | let cfg = LocalConfig::new(); 50 | if let Err(err) = cfg { 51 | error!("Settings failed to initialize, please check the local configuration file! Error: {:?}", err); 52 | panic!(); 53 | } 54 | cfg.unwrap() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/settings/model.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Debug, Deserialize, Clone)] 4 | pub struct LocalConfigData { 5 | #[cfg(feature = "with_peace")] 6 | pub postgres: deadpool_postgres::Config, 7 | #[cfg(feature = "with_peace")] 8 | pub redis: deadpool_redis::Config, 9 | #[cfg(not(feature = "with_peace"))] 10 | pub osu_api_keys: Vec, 11 | 12 | #[cfg(feature = "with_peace")] 13 | pub check_pools_on_created: bool, 14 | #[cfg(feature = "with_peace")] 15 | pub check_db_version_on_created: bool, 16 | #[cfg(feature = "with_peace")] 17 | pub peace_key: String, 18 | #[cfg(feature = "with_peace")] 19 | pub peace_url: String, 20 | 21 | pub env: String, 22 | pub debug: bool, 23 | pub osu_files_dir: String, 24 | pub recalculate_osu_file_md5: bool, 25 | pub preload_osu_files: bool, 26 | pub beatmap_cache_max: i32, 27 | pub beatmap_cache_timeout: u64, 28 | pub auto_clean_cache: bool, 29 | pub auto_clean_interval: u64, 30 | pub auto_pp_recalculate: AutoPPRecalculate, 31 | pub server: Server, 32 | pub logger: Logger, 33 | #[serde(rename = "prometheus")] 34 | pub prom: Prometheus, 35 | } 36 | 37 | #[derive(Debug, Deserialize, Clone)] 38 | pub struct Server { 39 | pub host: String, 40 | pub port: String, 41 | } 42 | 43 | #[derive(Debug, Deserialize, Clone)] 44 | pub struct LoggerMode { 45 | debug: String, 46 | error: String, 47 | warn: String, 48 | info: String, 49 | } 50 | 51 | #[derive(Debug, Deserialize, Clone)] 52 | pub struct Logger { 53 | pub level: String, 54 | pub mode: LoggerMode, 55 | pub server_log_format: String, 56 | pub exclude_endpoints: Vec, 57 | pub exclude_endpoints_regex: Vec, 58 | } 59 | 60 | #[derive(Debug, Deserialize, Clone)] 61 | pub struct Prometheus { 62 | pub namespace: String, 63 | pub endpoint: String, 64 | pub exclude_endpoint_log: bool, 65 | } 66 | 67 | #[derive(Debug, Deserialize, Clone)] 68 | pub struct AutoPPRecalculate { 69 | pub interval: u64, 70 | pub max_retry: i32, 71 | } 72 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use ntex::web::types::Data; 2 | use tokio::fs::File as AsyncFile; 3 | use colored::Colorize; 4 | use peace_performance::Beatmap as PPbeatmap; 5 | use std::cmp::min; 6 | use std::time::Instant; 7 | use std::{fs, io}; 8 | 9 | use crate::objects::{Caches, PPbeatmapCache}; 10 | 11 | #[inline(always)] 12 | pub fn check_is_osu_file(entry: &Result) -> u8 { 13 | if entry.is_err() { 14 | return 3; 15 | }; 16 | let entry = entry.as_ref().unwrap(); 17 | if entry.path().is_dir() { 18 | return 2; 19 | }; 20 | let file_name = match entry.file_name().into_string() { 21 | Ok(n) => n, 22 | Err(_) => { 23 | return 3; 24 | } 25 | }; 26 | if !file_name.ends_with(".osu") { 27 | return 3; 28 | }; 29 | 1 30 | } 31 | 32 | #[inline(always)] 33 | pub fn listing_osu_files(osu_files_dir: &String) -> (Vec>, usize) { 34 | println!( 35 | "{}", 36 | format!("\n> Listing .osu dir '{}' now...", osu_files_dir).bright_yellow() 37 | ); 38 | let entries: Vec> = fs::read_dir(osu_files_dir.clone()) 39 | .unwrap() 40 | .map(|r| match check_is_osu_file(&r) { 41 | 1 => Some(r.unwrap()), 42 | _ => None, 43 | }) 44 | .filter(|r| r.is_some()) 45 | .collect(); 46 | let total = entries.len(); 47 | println!( 48 | "\n{}", 49 | format!("> Done, .osu file count: {}", total).bright_yellow() 50 | ); 51 | (entries, total) 52 | } 53 | 54 | #[inline(always)] 55 | pub async fn preload_osu_files(osu_files_dir: &String, max_load: i32, caches: &Data) { 56 | let (entries, total) = listing_osu_files(osu_files_dir); 57 | if total > 9000 && max_load > 9000 { 58 | println!("{}", "WARNING: Your will preload > 9000 beatmaps, loading them into memory may cause insufficient memory or even system crashes.".red()) 59 | }; 60 | println!("\n Preloading .osu files into Memory..."); 61 | let bar = peace_utils::common::progress_bar(min(max_load, total as i32) as u64); 62 | let mut success = 0; 63 | let start = Instant::now(); 64 | let mut pp_beatmap_cache = caches.pp_beatmap_cache.write().await; 65 | for entry in entries { 66 | bar.inc(1); 67 | if let Some(entry) = entry { 68 | { 69 | if let Ok(file_name) = entry.file_name().into_string() { 70 | let md5 = file_name.replace(".osu", ""); 71 | { 72 | if let Ok(file) = AsyncFile::open(entry.path()).await { 73 | match PPbeatmap::parse(file).await { 74 | Ok(b) => { 75 | pp_beatmap_cache.insert(md5.to_string(), PPbeatmapCache::new(b)) 76 | } 77 | Err(_e) => continue, 78 | }; 79 | }; 80 | } 81 | } 82 | } 83 | success += 1; 84 | if success >= max_load { 85 | break; 86 | } 87 | } 88 | } 89 | bar.finish(); 90 | println!( 91 | "\n{}\n", 92 | format!( 93 | "> Beatmaps has preloaded, \n> Success: {}, Total: {}, MaxLoad: {}; \n> time spent: {:?}", 94 | success, 95 | total, 96 | max_load, 97 | start.elapsed() 98 | ) 99 | .bright_yellow() 100 | ) 101 | } 102 | 103 | #[inline(always)] 104 | pub fn recalculate_osu_file_md5(osu_files_dir: &String) { 105 | let mut renamed = 0; 106 | let mut done = 0; 107 | let mut error = 0; 108 | let (entries, total) = listing_osu_files(osu_files_dir); 109 | println!("\n Recalculating MD5 file names..."); 110 | let bar = peace_utils::common::progress_bar(total as u64); 111 | let start = Instant::now(); 112 | for entry in entries { 113 | bar.inc(1); 114 | if let Some(entry) = entry { 115 | let md5 = match peace_utils::async_file::calc_file_md5(entry.path()) { 116 | Ok(md5) => md5, 117 | Err(_) => { 118 | error += 1; 119 | continue; 120 | } 121 | }; 122 | if fs::rename(entry.path(), format!("{}/{}.osu", osu_files_dir, md5)).is_err() { 123 | error += 1; 124 | } else { 125 | renamed += 1; 126 | } 127 | done += 1; 128 | } 129 | } 130 | bar.finish(); 131 | println!( 132 | "{}\n", 133 | format!( 134 | "> Done, \n> Success / Done / Total: {} / {} / {}; \n> Errors: {}; \n> time spent: {:?}", 135 | renamed, 136 | done, 137 | total, 138 | error, 139 | start.elapsed() 140 | ) 141 | .bright_yellow() 142 | ) 143 | } 144 | 145 | #[inline(always)] 146 | pub fn checking_osu_dir(osu_files_dir: &String, recalculate_md5: bool) { 147 | if osu_files_dir == "" { 148 | println!( 149 | "{}", 150 | "> [Error] Please add .osu files dir in pp-server-config!!!\n" 151 | .bold() 152 | .red() 153 | ); 154 | } else if recalculate_md5 { 155 | recalculate_osu_file_md5(osu_files_dir); 156 | }; 157 | } 158 | -------------------------------------------------------------------------------- /templates/main_page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PP Server | Welcome~ 8 | 9 | 10 | 11 |

Hello~ PP-server is DEVELOPING.

12 |

Please access /api/calc to calculate pp, usage:

13 |

Sample

14 |

WITH MD5: /api/calc?md5=ccb1f31b5eeaf26d40f8c905293efc03

16 |

WITH BID: /api/calc?bid=2848898

17 |

WITH SID + FILENAME: /api/calc?sid=1378720&file_name=Tanchiky%20-%20Bridge%20(NyarkoO)%20[Extension].osu 19 |

20 |

Optional:

21 |

md5 (Get beatmap with md5) *Recommend

22 |

bid (Get beatmap with bid)

23 |

sid (Get beatmap with sid and file_name)

24 |

file_name ({artist} - {title} ({mapper}) [{diff_name}].osu)

25 |

mode (0 = osu!, 1 = Taiko, 2 = CtB, 3 = osu!mania).

26 |

mods (See osu-api/wiki)

27 |

n50 (Count of 50) [ Irrelevant for osu!mania and osu!taiko ]

28 |

n100 (Count of 100) [ Irrelevant for osu!mania ]

29 |

n300 (Count of 300) [ Irrelevant for osu!mania ]

30 |

acc (float: 0-100) [ Irrelevant for osu!mania ]

31 |

miss (Count of miss) [ Irrelevant for osu!mania ]

32 |

score [ Only relevant for osu!mania ]

33 |

combo (Your combo) [ Irrelevant for osu!mania ]

34 |

katu (Count of katu) [ Only relevant for osu!ctb ]

35 |

passed_obj (If failed, use count of passed objects)

36 |

simple (0 or 1; if 1, returns simple info)

37 |

acc_list (0 or 1; if 1, calculate and returns pp results of acc 95, 98, 99, 100.

38 |

no_miss (0 or 1; if 1, calculate pp if no miss.

39 | 40 | 41 | 42 | 70 | 71 | -------------------------------------------------------------------------------- /win_cross_compile_linux.bat: -------------------------------------------------------------------------------- 1 | cargo cross_linux_x86 --------------------------------------------------------------------------------