mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge remote-tracking branch 'origin/master' into sharp/map-colors
This commit is contained in:
commit
f8926a5737
@ -8,7 +8,8 @@ variables:
|
||||
stages:
|
||||
- optional-builds
|
||||
- check-compile
|
||||
- post
|
||||
- build-post
|
||||
- publish
|
||||
|
||||
before_script:
|
||||
- source $HOME/.cargo/env
|
||||
@ -70,7 +71,7 @@ check:
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- RUSTFLAGS="-D warnings" cargo check
|
||||
- RUSTFLAGS="-D warnings" cargo check --locked
|
||||
|
||||
code-quality:
|
||||
stage: check-compile
|
||||
@ -90,29 +91,30 @@ security:
|
||||
|
||||
# --
|
||||
|
||||
# -- post build
|
||||
# -- build-post
|
||||
|
||||
unittests:
|
||||
stage: post
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- echo "Workaround, cargo tests fails due some rust files are already deleted, so we just stack cargo test. if its the os error, it wont appear on them all, if its a real error, it will retry and then fail"
|
||||
- cargo test || cargo test || cargo test || cargo test
|
||||
- cargo test || ( sleep 10 && cargo test ) || ( sleep 10 && cargo test ) || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test || cargo test
|
||||
|
||||
coverage:
|
||||
stage: post
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- cargo tarpaulin -v
|
||||
- echo "Workaround, tarpaulin fails due some rust files are already deleted, so we just stack tarpaulin. if its the os error, it wont appear on them all, if its a real error, it will retry and then fail"
|
||||
- cargo tarpaulin -v || ( sleep 10 && cargo tarpaulin -v ) || ( sleep 10 && cargo tarpaulin -v ) || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v || cargo tarpaulin -v
|
||||
|
||||
benchmarks:
|
||||
stage: post
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
tags:
|
||||
@ -121,14 +123,26 @@ benchmarks:
|
||||
- unset DISABLE_GIT_LFS_CHECK
|
||||
- cargo bench
|
||||
|
||||
localization-status:
|
||||
variables:
|
||||
GIT_DEPTH: 0
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
allow_failure: true
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- cargo test -q test_all_localizations -- --nocapture --ignored
|
||||
|
||||
linux:
|
||||
stage: post
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
only:
|
||||
refs:
|
||||
- /^r[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+/
|
||||
- /^master$/
|
||||
tags:
|
||||
- veloren-docker
|
||||
@ -147,13 +161,13 @@ linux:
|
||||
expire_in: 1 week
|
||||
|
||||
windows:
|
||||
stage: post
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
only:
|
||||
refs:
|
||||
- /^r[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+/
|
||||
- /^master$/
|
||||
tags:
|
||||
- veloren-docker
|
||||
@ -169,4 +183,52 @@ windows:
|
||||
- LICENSE
|
||||
expire_in: 1 week
|
||||
|
||||
macos:
|
||||
stage: build-post
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
only:
|
||||
refs:
|
||||
- /^r[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+/
|
||||
- /^master$/
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- PATH="/dockercache/osxcross/target/bin:$PATH" COREAUDIO_SDK_PATH=/dockercache/osxcross/target/SDK/MacOSX10.13.sdk CC=o64-clang CXX=o64-clang++ cargo build --target x86_64-apple-darwin --release
|
||||
- cp -r target/x86_64-apple-darwin/release/veloren-server-cli $CI_PROJECT_DIR
|
||||
- cp -r target/x86_64-apple-darwin/release/veloren-voxygen $CI_PROJECT_DIR
|
||||
artifacts:
|
||||
paths:
|
||||
- veloren-server-cli
|
||||
- veloren-voxygen
|
||||
- assets/
|
||||
- LICENSE
|
||||
expire_in: 1 week
|
||||
# --
|
||||
|
||||
# -- publish
|
||||
|
||||
docker:
|
||||
stage: publish
|
||||
when: delayed
|
||||
start_in: 5 seconds
|
||||
image:
|
||||
name: gcr.io/kaniko-project/executor:debug
|
||||
entrypoint: [""]
|
||||
dependencies:
|
||||
- linux
|
||||
before_script:
|
||||
- ls "$CI_PROJECT_DIR/server-cli/"
|
||||
only:
|
||||
refs:
|
||||
- /^r[0-9]+\.[0-9]+\.[0-9]+/
|
||||
- /^v[0-9]+\.[0-9]+/
|
||||
- /^master$/
|
||||
tags:
|
||||
- veloren-docker
|
||||
script:
|
||||
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
|
||||
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/server-cli/Dockerfile --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}-server"
|
||||
|
||||
# --
|
||||
|
28
.gitlab/CODEOWNERS
Normal file
28
.gitlab/CODEOWNERS
Normal file
@ -0,0 +1,28 @@
|
||||
# Defines people who should approve to certain parts of the codebase
|
||||
|
||||
/assets/ @assetsandvisualdesign @frontend
|
||||
/chat-cli/ @frontend
|
||||
/client/ @backend @networking
|
||||
/common/ @backend @networking
|
||||
/server/ @backend @networking
|
||||
/server-cli/ @frontend
|
||||
#/voxygen/ @someone
|
||||
/voxygen/anim/ @animation
|
||||
/voxygen/audio/ @audio
|
||||
#/voxygen/hud/ @someone
|
||||
#/voxygen/menu / @someone
|
||||
#/voxygen/mesh/ @someone
|
||||
/voxygen/render/ @rendering
|
||||
#/voxygen/scene/ @someone
|
||||
#/voxygen/ui/ @someone
|
||||
/world/ @worldgen
|
||||
|
||||
# All files related to documentation or game unrelated content needs to be approved by the meta group
|
||||
*.md @meta
|
||||
*.nix @meta
|
||||
.gitignore @meta
|
||||
.gitattributes @meta
|
||||
.gitlab-ci.yml @meta
|
||||
rust-toolchain @meta
|
||||
LICENSE @meta
|
||||
.cargo/ @meta
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -14,10 +14,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added rotating orientation marker to main-map
|
||||
- Added daily Mac builds
|
||||
- Allow spawning individual pet species, not just generic body kinds.
|
||||
- Configurable fonts
|
||||
- Tanslation status tracking
|
||||
- Added gamma setting
|
||||
- Added new orc hairstyles
|
||||
- Added sfx for wielding/unwielding weapons
|
||||
- Fixed NPCs attacking the player forever after killing them
|
||||
- Added sfx for collecting, dropping and using inventory items
|
||||
- New attack animation
|
||||
- weapon control system
|
||||
- Game pauses when in singleplayer and pause menu
|
||||
- Added authentication system (to play on the official server register on https://account.veloren.net)
|
||||
- Added gamepad/controller support
|
||||
- Added player feedback when attempting to pickup an item with a full inventory
|
||||
|
||||
### Changed
|
||||
|
||||
- Brighter / higher contrast main-map
|
||||
- Removed highlighting of non-collectible sprites
|
||||
- Fixed /give_exp ignoring player argument
|
||||
- Extend run sfx to small animals to prevent sneak attacks by geese.
|
||||
- Decreased clientside latency of ServerEvent mediated effects (e.g. projectiles, inventory operations, etc)
|
||||
|
||||
### Removed
|
||||
|
||||
|
1013
Cargo.lock
generated
1013
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,9 @@ Currently the communication of contributors happens mainly on our [official Disc
|
||||
|
||||
## Useful Links
|
||||
|
||||
[Sign Up](https://account.veloren.net) - Here you can create an online account for Veloren.
|
||||
This will be needed to play on auth-enabled servers, including the official server.
|
||||
|
||||
[The Book](https://book.veloren.net) - A collection of all important information relating to Veloren. It includes information on how to compile Veloren and how to contribute.
|
||||
|
||||
[Future Plans](https://gitlab.com/veloren/veloren/milestones) - Go here for information about Veloren's development roadmap and what we're currently working on.
|
||||
|
@ -19,6 +19,7 @@
|
||||
"Colborn",
|
||||
"Dagfinn",
|
||||
"Dagrod",
|
||||
"Digbod",
|
||||
"Dimian",
|
||||
"Domnhar",
|
||||
"Ebraheim",
|
||||
@ -458,6 +459,10 @@
|
||||
"peacock": {
|
||||
"keyword": "peacock",
|
||||
"generic": "Peacock"
|
||||
},
|
||||
"eagle": {
|
||||
"keyword": "eagle",
|
||||
"generic": "Eagle"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -23,5 +23,77 @@
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Wield(Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.weapon.sword_out",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Unwield(Sword): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.weapon.sword_in",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Collected): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Swapped): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Given): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_item",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Dropped): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.footsteps.stepgrass_4",
|
||||
],
|
||||
threshold: 0.5,
|
||||
),
|
||||
Inventory(Consumed(Potion)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.consumable.liquid",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(Consumed(PotionMinor)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.consumable.liquid",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(Consumed(Apple)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.consumable.apple",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(Consumed(Mushroom)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.consumable.food",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(Consumed(Cheese)): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.consumable.food",
|
||||
],
|
||||
threshold: 0.3,
|
||||
),
|
||||
Inventory(CollectFailed): (
|
||||
files: [
|
||||
"voxygen.audio.sfx.inventory.add_failed",
|
||||
],
|
||||
threshold: 0.3,
|
||||
)
|
||||
}
|
||||
)
|
BIN
assets/voxygen/audio/sfx/inventory/add_failed.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/add_failed.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/inventory/add_item.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/add_item.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/inventory/consumable/apple.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/consumable/apple.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/inventory/consumable/food.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/consumable/food.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/inventory/consumable/liquid.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/inventory/consumable/liquid.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/weapon/sword_in.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/weapon/sword_in.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/sfx/weapon/sword_out.wav
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/sfx/weapon/sword_out.wav
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/button.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/button.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/button_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/button_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/button_press.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/button_press.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/banner.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/banner.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/banner_small_top.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/banner_small_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/banner_top.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/banner_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/enemybar-0.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/enemybar-0.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/enemybar.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/enemybar.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/enemybar_bg.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/enemybar_bg.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/esc_menu.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/esc_menu.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/icons/elf_m.png
(Stored with Git LFS)
BIN
assets/voxygen/element/icons/elf_m.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/misc_bg/textbox_bot.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/misc_bg/textbox_bot.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/misc_bg/textbox_mid.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/misc_bg/textbox_mid.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/misc_bg/textbox_top.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/misc_bg/textbox_top.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/font/haxrcorp_4089_cyrillic_altgr_extended.ttf
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/font/haxrcorp_4089_cyrillic_altgr_extended.ttf
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -17,6 +17,28 @@ VoxygenLocalization(
|
||||
language_identifier: "en",
|
||||
),
|
||||
convert_utf8_to_ascii: false,
|
||||
fonts: {
|
||||
"opensans": Font (
|
||||
asset_key: "voxygen.font.OpenSans-Regular",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"metamorph": Font (
|
||||
asset_key: "voxygen.font.Metamorphous-Regular",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"alkhemi": Font (
|
||||
asset_key: "voxygen.font.Alkhemikal",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"wizard": Font (
|
||||
asset_key: "voxygen.font.wizard",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"cyri": Font (
|
||||
asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
},
|
||||
string_map: {
|
||||
/// Start Common section
|
||||
// Texts used in multiple locations with the same formatting
|
||||
@ -43,6 +65,8 @@ VoxygenLocalization(
|
||||
"common.disclaimer": "Disclaimer",
|
||||
"common.cancel": "Cancel",
|
||||
"common.none": "None",
|
||||
"common.error": "Error",
|
||||
"common.fatal_error": "Fatal Error",
|
||||
|
||||
// Message when connection to the server is lost
|
||||
"common.connection_lost": r#"Connection lost!
|
||||
@ -91,12 +115,25 @@ Thanks for taking the time to read this notice, we hope you enjoy the game!
|
||||
// Login process description
|
||||
"main.login_process": r#"Information on the Login Process:
|
||||
|
||||
Put in any username. No Account needed yet.
|
||||
If you are having issues signing in:
|
||||
|
||||
Character names and appearances will be saved locally.
|
||||
Please note that you now need an account
|
||||
to play on auth-enabled servers.
|
||||
|
||||
Levels/Items are not saved yet."#,
|
||||
You can create an account over at
|
||||
|
||||
https://account.veloren.net."#,
|
||||
"main.login.server_not_found": "Server not found",
|
||||
"main.login.authentication_error": "Auth error on server",
|
||||
"main.login.server_full": "Server is full",
|
||||
"main.login.untrusted_auth_server": "Auth server not trusted",
|
||||
"main.login.outdated_client_or_server": "ServerWentMad: Probably versions are incompatible, check for updates.",
|
||||
"main.login.timeout": "Timeout: Server did not respond in time. (Overloaded or network issues).",
|
||||
"main.login.server_shut_down": "Server shut down",
|
||||
"main.login.already_logged_in": "You are already logged into the server.",
|
||||
"main.login.network_error": "Network error",
|
||||
"main.login.failed_sending_request": "Request to Auth server failed",
|
||||
"main.login.client_crashed": "Client crashed",
|
||||
|
||||
/// End Main screen section
|
||||
|
||||
@ -186,6 +223,7 @@ Enjoy your stay in the World of Veloren."#,
|
||||
"hud.settings.view_distance": "View Distance",
|
||||
"hud.settings.maximum_fps": "Maximum FPS",
|
||||
"hud.settings.fov": "Field of View (deg)",
|
||||
"hud.settings.gamma": "Gamma",
|
||||
"hud.settings.antialiasing_mode": "AntiAliasing Mode",
|
||||
"hud.settings.cloud_rendering_mode": "Cloud Rendering Mode",
|
||||
"hud.settings.fluid_rendering_mode": "Fluid Rendering Mode",
|
||||
|
@ -4,7 +4,29 @@ VoxygenLocalization(
|
||||
language_name: "Français",
|
||||
language_identifier: "fr_FR",
|
||||
),
|
||||
convert_utf8_to_ascii: true,
|
||||
convert_utf8_to_ascii: false,
|
||||
fonts: {
|
||||
"opensans": Font (
|
||||
asset_key: "voxygen.font.OpenSans-Regular",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"metamorph": Font (
|
||||
asset_key: "voxygen.font.Metamorphous-Regular",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"alkhemi": Font (
|
||||
asset_key: "voxygen.font.Alkhemikal",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"wizard": Font (
|
||||
asset_key: "voxygen.font.wizard",
|
||||
scale_ratio: 1.0,
|
||||
),
|
||||
"cyri": Font (
|
||||
asset_key: "voxygen.font.haxrcorp_4089_cyrillic_altgr_extended",
|
||||
scale_ratio: 0.9,
|
||||
),
|
||||
},
|
||||
string_map: {
|
||||
// Common texts used in multiple locations
|
||||
"common.username": "pseudo",
|
||||
@ -16,7 +38,7 @@ VoxygenLocalization(
|
||||
"common.languages": "Langues",
|
||||
"common.interface": "Interface",
|
||||
"common.gameplay": "Gameplay",
|
||||
"common.controls": "Controles",
|
||||
"common.controls": "Contrôles",
|
||||
"common.video": "Video",
|
||||
"common.sound": "Audio",
|
||||
"common.resume": "Reprendre",
|
||||
|
61
assets/voxygen/net.veloren.veloren.appdata.xml
Normal file
61
assets/voxygen/net.veloren.veloren.appdata.xml
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2019 Artem Polishchuk <ego.cordatus@gmail.com> -->
|
||||
<component type="desktop-application">
|
||||
<id>net.veloren.veloren.desktop</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0-or-later</project_license>
|
||||
<content_rating type="oars-1.0">
|
||||
<content_attribute id="social-chat">intense</content_attribute>
|
||||
<content_attribute id="social-info">mild</content_attribute>
|
||||
<content_attribute id="violence-cartoon">mild</content_attribute>
|
||||
<content_attribute id="violence-fantasy">mild</content_attribute>
|
||||
</content_rating>
|
||||
<name>Veloren</name>
|
||||
<summary>
|
||||
Veloren is a multiplayer voxel RPG written in Rust. It is inspired
|
||||
by games such as Cube World, Legend of Zelda: Breath of the Wild,
|
||||
Dwarf Fortress and Minecraft.
|
||||
</summary>
|
||||
<description>
|
||||
<p>
|
||||
Welcome To Veloren!
|
||||
</p><p>
|
||||
Veloren is a multiplayer voxel RPG written in Rust. Veloren takes
|
||||
inspiration from games such as Cube World, Minecraft and Dwarf
|
||||
Fortress. The game is currently under heavy development, but is
|
||||
playable.
|
||||
</p><p>
|
||||
Development
|
||||
</p><p>
|
||||
Currently the communication of contributors happens mainly on our
|
||||
official Discord server (https://discord.gg/kjwJwjK). You can join
|
||||
it to keep up with the development, talk to us or contribute
|
||||
something yourself. Anyone who shows genuine effort to help is
|
||||
welcome in our team. You don't have to know how to program to
|
||||
contribute!
|
||||
</p>
|
||||
</description>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://media.discordapp.net/attachments/634860358623821835/643034796548947968/screenshot_1573381825305.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://media.discordapp.net/attachments/597826574095613962/643102462781423616/screenshot_1573397958545.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://cdn.discordapp.com/attachments/634860358623821835/646518917577310219/screenshot_1574211401431.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<keywords>
|
||||
<keyword>sandbox</keyword>
|
||||
<keyword>world</keyword>
|
||||
<keyword>multiplayer</keyword>
|
||||
</keywords>
|
||||
<url type="homepage">https://veloren.net</url>
|
||||
<url type="bugtracker">https://gitlab.com/veloren/veloren/issues</url>
|
||||
<url type="faq">https://gitlab.com/veloren/veloren#faq</url>
|
||||
<url type="help">https://book.veloren.net/</url>
|
||||
<provides>
|
||||
<binary>veloren-voxygen</binary>
|
||||
</provides>
|
||||
</component>
|
9
assets/voxygen/net.veloren.veloren.desktop
Normal file
9
assets/voxygen/net.veloren.veloren.desktop
Normal file
@ -0,0 +1,9 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Veloren
|
||||
Comment=Veloren is a multiplayer voxel RPG written in Rust
|
||||
Exec=veloren-voxygen
|
||||
Categories=Game;Simulation;
|
||||
Keywords=veloren;sandbox;world;blocks;nodes;multiplayer;roleplaying;
|
||||
Icon=net.veloren.veloren.png
|
||||
Terminal=false
|
BIN
assets/voxygen/net.veloren.veloren.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/net.veloren.veloren.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -12,4 +12,5 @@ uniform u_globals {
|
||||
uvec4 light_shadow_count;
|
||||
uvec4 medium;
|
||||
ivec4 select_pos;
|
||||
vec4 gamma;
|
||||
};
|
||||
|
@ -45,7 +45,7 @@ void main() {
|
||||
//hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0);
|
||||
//vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a);
|
||||
|
||||
vec4 final_color = aa_color;
|
||||
vec4 final_color = pow(aa_color, gamma);
|
||||
|
||||
if (medium.x == 1u) {
|
||||
final_color *= vec4(0.2, 0.2, 0.8, 1.0);
|
||||
|
@ -111,4 +111,32 @@
|
||||
center: ("npc.peacock.female.tail"),
|
||||
)
|
||||
),
|
||||
(Eagle, Male): (
|
||||
head: (
|
||||
offset: (-2.0, -2.0, -3.5),
|
||||
center: ("npc.eagle.female.head"),
|
||||
),
|
||||
torso: (
|
||||
offset: (-3.0, -4.5, -4.5),
|
||||
center: ("npc.eagle.female.torso"),
|
||||
),
|
||||
tail: (
|
||||
offset: (-2.0, -3.5, -3.5),
|
||||
center: ("npc.eagle.female.tail"),
|
||||
)
|
||||
),
|
||||
(Eagle, Female): (
|
||||
head: (
|
||||
offset: (-2.0, -2.0, -3.5),
|
||||
center: ("npc.eagle.female.head"),
|
||||
),
|
||||
torso: (
|
||||
offset: (-3.0, -4.5, -4.5),
|
||||
center: ("npc.eagle.female.torso"),
|
||||
),
|
||||
tail: (
|
||||
offset: (-2.0, -3.5, -3.5),
|
||||
center: ("npc.eagle.female.tail"),
|
||||
)
|
||||
),
|
||||
})
|
@ -143,4 +143,40 @@
|
||||
lateral: ("npc.peacock.female.leg_r"),
|
||||
)
|
||||
),
|
||||
(Eagle, Male): (
|
||||
wing_l: (
|
||||
offset: (-1.0, -3.5, -13.0),
|
||||
lateral: ("npc.eagle.male.wing_l"),
|
||||
),
|
||||
wing_r: (
|
||||
offset: (-1.0, -3.5, -13.0),
|
||||
lateral: ("npc.eagle.male.wing_r"),
|
||||
),
|
||||
foot_l: (
|
||||
offset: (-1.5, 0.0, -8.0),
|
||||
lateral: ("npc.eagle.male.leg_l"),
|
||||
),
|
||||
foot_r: (
|
||||
offset: (-1.5, 0.0, -8.0),
|
||||
lateral: ("npc.eagle.male.leg_r"),
|
||||
)
|
||||
),
|
||||
(Eagle, Female): (
|
||||
wing_l: (
|
||||
offset: (-1.0, -3.5, -13.0),
|
||||
lateral: ("npc.eagle.female.wing_l"),
|
||||
),
|
||||
wing_r: (
|
||||
offset: (-1.0, -3.5, -13.0),
|
||||
lateral: ("npc.eagle.female.wing_r"),
|
||||
),
|
||||
foot_l: (
|
||||
offset: (-1.5, 0.0, -8.0),
|
||||
lateral: ("npc.eagle.female.leg_l"),
|
||||
),
|
||||
foot_r: (
|
||||
offset: (-1.5, 0.0, -8.0),
|
||||
lateral: ("npc.eagle.female.leg_r"),
|
||||
)
|
||||
),
|
||||
})
|
BIN
assets/voxygen/voxel/figure/accessory/orc/warpaint-female-0.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/figure/accessory/orc/warpaint-female-0.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/figure/accessory/orc/warpaint-female-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/accessory/orc/warpaint-female-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/beard/human/human-0.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/figure/beard/human/human-0.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/elf/male-2.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/figure/hair/elf/male-2.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/human/male-20.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/figure/hair/human/male-20.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-0.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-0.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-1.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-1.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-2.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-2.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-3.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-3.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-4.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-4.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-5.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-5.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female-6.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/figure/hair/orc/female-6.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/figure/hair/orc/female.vox
(Stored with Git LFS)
BIN
assets/voxygen/voxel/figure/hair/orc/female.vox
(Stored with Git LFS)
Binary file not shown.
@ -27,6 +27,7 @@
|
||||
Some(("figure.hair.human.male-20", (-3, -4, -7))),
|
||||
],
|
||||
beard: [
|
||||
None,
|
||||
Some(("figure.beard.human.human-0", (4, 6, -2))),
|
||||
Some(("figure.beard.human.human-1", (5, 10, -2))),
|
||||
Some(("figure.beard.human.human-2", (3, 7, -3))),
|
||||
@ -92,17 +93,24 @@
|
||||
],
|
||||
),
|
||||
(Orc, Female): (
|
||||
offset: (-8.0, -3.0, -6.0),
|
||||
head: ("figure.head.orc.female", (0, 2, 0)),
|
||||
eyes: ("figure.eyes.orc.female-0", (3, 9, 2)),
|
||||
offset: (-8.0, -2.5, -6.0),
|
||||
head: ("figure.head.orc.female", (0, 1, 0)),
|
||||
eyes: ("figure.eyes.orc.female-0", (3, 8, 2)),
|
||||
hair: [
|
||||
Some(("figure.hair.orc.female", (5, -2, 0))),
|
||||
Some(("figure.hair.orc.female-0", (-2, -8, 0))),
|
||||
Some(("figure.hair.orc.female-1", (-2, -8, 0))),
|
||||
Some(("figure.hair.orc.female-2", (-2, -8, 0))),
|
||||
Some(("figure.hair.orc.female-3", (-2, -8, -4))),
|
||||
Some(("figure.hair.orc.female-4", (-2, -8, 0))),
|
||||
Some(("figure.hair.orc.female-5", (-2, -8, -4))),
|
||||
Some(("figure.hair.orc.female-6", (-2, -8, -4))),
|
||||
],
|
||||
beard: [None],
|
||||
accessory: [
|
||||
None,
|
||||
Some(("figure.accessory.orc.earring-female-0", (2, 5, 1))),
|
||||
Some(("figure.accessory.orc.warpaint-female-0", (3, 5, 1))),
|
||||
Some(("figure.accessory.orc.earring-female-0", (2, 4, 1))),
|
||||
Some(("figure.accessory.orc.warpaint-female-0", (-2, -4, -7))),
|
||||
Some(("figure.accessory.orc.warpaint-female-1", (-2, -4, -7))),
|
||||
],
|
||||
),
|
||||
(Elf, Male): (
|
||||
@ -239,6 +247,7 @@
|
||||
Some(("figure.hair.danari.male-1", (3, 1, 2))),
|
||||
],
|
||||
beard: [
|
||||
None,
|
||||
Some(("figure.beard.danari.danari-0", (4, 6, -1))),
|
||||
],
|
||||
accessory: [
|
||||
|
BIN
assets/voxygen/voxel/npc/eagle/female/head.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/head.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/leg_l.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/leg_l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/leg_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/leg_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/tail.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/tail.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/torso.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/torso.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/wing_l.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/wing_l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/female/wing_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/female/wing_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/head.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/head.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/leg_l.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/leg_l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/leg_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/leg_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/tail.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/tail.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/torso.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/torso.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/wing_l.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/wing_l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/npc/eagle/male/wing_r.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/npc/eagle/male/wing_r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -386,7 +386,7 @@
|
||||
central: ("npc.lion.male.ears"),
|
||||
),
|
||||
tail: (
|
||||
offset: (-0.5, -1.0, -8.0),
|
||||
offset: (-0.5, -1.0, -1.0),
|
||||
central: ("npc.lion.male.tail"),
|
||||
),
|
||||
),
|
||||
@ -417,7 +417,7 @@
|
||||
),
|
||||
tail: (
|
||||
offset: (-0.5, -1.0, -1.0),
|
||||
central: ("npc.lion.male.tail"),
|
||||
central: ("npc.lion.female.tail"),
|
||||
),
|
||||
),
|
||||
(Tarasque, Male): (
|
||||
|
BIN
assets/world/structure/dungeon/meso_sewer_temple.vox
(Stored with Git LFS)
BIN
assets/world/structure/dungeon/meso_sewer_temple.vox
(Stored with Git LFS)
Binary file not shown.
@ -51,7 +51,9 @@ fn main() {
|
||||
println!("Players online: {:?}", client.get_players());
|
||||
|
||||
client
|
||||
.register(comp::Player::new(username, None), password)
|
||||
.register(username, password, |provider| {
|
||||
provider == "https://auth.veloren.net"
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
@ -15,3 +15,4 @@ log = "0.4.8"
|
||||
specs = "0.15.1"
|
||||
vek = { version = "0.9.9", features = ["serde"] }
|
||||
hashbrown = { version = "0.6.2", features = ["rayon", "serde", "nightly"] }
|
||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "65571ade0d954a0e0bd995fdb314854ff146ab97" }
|
||||
|
@ -1,3 +1,4 @@
|
||||
use authc::AuthClientError;
|
||||
use common::net::PostError;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -7,11 +8,18 @@ pub enum Error {
|
||||
ServerTimeout,
|
||||
ServerShutdown,
|
||||
TooManyPlayers,
|
||||
InvalidAuth,
|
||||
AlreadyLoggedIn,
|
||||
AuthErr(String),
|
||||
AuthClientError(AuthClientError),
|
||||
AuthServerNotTrusted,
|
||||
//TODO: InvalidAlias,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl From<PostError> for Error {
|
||||
fn from(err: PostError) -> Self { Error::Network(err) }
|
||||
fn from(err: PostError) -> Self { Self::Network(err) }
|
||||
}
|
||||
|
||||
impl From<AuthClientError> for Error {
|
||||
fn from(err: AuthClientError) -> Self { Self::AuthClientError(err) }
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ pub mod error;
|
||||
|
||||
// Reexports
|
||||
pub use crate::error::Error;
|
||||
pub use authc::AuthClientError;
|
||||
pub use specs::{
|
||||
join::Join,
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
@ -13,10 +14,13 @@ pub use specs::{
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use common::{
|
||||
comp::{self, ControlEvent, Controller, ControllerInputs, InventoryManip},
|
||||
comp::{
|
||||
self, ControlEvent, Controller, ControllerInputs, InventoryManip, InventoryUpdateEvent,
|
||||
},
|
||||
event::{EventBus, SfxEvent, SfxEventItem},
|
||||
msg::{
|
||||
validate_chat_msg, ChatMsgValidationError, ClientMsg, ClientState, PlayerListUpdate,
|
||||
RequestStateError, ServerError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG,
|
||||
RegisterError, RequestStateError, ServerInfo, ServerMsg, MAX_BYTES_CHAT_MSG,
|
||||
},
|
||||
net::PostBox,
|
||||
state::State,
|
||||
@ -39,11 +43,11 @@ use vek::*;
|
||||
// The duration of network inactivity until the player is kicked
|
||||
// @TODO: in the future, this should be configurable on the server
|
||||
// and be provided to the client
|
||||
const SERVER_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
const SERVER_TIMEOUT: f64 = 20.0;
|
||||
|
||||
// After this duration has elapsed, the user will begin getting kick warnings in
|
||||
// their chat window
|
||||
const SERVER_TIMEOUT_GRACE_PERIOD: Duration = Duration::from_secs(14);
|
||||
const SERVER_TIMEOUT_GRACE_PERIOD: f64 = 14.0;
|
||||
|
||||
pub enum Event {
|
||||
Chat {
|
||||
@ -63,8 +67,8 @@ pub struct Client {
|
||||
|
||||
postbox: PostBox<ClientMsg, ServerMsg>,
|
||||
|
||||
last_server_ping: Instant,
|
||||
last_server_pong: Instant,
|
||||
last_server_ping: f64,
|
||||
last_server_pong: f64,
|
||||
last_ping_delta: f64,
|
||||
|
||||
tick: u64,
|
||||
@ -85,13 +89,13 @@ impl Client {
|
||||
let mut postbox = PostBox::to(addr)?;
|
||||
|
||||
// Wait for initial sync
|
||||
let (state, entity, server_info, world_map) = match postbox.next_message() {
|
||||
Some(ServerMsg::InitialSync {
|
||||
let (state, entity, server_info, world_map) = match postbox.next_message()? {
|
||||
ServerMsg::InitialSync {
|
||||
entity_package,
|
||||
server_info,
|
||||
time_of_day,
|
||||
world_map: (map_size, world_map),
|
||||
}) => {
|
||||
} => {
|
||||
// TODO: Display that versions don't match in Voxygen
|
||||
if server_info.git_hash != common::util::GIT_HASH.to_string() {
|
||||
log::warn!(
|
||||
@ -104,6 +108,8 @@ impl Client {
|
||||
);
|
||||
}
|
||||
|
||||
log::debug!("Auth Server: {:?}", server_info.auth_provider);
|
||||
|
||||
// Initialize `State`
|
||||
let mut state = State::default();
|
||||
let entity = state.ecs_mut().apply_entity_package(entity_package);
|
||||
@ -128,9 +134,7 @@ impl Client {
|
||||
|
||||
(state, entity, server_info, (world_map, map_size))
|
||||
},
|
||||
Some(ServerMsg::Error(ServerError::TooManyPlayers)) => {
|
||||
return Err(Error::TooManyPlayers);
|
||||
},
|
||||
ServerMsg::TooManyPlayers => return Err(Error::TooManyPlayers),
|
||||
_ => return Err(Error::ServerWentMad),
|
||||
};
|
||||
|
||||
@ -151,8 +155,8 @@ impl Client {
|
||||
|
||||
postbox,
|
||||
|
||||
last_server_ping: Instant::now(),
|
||||
last_server_pong: Instant::now(),
|
||||
last_server_ping: 0.0,
|
||||
last_server_pong: 0.0,
|
||||
last_ping_delta: 0.0,
|
||||
|
||||
tick: 0,
|
||||
@ -171,16 +175,40 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Request a state transition to `ClientState::Registered`.
|
||||
pub fn register(&mut self, player: comp::Player, password: String) -> Result<(), Error> {
|
||||
self.postbox
|
||||
.send_message(ClientMsg::Register { player, password });
|
||||
pub fn register(
|
||||
&mut self,
|
||||
username: String,
|
||||
password: String,
|
||||
mut auth_trusted: impl FnMut(&str) -> bool,
|
||||
) -> Result<(), Error> {
|
||||
// Authentication
|
||||
let token_or_username = self.server_info.auth_provider.as_ref().map(|addr|
|
||||
// Query whether this is a trusted auth server
|
||||
if auth_trusted(&addr) {
|
||||
Ok(authc::AuthClient::new(addr)
|
||||
.sign_in(&username, &password)?
|
||||
.serialize())
|
||||
} else {
|
||||
Err(Error::AuthServerNotTrusted)
|
||||
}
|
||||
).unwrap_or(Ok(username))?;
|
||||
|
||||
self.postbox.send_message(ClientMsg::Register {
|
||||
view_distance: self.view_distance,
|
||||
token_or_username,
|
||||
});
|
||||
self.client_state = ClientState::Pending;
|
||||
|
||||
loop {
|
||||
match self.postbox.next_message() {
|
||||
Some(ServerMsg::StateAnswer(Err((RequestStateError::Denied, _)))) => {
|
||||
break Err(Error::InvalidAuth);
|
||||
match self.postbox.next_message()? {
|
||||
ServerMsg::StateAnswer(Err((RequestStateError::RegisterDenied(err), state))) => {
|
||||
self.client_state = state;
|
||||
break Err(match err {
|
||||
RegisterError::AlreadyLoggedIn => Error::AlreadyLoggedIn,
|
||||
RegisterError::AuthError(err) => Error::AuthErr(err),
|
||||
});
|
||||
},
|
||||
Some(ServerMsg::StateAnswer(Ok(ClientState::Registered))) => break Ok(()),
|
||||
ServerMsg::StateAnswer(Ok(ClientState::Registered)) => break Ok(()),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
@ -194,10 +222,7 @@ impl Client {
|
||||
}
|
||||
|
||||
/// Send disconnect message to the server
|
||||
pub fn request_logout(&mut self) {
|
||||
self.postbox.send_message(ClientMsg::Disconnect);
|
||||
self.client_state = ClientState::Pending;
|
||||
}
|
||||
pub fn request_logout(&mut self) { self.postbox.send_message(ClientMsg::Disconnect); }
|
||||
|
||||
/// Request a state transition to `ClientState::Registered` from an ingame
|
||||
/// state.
|
||||
@ -377,13 +402,14 @@ impl Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle new messages from the server.
|
||||
frontend_events.append(&mut self.handle_new_messages()?);
|
||||
|
||||
// 3) Update client local data
|
||||
|
||||
// 4) Tick the client's LocalState
|
||||
self.state.tick(dt, add_foreign_systems);
|
||||
self.state.tick(dt, add_foreign_systems, true);
|
||||
|
||||
// 5) Terrain
|
||||
let pos = self
|
||||
@ -479,9 +505,9 @@ impl Client {
|
||||
}
|
||||
|
||||
// Send a ping to the server once every second
|
||||
if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) {
|
||||
if self.state.get_time() - self.last_server_ping > 1. {
|
||||
self.postbox.send_message(ClientMsg::Ping);
|
||||
self.last_server_ping = Instant::now();
|
||||
self.last_server_ping = self.state.get_time();
|
||||
}
|
||||
|
||||
// 6) Update the server about the player's physics attributes.
|
||||
@ -526,16 +552,14 @@ impl Client {
|
||||
// Check that we have an valid connection.
|
||||
// Use the last ping time as a 1s rate limiter, we only notify the user once per
|
||||
// second
|
||||
if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) {
|
||||
let duration_since_last_pong = Instant::now().duration_since(self.last_server_pong);
|
||||
if self.state.get_time() - self.last_server_ping > 1. {
|
||||
let duration_since_last_pong = self.state.get_time() - self.last_server_pong;
|
||||
|
||||
// Dispatch a notification to the HUD warning they will be kicked in {n} seconds
|
||||
if duration_since_last_pong.as_secs() >= SERVER_TIMEOUT_GRACE_PERIOD.as_secs() {
|
||||
if let Some(seconds_until_kick) =
|
||||
SERVER_TIMEOUT.checked_sub(duration_since_last_pong)
|
||||
{
|
||||
if duration_since_last_pong >= SERVER_TIMEOUT_GRACE_PERIOD {
|
||||
if self.state.get_time() - duration_since_last_pong > 0. {
|
||||
frontend_events.push(Event::DisconnectionNotification(
|
||||
seconds_until_kick.as_secs(),
|
||||
(self.state.get_time() - duration_since_last_pong).round() as u64,
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -546,10 +570,8 @@ impl Client {
|
||||
if new_msgs.len() > 0 {
|
||||
for msg in new_msgs {
|
||||
match msg {
|
||||
ServerMsg::Error(e) => match e {
|
||||
ServerError::TooManyPlayers => return Err(Error::ServerWentMad),
|
||||
ServerError::InvalidAuth => return Err(Error::InvalidAuth),
|
||||
//TODO: ServerError::InvalidAlias => return Err(Error::InvalidAlias),
|
||||
ServerMsg::TooManyPlayers => {
|
||||
return Err(Error::ServerWentMad);
|
||||
},
|
||||
ServerMsg::Shutdown => return Err(Error::ServerShutdown),
|
||||
ServerMsg::InitialSync { .. } => return Err(Error::ServerWentMad),
|
||||
@ -589,11 +611,10 @@ impl Client {
|
||||
|
||||
ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong),
|
||||
ServerMsg::Pong => {
|
||||
self.last_server_pong = Instant::now();
|
||||
self.last_server_pong = self.state.get_time();
|
||||
|
||||
self.last_ping_delta = Instant::now()
|
||||
.duration_since(self.last_server_ping)
|
||||
.as_secs_f64();
|
||||
self.last_ping_delta =
|
||||
(self.state.get_time() - self.last_server_ping).round();
|
||||
},
|
||||
ServerMsg::ChatMsg { message, chat_type } => {
|
||||
frontend_events.push(Event::Chat { message, chat_type })
|
||||
@ -669,8 +690,26 @@ impl Client {
|
||||
self.state.write_component(entity, character_state);
|
||||
}
|
||||
},
|
||||
ServerMsg::InventoryUpdate(inventory) => {
|
||||
self.state.write_component(self.entity, inventory)
|
||||
ServerMsg::InventoryUpdate(inventory, event) => {
|
||||
match event {
|
||||
InventoryUpdateEvent::CollectFailed => {
|
||||
frontend_events.push(Event::Chat {
|
||||
message: String::from(
|
||||
"Failed to collect item. Your inventory may be full!",
|
||||
),
|
||||
chat_type: ChatType::Meta,
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
self.state.write_component(self.entity, inventory);
|
||||
},
|
||||
}
|
||||
|
||||
self.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<SfxEventItem>>()
|
||||
.emitter()
|
||||
.emit(SfxEventItem::at_player_position(SfxEvent::Inventory(event)));
|
||||
},
|
||||
ServerMsg::TerrainChunkUpdate { key, chunk } => {
|
||||
if let Ok(chunk) = chunk {
|
||||
@ -687,10 +726,6 @@ impl Client {
|
||||
self.client_state = state;
|
||||
},
|
||||
ServerMsg::StateAnswer(Err((error, state))) => {
|
||||
if error == RequestStateError::Denied {
|
||||
warn!("Connection denied!");
|
||||
return Err(Error::InvalidAuth);
|
||||
}
|
||||
warn!(
|
||||
"StateAnswer: {:?}. Server thinks client is in state {:?}.",
|
||||
error, state
|
||||
@ -698,13 +733,14 @@ impl Client {
|
||||
},
|
||||
ServerMsg::Disconnect => {
|
||||
frontend_events.push(Event::Disconnect);
|
||||
self.postbox.send_message(ClientMsg::Terminate);
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if let Some(err) = self.postbox.error() {
|
||||
return Err(err.into());
|
||||
// We regularily ping in the tick method
|
||||
} else if Instant::now().duration_since(self.last_server_pong) > SERVER_TIMEOUT {
|
||||
} else if self.state.get_time() - self.last_server_pong > SERVER_TIMEOUT {
|
||||
return Err(Error::ServerTimeout);
|
||||
}
|
||||
Ok(frontend_events)
|
||||
|
@ -30,9 +30,10 @@ hashbrown = { version = "0.6.2", features = ["rayon", "serde", "nightly"] }
|
||||
find_folder = "0.3.0"
|
||||
parking_lot = "0.9.0"
|
||||
crossbeam = "=0.7.2"
|
||||
notify = "5.0.0-pre.1"
|
||||
notify = "5.0.0-pre.2"
|
||||
indexmap = "1.3.0"
|
||||
sum_type = "0.2.0"
|
||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "65571ade0d954a0e0bd995fdb314854ff146ab97" }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
|
@ -31,6 +31,7 @@ pub enum Species {
|
||||
Chicken = 1,
|
||||
Goose = 2,
|
||||
Peacock = 3,
|
||||
Eagle = 4,
|
||||
}
|
||||
|
||||
/// Data representing per-species generic data.
|
||||
@ -42,6 +43,7 @@ pub struct AllSpecies<SpeciesMeta> {
|
||||
pub chicken: SpeciesMeta,
|
||||
pub goose: SpeciesMeta,
|
||||
pub peacock: SpeciesMeta,
|
||||
pub eagle: SpeciesMeta,
|
||||
}
|
||||
|
||||
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
|
||||
@ -54,15 +56,17 @@ impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta>
|
||||
Species::Chicken => &self.chicken,
|
||||
Species::Goose => &self.goose,
|
||||
Species::Peacock => &self.peacock,
|
||||
Species::Eagle => &self.eagle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const ALL_SPECIES: [Species; 4] = [
|
||||
pub const ALL_SPECIES: [Species; 5] = [
|
||||
Species::Duck,
|
||||
Species::Chicken,
|
||||
Species::Goose,
|
||||
Species::Peacock,
|
||||
Species::Eagle,
|
||||
];
|
||||
|
||||
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
|
||||
|
@ -131,10 +131,10 @@ pub const DANARI_HAIR_COLORS: [(u8, u8, u8); 11] = [
|
||||
//(228, 208, 147), // Gold Blonde
|
||||
//(228, 223, 141), // Platinum Blonde
|
||||
(199, 131, 58), // Summer Blonde
|
||||
(107, 76, 51), // Oak Brown
|
||||
//(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(86, 72, 71), // Ash Brown
|
||||
(107, 76, 51), // Oak Skin4
|
||||
//(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -148,10 +148,10 @@ pub const DWARF_HAIR_COLORS: [(u8, u8, u8); 20] = [
|
||||
(228, 208, 147), // Gold Blonde
|
||||
(228, 223, 141), // Platinum Blonde
|
||||
(199, 131, 58), // Summer Blonde
|
||||
(107, 76, 51), // Oak Brown
|
||||
(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(86, 72, 71), // Ash Brown
|
||||
(107, 76, 51), // Oak Skin4
|
||||
(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -172,10 +172,10 @@ pub const ELF_HAIR_COLORS: [(u8, u8, u8); 23] = [
|
||||
(228, 208, 147), // Gold Blonde
|
||||
(228, 223, 141), // Platinum Blonde
|
||||
(199, 131, 58), // Summer Blonde
|
||||
(107, 76, 51), // Oak Brown
|
||||
(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(86, 72, 71), // Ash Brown
|
||||
(107, 76, 51), // Oak Skin4
|
||||
(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -195,10 +195,10 @@ pub const HUMAN_HAIR_COLORS: [(u8, u8, u8); 21] = [
|
||||
(228, 208, 147), // Gold Blonde
|
||||
(228, 223, 141), // Platinum Blonde
|
||||
(199, 131, 58), // Summer Blonde
|
||||
(107, 76, 51), // Oak Brown
|
||||
(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(86, 72, 71), // Ash Brown
|
||||
(107, 76, 51), // Oak Skin4
|
||||
(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -215,11 +215,11 @@ pub const HUMAN_HAIR_COLORS: [(u8, u8, u8); 21] = [
|
||||
];
|
||||
pub const ORC_HAIR_COLORS: [(u8, u8, u8); 10] = [
|
||||
(66, 66, 59), // Wise Grey
|
||||
//(107, 76, 51), // Oak Brown
|
||||
//(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(54, 30, 26), // Dark Chocolate
|
||||
(86, 72, 71), // Ash Brown
|
||||
//(107, 76, 51), // Oak Skin4
|
||||
//(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(54, 30, 26), // Dark Skin7
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -232,10 +232,10 @@ pub const UNDEAD_HAIR_COLORS: [(u8, u8, u8); 21] = [
|
||||
(228, 208, 147), // Gold Blonde
|
||||
//(228, 223, 141), // Platinum Blonde
|
||||
(199, 131, 58), // Summer Blonde
|
||||
(107, 76, 51), // Oak Brown
|
||||
(203, 154, 98), // Light Brown
|
||||
(64, 32, 18), // Chocolate Brown
|
||||
(86, 72, 71), // Ash Brown
|
||||
(107, 76, 51), // Oak Skin4
|
||||
(203, 154, 98), // Light Skin4
|
||||
(64, 32, 18), // Skin7 Skin4
|
||||
(86, 72, 71), // Ash Skin4
|
||||
(57, 56, 61), // Raven Black
|
||||
(101, 83, 95), // Matte Purple
|
||||
(101, 57, 90), // Witch Purple
|
||||
@ -261,30 +261,59 @@ pub const DANARI_SKIN_COLORS: [Skin; 4] = [
|
||||
Skin::DanariThree,
|
||||
Skin::DanariFour,
|
||||
];
|
||||
pub const DWARF_SKIN_COLORS: [Skin; 5] = [
|
||||
Skin::Pale,
|
||||
Skin::White,
|
||||
Skin::Tanned,
|
||||
pub const DWARF_SKIN_COLORS: [Skin; 14] = [
|
||||
Skin::Skin1,
|
||||
Skin::Skin2,
|
||||
Skin::Skin3,
|
||||
Skin::Skin4,
|
||||
Skin::Skin5,
|
||||
Skin::Skin6,
|
||||
Skin::Skin7,
|
||||
Skin::Skin8,
|
||||
Skin::Skin9,
|
||||
Skin::Skin10,
|
||||
Skin::Skin11,
|
||||
Skin::Skin12,
|
||||
Skin::Iron,
|
||||
Skin::Steel,
|
||||
];
|
||||
pub const ELF_SKIN_COLORS: [Skin; 7] = [
|
||||
Skin::Pale,
|
||||
pub const ELF_SKIN_COLORS: [Skin; 14] = [
|
||||
Skin::Skin1,
|
||||
Skin::Skin2,
|
||||
Skin::Skin3,
|
||||
Skin::Skin5,
|
||||
Skin::Skin6,
|
||||
Skin::Skin7,
|
||||
Skin::Skin8,
|
||||
Skin::Skin9,
|
||||
Skin::Skin10,
|
||||
Skin::Skin11,
|
||||
Skin::Skin12,
|
||||
Skin::ElfOne,
|
||||
Skin::ElfTwo,
|
||||
Skin::ElfThree,
|
||||
Skin::White,
|
||||
Skin::Tanned,
|
||||
Skin::TannedBrown,
|
||||
];
|
||||
pub const HUMAN_SKIN_COLORS: [Skin; 5] = [
|
||||
Skin::Pale,
|
||||
Skin::White,
|
||||
Skin::Tanned,
|
||||
Skin::TannedBrown,
|
||||
Skin::TannedDarkBrown,
|
||||
pub const HUMAN_SKIN_COLORS: [Skin; 18] = [
|
||||
Skin::Skin1,
|
||||
Skin::Skin2,
|
||||
Skin::Skin3,
|
||||
Skin::Skin4,
|
||||
Skin::Skin5,
|
||||
Skin::Skin6,
|
||||
Skin::Skin7,
|
||||
Skin::Skin8,
|
||||
Skin::Skin9,
|
||||
Skin::Skin10,
|
||||
Skin::Skin11,
|
||||
Skin::Skin12,
|
||||
Skin::Skin13,
|
||||
Skin::Skin14,
|
||||
Skin::Skin15,
|
||||
Skin::Skin16,
|
||||
Skin::Skin17,
|
||||
Skin::Skin18,
|
||||
];
|
||||
pub const ORC_SKIN_COLORS: [Skin; 4] = [Skin::OrcOne, Skin::OrcTwo, Skin::OrcThree, Skin::Brown];
|
||||
pub const ORC_SKIN_COLORS: [Skin; 4] = [Skin::OrcOne, Skin::OrcTwo, Skin::OrcThree, Skin::OrcFour];
|
||||
pub const UNDEAD_SKIN_COLORS: [Skin; 3] = [Skin::UndeadOne, Skin::UndeadTwo, Skin::UndeadThree];
|
||||
|
||||
// Eye colors
|
||||
@ -293,22 +322,31 @@ pub const DANARI_EYE_COLORS: [EyeColor; 3] = [
|
||||
EyeColor::LoyalBrown,
|
||||
EyeColor::ViciousRed,
|
||||
];
|
||||
pub const DWARF_EYE_COLORS: [EyeColor; 3] = [
|
||||
pub const DWARF_EYE_COLORS: [EyeColor; 4] = [
|
||||
EyeColor::CuriousGreen,
|
||||
EyeColor::LoyalBrown,
|
||||
EyeColor::NobleBlue,
|
||||
EyeColor::CornflowerBlue,
|
||||
];
|
||||
pub const ELF_EYE_COLORS: [EyeColor; 3] = [
|
||||
pub const ELF_EYE_COLORS: [EyeColor; 4] = [
|
||||
EyeColor::NobleBlue,
|
||||
EyeColor::CornflowerBlue,
|
||||
EyeColor::CuriousGreen,
|
||||
EyeColor::LoyalBrown,
|
||||
];
|
||||
pub const HUMAN_EYE_COLORS: [EyeColor; 3] = [
|
||||
pub const HUMAN_EYE_COLORS: [EyeColor; 4] = [
|
||||
EyeColor::NobleBlue,
|
||||
EyeColor::CornflowerBlue,
|
||||
EyeColor::CuriousGreen,
|
||||
EyeColor::LoyalBrown,
|
||||
];
|
||||
pub const ORC_EYE_COLORS: [EyeColor; 2] = [EyeColor::LoyalBrown, EyeColor::ExoticPurple];
|
||||
pub const ORC_EYE_COLORS: [EyeColor; 5] = [
|
||||
EyeColor::LoyalBrown,
|
||||
EyeColor::ExoticPurple,
|
||||
EyeColor::AmberOrange,
|
||||
EyeColor::PineGreen,
|
||||
EyeColor::CornflowerBlue,
|
||||
];
|
||||
pub const UNDEAD_EYE_COLORS: [EyeColor; 5] = [
|
||||
EyeColor::ViciousRed,
|
||||
EyeColor::PumpkinOrange,
|
||||
@ -365,7 +403,7 @@ impl Race {
|
||||
self.skin_colors()
|
||||
.get(val as usize)
|
||||
.copied()
|
||||
.unwrap_or(Skin::Tanned)
|
||||
.unwrap_or(Skin::Skin3)
|
||||
}
|
||||
|
||||
pub fn num_skin_colors(self) -> u8 { self.skin_colors().len() as u8 }
|
||||
@ -389,7 +427,7 @@ impl Race {
|
||||
(Race::Elf, BodyType::Male) => 4,
|
||||
(Race::Human, BodyType::Female) => 19,
|
||||
(Race::Human, BodyType::Male) => 17,
|
||||
(Race::Orc, BodyType::Female) => 1,
|
||||
(Race::Orc, BodyType::Female) => 7,
|
||||
(Race::Orc, BodyType::Male) => 8,
|
||||
(Race::Undead, BodyType::Female) => 4,
|
||||
(Race::Undead, BodyType::Male) => 3,
|
||||
@ -406,7 +444,7 @@ impl Race {
|
||||
(Race::Elf, BodyType::Male) => 1,
|
||||
(Race::Human, BodyType::Female) => 1,
|
||||
(Race::Human, BodyType::Male) => 1,
|
||||
(Race::Orc, BodyType::Female) => 3,
|
||||
(Race::Orc, BodyType::Female) => 4,
|
||||
(Race::Orc, BodyType::Male) => 5,
|
||||
(Race::Undead, BodyType::Female) => 1,
|
||||
(Race::Undead, BodyType::Male) => 1,
|
||||
@ -534,6 +572,10 @@ pub enum EyeColor {
|
||||
MagicPurple = 7,
|
||||
ToxicGreen = 8,
|
||||
ExoticPurple = 9,
|
||||
SulfurYellow = 10,
|
||||
AmberOrange = 11,
|
||||
PineGreen = 12,
|
||||
CornflowerBlue = 13,
|
||||
}
|
||||
impl EyeColor {
|
||||
pub fn light_rgb(self) -> Rgb<u8> {
|
||||
@ -548,6 +590,10 @@ impl EyeColor {
|
||||
EyeColor::MagicPurple => Rgb::new(137, 4, 177),
|
||||
EyeColor::ToxicGreen => Rgb::new(1, 223, 1),
|
||||
EyeColor::ExoticPurple => Rgb::new(95, 32, 111),
|
||||
EyeColor::SulfurYellow => Rgb::new(235, 198, 94),
|
||||
EyeColor::AmberOrange => Rgb::new(137, 46, 1),
|
||||
EyeColor::PineGreen => Rgb::new(0, 78, 56),
|
||||
EyeColor::CornflowerBlue => Rgb::new(18, 66, 90),
|
||||
}
|
||||
}
|
||||
|
||||
@ -563,6 +609,10 @@ impl EyeColor {
|
||||
EyeColor::MagicPurple => Rgb::new(110, 3, 143),
|
||||
EyeColor::ToxicGreen => Rgb::new(1, 185, 1),
|
||||
EyeColor::ExoticPurple => Rgb::new(69, 23, 80),
|
||||
EyeColor::SulfurYellow => Rgb::new(209, 176, 84),
|
||||
EyeColor::AmberOrange => Rgb::new(112, 40, 1),
|
||||
EyeColor::PineGreen => Rgb::new(0, 54, 38),
|
||||
EyeColor::CornflowerBlue => Rgb::new(13, 47, 64),
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,12 +630,12 @@ pub const ALL_ACCESSORIES: [Accessory; 2] = [Accessory::Nothing, Accessory::Some
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[repr(u32)]
|
||||
pub enum Skin {
|
||||
Pale = 0,
|
||||
White = 1,
|
||||
Tanned = 2,
|
||||
Brown = 3,
|
||||
TannedBrown = 4,
|
||||
TannedDarkBrown = 5,
|
||||
Skin1 = 0,
|
||||
Skin2 = 1,
|
||||
Skin3 = 2,
|
||||
Skin4 = 3,
|
||||
Skin5 = 4,
|
||||
Skin6 = 5,
|
||||
Iron = 6,
|
||||
Steel = 7,
|
||||
DanariOne = 8,
|
||||
@ -601,16 +651,41 @@ pub enum Skin {
|
||||
UndeadOne = 18,
|
||||
UndeadTwo = 19,
|
||||
UndeadThree = 20,
|
||||
Skin7 = 21,
|
||||
Skin8 = 22,
|
||||
Skin9 = 23,
|
||||
Skin10 = 24,
|
||||
Skin11 = 25,
|
||||
Skin12 = 26,
|
||||
Skin13 = 27,
|
||||
Skin14 = 28,
|
||||
Skin15 = 29,
|
||||
Skin16 = 30,
|
||||
Skin17 = 31,
|
||||
Skin18 = 32,
|
||||
OrcFour = 33,
|
||||
}
|
||||
impl Skin {
|
||||
pub fn rgb(self) -> Rgb<u8> {
|
||||
let color = match self {
|
||||
Self::Pale => (252, 211, 179),
|
||||
Self::White => (253, 195, 164),
|
||||
Self::Tanned => (222, 181, 151),
|
||||
Self::Brown => (123, 80, 45),
|
||||
Self::TannedBrown => (135, 70, 50),
|
||||
Self::TannedDarkBrown => (116, 61, 43),
|
||||
Self::Skin1 => (255, 229, 200),
|
||||
Self::Skin2 => (255, 218, 190),
|
||||
Self::Skin3 => (255, 206, 180),
|
||||
Self::Skin4 => (255, 195, 170),
|
||||
Self::Skin5 => (240, 184, 160),
|
||||
Self::Skin6 => (225, 172, 150),
|
||||
Self::Skin7 => (210, 161, 140),
|
||||
Self::Skin8 => (195, 149, 130),
|
||||
Self::Skin9 => (180, 138, 120),
|
||||
Self::Skin10 => (165, 126, 110),
|
||||
Self::Skin11 => (150, 114, 100),
|
||||
Self::Skin12 => (135, 103, 90),
|
||||
Self::Skin13 => (120, 92, 80),
|
||||
Self::Skin14 => (105, 80, 70),
|
||||
Self::Skin15 => (90, 69, 60),
|
||||
Self::Skin16 => (75, 57, 50),
|
||||
Self::Skin17 => (60, 46, 40),
|
||||
Self::Skin18 => (45, 34, 30),
|
||||
Self::Iron => (135, 113, 95),
|
||||
Self::Steel => (108, 94, 86),
|
||||
Self::DanariOne => (104, 168, 196),
|
||||
@ -623,6 +698,7 @@ impl Skin {
|
||||
Self::OrcOne => (61, 130, 42),
|
||||
Self::OrcTwo => (82, 117, 36),
|
||||
Self::OrcThree => (71, 94, 42),
|
||||
Self::OrcFour => (97, 54, 29),
|
||||
Self::UndeadOne => (240, 243, 239),
|
||||
Self::UndeadTwo => (178, 178, 178),
|
||||
Self::UndeadThree => (145, 135, 121),
|
||||
@ -632,12 +708,24 @@ impl Skin {
|
||||
|
||||
pub fn light_rgb(self) -> Rgb<u8> {
|
||||
let color = match self {
|
||||
Self::Pale => (255, 227, 193),
|
||||
Self::White => (255, 210, 180),
|
||||
Self::Tanned => (239, 197, 164),
|
||||
Self::Brown => (150, 104, 68),
|
||||
Self::TannedBrown => (148, 85, 64),
|
||||
Self::TannedDarkBrown => (132, 74, 56),
|
||||
Self::Skin1 => (255, 229, 200),
|
||||
Self::Skin2 => (255, 218, 190),
|
||||
Self::Skin3 => (255, 206, 180),
|
||||
Self::Skin4 => (255, 195, 170),
|
||||
Self::Skin5 => (240, 184, 160),
|
||||
Self::Skin6 => (225, 172, 150),
|
||||
Self::Skin7 => (210, 161, 140),
|
||||
Self::Skin8 => (195, 149, 130),
|
||||
Self::Skin9 => (180, 138, 120),
|
||||
Self::Skin10 => (165, 126, 110),
|
||||
Self::Skin11 => (150, 114, 100),
|
||||
Self::Skin12 => (135, 103, 90),
|
||||
Self::Skin13 => (120, 92, 80),
|
||||
Self::Skin14 => (105, 80, 70),
|
||||
Self::Skin15 => (90, 69, 60),
|
||||
Self::Skin16 => (75, 57, 50),
|
||||
Self::Skin17 => (60, 46, 40),
|
||||
Self::Skin18 => (45, 34, 30),
|
||||
Self::Iron => (144, 125, 106),
|
||||
Self::Steel => (120, 107, 99),
|
||||
Self::DanariOne => (116, 176, 208),
|
||||
@ -650,6 +738,7 @@ impl Skin {
|
||||
Self::OrcOne => (83, 165, 56),
|
||||
Self::OrcTwo => (92, 132, 46),
|
||||
Self::OrcThree => (84, 110, 54),
|
||||
Self::OrcFour => (97, 54, 29),
|
||||
Self::UndeadOne => (254, 252, 251),
|
||||
Self::UndeadTwo => (190, 192, 191),
|
||||
Self::UndeadThree => (160, 151, 134),
|
||||
@ -659,12 +748,24 @@ impl Skin {
|
||||
|
||||
pub fn dark_rgb(self) -> Rgb<u8> {
|
||||
let color = match self {
|
||||
Self::Pale => (229, 192, 163),
|
||||
Self::White => (239, 179, 150),
|
||||
Self::Tanned => (208, 167, 135),
|
||||
Self::Brown => (106, 63, 30),
|
||||
Self::TannedBrown => (122, 58, 40),
|
||||
Self::TannedDarkBrown => (100, 47, 32),
|
||||
Self::Skin1 => (242, 217, 189),
|
||||
Self::Skin2 => (242, 207, 189),
|
||||
Self::Skin3 => (242, 197, 172),
|
||||
Self::Skin4 => (242, 186, 162),
|
||||
Self::Skin5 => (212, 173, 150),
|
||||
Self::Skin6 => (212, 163, 142),
|
||||
Self::Skin7 => (196, 151, 132),
|
||||
Self::Skin8 => (181, 139, 121),
|
||||
Self::Skin9 => (168, 129, 113),
|
||||
Self::Skin10 => (153, 117, 103),
|
||||
Self::Skin11 => (138, 105, 92),
|
||||
Self::Skin12 => (122, 93, 82),
|
||||
Self::Skin13 => (107, 82, 72),
|
||||
Self::Skin14 => (92, 70, 62),
|
||||
Self::Skin15 => (77, 59, 51),
|
||||
Self::Skin16 => (61, 47, 41),
|
||||
Self::Skin17 => (48, 37, 32),
|
||||
Self::Skin18 => (33, 25, 22),
|
||||
Self::Iron => (124, 99, 82),
|
||||
Self::Steel => (96, 81, 72),
|
||||
Self::DanariOne => (92, 155, 183),
|
||||
@ -677,6 +778,7 @@ impl Skin {
|
||||
Self::OrcOne => (55, 114, 36),
|
||||
Self::OrcTwo => (70, 104, 29),
|
||||
Self::OrcThree => (60, 83, 32),
|
||||
Self::OrcFour => (84, 47, 25),
|
||||
Self::UndeadOne => (229, 231, 230),
|
||||
Self::UndeadTwo => (165, 166, 164),
|
||||
Self::UndeadThree => (130, 122, 106),
|
||||
|
@ -90,7 +90,7 @@ pub enum Armor {
|
||||
Necklace,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum Consumable {
|
||||
Apple,
|
||||
Cheese,
|
||||
|
@ -1,12 +1,16 @@
|
||||
pub mod item;
|
||||
|
||||
// Reexports
|
||||
pub use item::{Debug, Item, ItemKind, Tool};
|
||||
pub use item::{Consumable, Debug, Item, ItemKind, Tool};
|
||||
|
||||
use crate::assets;
|
||||
use specs::{Component, HashMapStorage, NullStorage};
|
||||
use specs::{Component, FlaggedStorage, HashMapStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
use std::ops::Not;
|
||||
|
||||
// The limit on distance between the entity and a collectible (squared)
|
||||
pub const MAX_PICKUP_RANGE_SQR: f32 = 64.0;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Inventory {
|
||||
pub slots: Vec<Option<Item>>,
|
||||
@ -136,12 +140,38 @@ impl Component for Inventory {
|
||||
type Storage = HashMapStorage<Self>;
|
||||
}
|
||||
|
||||
// ForceUpdate
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum InventoryUpdateEvent {
|
||||
Init,
|
||||
Used,
|
||||
Consumed(Consumable),
|
||||
Gave,
|
||||
Given,
|
||||
Swapped,
|
||||
Dropped,
|
||||
Collected,
|
||||
CollectFailed,
|
||||
Possession,
|
||||
Debug,
|
||||
}
|
||||
|
||||
impl Default for InventoryUpdateEvent {
|
||||
fn default() -> Self { Self::Init }
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct InventoryUpdate;
|
||||
pub struct InventoryUpdate {
|
||||
event: InventoryUpdateEvent,
|
||||
}
|
||||
|
||||
impl InventoryUpdate {
|
||||
pub fn new(event: InventoryUpdateEvent) -> Self { Self { event } }
|
||||
|
||||
pub fn event(&self) -> InventoryUpdateEvent { self.event }
|
||||
}
|
||||
|
||||
impl Component for InventoryUpdate {
|
||||
type Storage = NullStorage<Self>;
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
||||
|
||||
#[cfg(test)] mod test;
|
||||
|
@ -28,7 +28,9 @@ pub use controller::{
|
||||
};
|
||||
pub use energy::{Energy, EnergySource};
|
||||
pub use inputs::CanBuild;
|
||||
pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind};
|
||||
pub use inventory::{
|
||||
item, Inventory, InventoryUpdate, InventoryUpdateEvent, Item, ItemKind, MAX_PICKUP_RANGE_SQR,
|
||||
};
|
||||
pub use last::Last;
|
||||
pub use location::{Waypoint, WaypointArea};
|
||||
pub use phys::{ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel};
|
||||
|
@ -1,3 +1,4 @@
|
||||
use authc::Uuid;
|
||||
use specs::{Component, FlaggedStorage, NullStorage};
|
||||
use specs_idvs::IDVStorage;
|
||||
|
||||
@ -7,20 +8,26 @@ const MAX_ALIAS_LEN: usize = 32;
|
||||
pub struct Player {
|
||||
pub alias: String,
|
||||
pub view_distance: Option<u32>,
|
||||
uuid: Uuid,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(alias: String, view_distance: Option<u32>) -> Self {
|
||||
pub fn new(alias: String, view_distance: Option<u32>, uuid: Uuid) -> Self {
|
||||
Self {
|
||||
alias,
|
||||
view_distance,
|
||||
uuid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.alias.chars().all(|c| c.is_alphanumeric() || c == '_')
|
||||
&& self.alias.len() <= MAX_ALIAS_LEN
|
||||
pub fn is_valid(&self) -> bool { Self::alias_is_valid(&self.alias) }
|
||||
|
||||
pub fn alias_is_valid(alias: &str) -> bool {
|
||||
alias.chars().all(|c| c.is_alphanumeric() || c == '_') && alias.len() <= MAX_ALIAS_LEN
|
||||
}
|
||||
|
||||
/// Not to be confused with uid
|
||||
pub fn uuid(&self) -> Uuid { self.uuid }
|
||||
}
|
||||
|
||||
impl Component for Player {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{comp, sync::Uid};
|
||||
use comp::item::Tool;
|
||||
use comp::{item::Tool, InventoryUpdateEvent};
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
use specs::Entity as EcsEntity;
|
||||
@ -9,22 +9,26 @@ use vek::*;
|
||||
pub struct SfxEventItem {
|
||||
pub sfx: SfxEvent,
|
||||
pub pos: Option<Vec3<f32>>,
|
||||
pub vol: Option<f32>,
|
||||
}
|
||||
|
||||
impl SfxEventItem {
|
||||
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>) -> Self { Self { sfx, pos } }
|
||||
pub fn new(sfx: SfxEvent, pos: Option<Vec3<f32>>, vol: Option<f32>) -> Self {
|
||||
Self { sfx, pos, vol }
|
||||
}
|
||||
|
||||
pub fn at_player_position(sfx: SfxEvent) -> Self { Self { sfx, pos: None } }
|
||||
pub fn at_player_position(sfx: SfxEvent) -> Self {
|
||||
Self {
|
||||
sfx,
|
||||
pos: None,
|
||||
vol: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
|
||||
pub enum SfxEvent {
|
||||
Idle,
|
||||
PlaceBlock,
|
||||
RemoveBlock,
|
||||
OpenChest,
|
||||
ChatTellReceived,
|
||||
OpenBag,
|
||||
Run,
|
||||
Roll,
|
||||
Climb,
|
||||
@ -36,10 +40,9 @@ pub enum SfxEvent {
|
||||
Fall,
|
||||
ExperienceGained,
|
||||
LevelUp,
|
||||
LightLantern,
|
||||
ExtinguishLantern,
|
||||
Attack(Tool),
|
||||
AttackWolf,
|
||||
Wield(Tool),
|
||||
Unwield(Tool),
|
||||
Inventory(InventoryUpdateEvent),
|
||||
}
|
||||
|
||||
pub enum LocalEvent {
|
||||
|
@ -4,8 +4,8 @@ use vek::*;
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ClientMsg {
|
||||
Register {
|
||||
player: comp::Player,
|
||||
password: String,
|
||||
view_distance: Option<u32>,
|
||||
token_or_username: String,
|
||||
},
|
||||
Character {
|
||||
name: String,
|
||||
@ -35,4 +35,5 @@ pub enum ClientMsg {
|
||||
key: Vec2<i32>,
|
||||
},
|
||||
Disconnect,
|
||||
Terminate,
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ pub mod server;
|
||||
pub use self::{
|
||||
client::ClientMsg,
|
||||
ecs_packet::EcsCompPacket,
|
||||
server::{PlayerListUpdate, RequestStateError, ServerError, ServerInfo, ServerMsg},
|
||||
server::{PlayerListUpdate, RegisterError, RequestStateError, ServerInfo, ServerMsg},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
|
||||
|
@ -4,23 +4,17 @@ use crate::{
|
||||
terrain::{Block, TerrainChunk},
|
||||
ChatType,
|
||||
};
|
||||
use authc::AuthClientError;
|
||||
use hashbrown::HashMap;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum RequestStateError {
|
||||
Denied,
|
||||
Already,
|
||||
Impossible,
|
||||
WrongMessage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ServerInfo {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub git_hash: String,
|
||||
pub git_date: String,
|
||||
pub auth_provider: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -71,24 +65,37 @@ pub enum ServerMsg {
|
||||
entity: u64,
|
||||
character_state: comp::CharacterState,
|
||||
},
|
||||
InventoryUpdate(comp::Inventory),
|
||||
InventoryUpdate(comp::Inventory, comp::InventoryUpdateEvent),
|
||||
TerrainChunkUpdate {
|
||||
key: Vec2<i32>,
|
||||
chunk: Result<Box<TerrainChunk>, ()>,
|
||||
},
|
||||
TerrainBlockUpdates(HashMap<Vec3<i32>, Block>),
|
||||
Error(ServerError),
|
||||
Disconnect,
|
||||
Shutdown,
|
||||
TooManyPlayers,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerError {
|
||||
TooManyPlayers,
|
||||
InvalidAuth,
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum RequestStateError {
|
||||
RegisterDenied(RegisterError),
|
||||
Denied,
|
||||
Already,
|
||||
Impossible,
|
||||
WrongMessage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum RegisterError {
|
||||
AlreadyLoggedIn,
|
||||
AuthError(String),
|
||||
//TODO: InvalidAlias,
|
||||
}
|
||||
|
||||
impl From<AuthClientError> for RegisterError {
|
||||
fn from(err: AuthClientError) -> Self { Self::AuthError(err.to_string()) }
|
||||
}
|
||||
|
||||
impl ServerMsg {
|
||||
pub fn chat(message: String) -> ServerMsg {
|
||||
ServerMsg::ChatMsg {
|
||||
|
@ -119,16 +119,16 @@ impl<S: PostMsg, R: PostMsg> PostBox<S, R> {
|
||||
|
||||
pub fn send_message(&mut self, msg: S) { let _ = self.send_tx.send(msg); }
|
||||
|
||||
pub fn next_message(&mut self) -> Option<R> {
|
||||
if self.error.is_some() {
|
||||
return None;
|
||||
pub fn next_message(&mut self) -> Result<R, Error> {
|
||||
if let Some(e) = self.error.clone() {
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
match self.recv_rx.recv().ok()? {
|
||||
Ok(msg) => Some(msg),
|
||||
match self.recv_rx.recv().map_err(|_| Error::ChannelFailure)? {
|
||||
Ok(msg) => Ok(msg),
|
||||
Err(e) => {
|
||||
self.error = Some(e);
|
||||
None
|
||||
self.error = Some(e.clone());
|
||||
Err(e)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -286,8 +286,35 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
// Run RegionMap tick to update entity region occupancy
|
||||
pub fn update_region_map(&self) {
|
||||
self.ecs.write_resource::<RegionMap>().tick(
|
||||
self.ecs.read_storage::<comp::Pos>(),
|
||||
self.ecs.read_storage::<comp::Vel>(),
|
||||
self.ecs.entities(),
|
||||
);
|
||||
}
|
||||
|
||||
// Apply terrain changes
|
||||
pub fn apply_terrain_changes(&self) {
|
||||
let mut terrain = self.ecs.write_resource::<TerrainGrid>();
|
||||
let mut modified_blocks = std::mem::replace(
|
||||
&mut self.ecs.write_resource::<BlockChange>().blocks,
|
||||
Default::default(),
|
||||
);
|
||||
// Apply block modifications
|
||||
// Only include in `TerrainChanges` if successful
|
||||
modified_blocks.retain(|pos, block| terrain.set(*pos, *block).is_ok());
|
||||
self.ecs.write_resource::<TerrainChanges>().modified_blocks = modified_blocks;
|
||||
}
|
||||
|
||||
/// Execute a single tick, simulating the game state by the given duration.
|
||||
pub fn tick(&mut self, dt: Duration, add_foreign_systems: impl Fn(&mut DispatcherBuilder)) {
|
||||
pub fn tick(
|
||||
&mut self,
|
||||
dt: Duration,
|
||||
add_foreign_systems: impl Fn(&mut DispatcherBuilder),
|
||||
update_terrain_and_regions: bool,
|
||||
) {
|
||||
// Change the time accordingly.
|
||||
self.ecs.write_resource::<TimeOfDay>().0 += dt.as_secs_f64() * DAY_CYCLE_FACTOR;
|
||||
self.ecs.write_resource::<Time>().0 += dt.as_secs_f64();
|
||||
@ -297,12 +324,9 @@ impl State {
|
||||
// important physics events.
|
||||
self.ecs.write_resource::<DeltaTime>().0 = dt.as_secs_f32().min(MAX_DELTA_TIME);
|
||||
|
||||
// Run RegionMap tick to update entity region occupancy
|
||||
self.ecs.write_resource::<RegionMap>().tick(
|
||||
self.ecs.read_storage::<comp::Pos>(),
|
||||
self.ecs.read_storage::<comp::Vel>(),
|
||||
self.ecs.entities(),
|
||||
);
|
||||
if update_terrain_and_regions {
|
||||
self.update_region_map();
|
||||
}
|
||||
|
||||
// Run systems to update the world.
|
||||
// Create and run a dispatcher for ecs systems.
|
||||
@ -315,16 +339,9 @@ impl State {
|
||||
|
||||
self.ecs.maintain();
|
||||
|
||||
// Apply terrain changes
|
||||
let mut terrain = self.ecs.write_resource::<TerrainGrid>();
|
||||
let mut modified_blocks = std::mem::replace(
|
||||
&mut self.ecs.write_resource::<BlockChange>().blocks,
|
||||
Default::default(),
|
||||
);
|
||||
// Apply block modifications
|
||||
// Only include in `TerrainChanges` if successful
|
||||
modified_blocks.retain(|pos, block| terrain.set(*pos, *block).is_ok());
|
||||
self.ecs.write_resource::<TerrainChanges>().modified_blocks = modified_blocks;
|
||||
if update_terrain_and_regions {
|
||||
self.apply_terrain_changes();
|
||||
}
|
||||
|
||||
// Process local events
|
||||
let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
|
||||
|
@ -153,7 +153,7 @@ impl<'a> System<'a> for Sys {
|
||||
been_close,
|
||||
..
|
||||
} => {
|
||||
if let (Some(tgt_pos), _tgt_stats, tgt_alignment) = (
|
||||
if let (Some(tgt_pos), Some(tgt_stats), tgt_alignment) = (
|
||||
positions.get(*target),
|
||||
stats.get(*target),
|
||||
alignments
|
||||
@ -164,7 +164,8 @@ impl<'a> System<'a> for Sys {
|
||||
// Don't attack entities we are passive towards
|
||||
// TODO: This is here, it's a bit of a hack
|
||||
if let Some(alignment) = alignment {
|
||||
if (*alignment).passive_towards(tgt_alignment) {
|
||||
if (*alignment).passive_towards(tgt_alignment) || tgt_stats.is_dead
|
||||
{
|
||||
do_idle = true;
|
||||
break 'activity;
|
||||
}
|
||||
|
8
server-cli/Dockerfile
Normal file
8
server-cli/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM debian:stable-slim
|
||||
|
||||
ARG PROJECTNAME=server-cli
|
||||
|
||||
COPY ./server-cli/docker-run.sh /opt/docker-run.sh
|
||||
COPY ./veloren-server-cli /opt/veloren-server-cli
|
||||
COPY ./assets/common /opt/assets/common
|
||||
COPY ./assets/world /opt/assets/world
|
17
server-cli/docker-compose.yml
Normal file
17
server-cli/docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
game-server:
|
||||
image: registry.gitlab.com/veloren/veloren:master-server
|
||||
ports:
|
||||
- "14004:14004"
|
||||
- "14005:14005"
|
||||
deploy:
|
||||
replicas: 1
|
||||
update_config:
|
||||
parallelism: 2
|
||||
delay: 10s
|
||||
order: stop-first
|
||||
failure_action: rollback
|
||||
restart_policy:
|
||||
condition: on-failure
|
3
server-cli/docker-run.sh
Executable file
3
server-cli/docker-run.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /opt
|
||||
RUST_LOG=info,common=debug,common::net=info RUST_BACKTRACE=1 /opt/veloren-server-cli
|
@ -31,3 +31,4 @@ prometheus = "0.7"
|
||||
prometheus-static-metric = "0.2"
|
||||
rouille = "3.0.0"
|
||||
portpicker = { git = "https://github.com/wusyong/portpicker-rs", branch = "fix_ipv6" }
|
||||
authc = { git = "https://gitlab.com/veloren/auth.git", rev = "65571ade0d954a0e0bd995fdb314854ff146ab97" }
|
||||
|
@ -1,32 +1,88 @@
|
||||
use authc::{AuthClient, AuthToken, Uuid};
|
||||
use common::msg::RegisterError;
|
||||
use hashbrown::HashMap;
|
||||
use log::{info, warn};
|
||||
use log::error;
|
||||
use std::str::FromStr;
|
||||
|
||||
fn derive_uuid(username: &str) -> Uuid {
|
||||
let mut state: [u8; 16] = [
|
||||
52, 17, 19, 239, 52, 17, 19, 239, 52, 17, 19, 239, 52, 17, 19, 239,
|
||||
];
|
||||
for mix_byte_1 in username.as_bytes() {
|
||||
for i in 0..16 {
|
||||
let mix_byte_step: u8 = mix_byte_1
|
||||
.wrapping_pow(239)
|
||||
.wrapping_mul((i as u8).wrapping_pow(43));
|
||||
let mix_byte_2 = state[(i + mix_byte_step as usize) % 16];
|
||||
let rot_step: u8 = mix_byte_1
|
||||
.wrapping_pow(29)
|
||||
.wrapping_mul((i as u8).wrapping_pow(163));
|
||||
state[i] = (state[i] ^ mix_byte_1)
|
||||
.wrapping_mul(mix_byte_2)
|
||||
.rotate_left(rot_step as u32);
|
||||
}
|
||||
}
|
||||
Uuid::from_slice(&state).unwrap()
|
||||
}
|
||||
|
||||
pub struct AuthProvider {
|
||||
accounts: HashMap<String, String>,
|
||||
accounts: HashMap<Uuid, String>,
|
||||
auth_server: Option<AuthClient>,
|
||||
}
|
||||
|
||||
impl AuthProvider {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(auth_addr: Option<String>) -> Self {
|
||||
let auth_server = match auth_addr {
|
||||
Some(addr) => Some(AuthClient::new(addr)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
AuthProvider {
|
||||
accounts: HashMap::new(),
|
||||
auth_server,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query(&mut self, username: String, password: String) -> bool {
|
||||
let pwd = password.clone();
|
||||
if self.accounts.entry(username.clone()).or_insert_with(|| {
|
||||
info!("Registered new user '{}'", &username);
|
||||
pwd
|
||||
}) == &password
|
||||
{
|
||||
info!("User '{}' successfully authenticated", username);
|
||||
true
|
||||
pub fn logout(&mut self, uuid: Uuid) {
|
||||
if self.accounts.remove(&uuid).is_none() {
|
||||
error!("Attempted to logout user that is not logged in.");
|
||||
};
|
||||
}
|
||||
|
||||
pub fn query(&mut self, username_or_token: String) -> Result<(String, Uuid), RegisterError> {
|
||||
// Based on whether auth server is provided or not we expect an username or
|
||||
// token
|
||||
match &self.auth_server {
|
||||
// Token from auth server expected
|
||||
Some(srv) => {
|
||||
log::info!("Validating '{}' token.", &username_or_token);
|
||||
// Parse token
|
||||
let token = AuthToken::from_str(&username_or_token)
|
||||
.map_err(|e| RegisterError::AuthError(e.to_string()))?;
|
||||
// Validate token
|
||||
let uuid = srv.validate(token)?;
|
||||
// Check if already logged in
|
||||
if self.accounts.contains_key(&uuid) {
|
||||
return Err(RegisterError::AlreadyLoggedIn);
|
||||
}
|
||||
// Log in
|
||||
let username = srv.uuid_to_username(uuid)?;
|
||||
self.accounts.insert(uuid, username.clone());
|
||||
Ok((username, uuid))
|
||||
},
|
||||
// Username is expected
|
||||
None => {
|
||||
// Assume username was provided
|
||||
let username = username_or_token;
|
||||
let uuid = derive_uuid(&username);
|
||||
if !self.accounts.contains_key(&uuid) {
|
||||
log::info!("New User '{}'", username);
|
||||
self.accounts.insert(uuid, username.clone());
|
||||
Ok((username, uuid))
|
||||
} else {
|
||||
warn!(
|
||||
"User '{}' attempted to log in with invalid password '{}'!",
|
||||
username, password
|
||||
);
|
||||
false
|
||||
Err(RegisterError::AlreadyLoggedIn)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,11 +234,18 @@ lazy_static! {
|
||||
),
|
||||
ChatCommand::new(
|
||||
"give_exp",
|
||||
"{} {}",
|
||||
"/give_exp <playername> <amount> : Give experience to specified player",
|
||||
"{d} {}",
|
||||
"/give_exp <amount> <playername?> : Give experience to yourself or specify a target player",
|
||||
true,
|
||||
handle_exp,
|
||||
),
|
||||
ChatCommand::new(
|
||||
"set_level",
|
||||
"{d} {}",
|
||||
"/set_level <level> <playername?> : Set own Level or specify a target player",
|
||||
true,
|
||||
handle_level
|
||||
),
|
||||
ChatCommand::new(
|
||||
"removelights",
|
||||
"{}",
|
||||
@ -255,6 +262,7 @@ lazy_static! {
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) {
|
||||
if let Ok(item) = assets::load_cloned(&args) {
|
||||
server
|
||||
@ -267,7 +275,10 @@ fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &C
|
||||
.state
|
||||
.ecs()
|
||||
.write_storage::<comp::InventoryUpdate>()
|
||||
.insert(entity, comp::InventoryUpdate);
|
||||
.insert(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given),
|
||||
);
|
||||
} else {
|
||||
server.notify_client(entity, ServerMsg::private(String::from("Invalid item!")));
|
||||
}
|
||||
@ -632,7 +643,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
|
||||
.read_storage::<comp::Ori>()
|
||||
.get(entity)
|
||||
.copied();
|
||||
/*let builder = server
|
||||
/*let builder = server.state
|
||||
.create_object(pos, ori, obj_type)
|
||||
.with(ori);*/
|
||||
if let (Some(pos), Some(ori)) = (pos, ori) {
|
||||
@ -694,6 +705,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
|
||||
},
|
||||
};
|
||||
server
|
||||
.state
|
||||
.create_object(pos, obj_type)
|
||||
.with(comp::Ori(
|
||||
// converts player orientation into a 90° rotation for the object by using the axis
|
||||
@ -1015,27 +1027,74 @@ spawn_rate {:?} "#,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let (a_alias, a_exp) = scan_fmt_some!(&args, action.arg_fmt, String, i64);
|
||||
if let (Some(alias), Some(exp)) = (a_alias, a_exp) {
|
||||
let ecs = server.state.ecs_mut();
|
||||
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
fn find_target(
|
||||
ecs: &specs::World,
|
||||
opt_alias: Option<String>,
|
||||
fallback: EcsEntity,
|
||||
) -> Result<EcsEntity, ServerMsg> {
|
||||
if let Some(alias) = opt_alias {
|
||||
(&ecs.entities(), &ecs.read_storage::<comp::Player>())
|
||||
.join()
|
||||
.find(|(_, player)| player.alias == alias)
|
||||
.map(|(entity, _)| entity);
|
||||
.map(|(entity, _)| entity)
|
||||
.ok_or(ServerMsg::private(format!("Player '{}' not found!", alias)))
|
||||
} else {
|
||||
Ok(fallback)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String);
|
||||
|
||||
if let Some(exp) = a_exp {
|
||||
let ecs = server.state.ecs_mut();
|
||||
let target = find_target(&ecs, a_alias, entity);
|
||||
|
||||
let mut error_msg = None;
|
||||
|
||||
match opt_player {
|
||||
Some(_alias) => {
|
||||
if let Some(stats) = ecs.write_storage::<comp::Stats>().get_mut(entity) {
|
||||
match target {
|
||||
Ok(player) => {
|
||||
if let Some(stats) = ecs.write_storage::<comp::Stats>().get_mut(player) {
|
||||
stats.exp.change_by(exp);
|
||||
} else {
|
||||
error_msg = Some(ServerMsg::private(String::from("Player has no stats!")));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
error_msg = Some(ServerMsg::private(format!("Player '{}' not found!", alias)));
|
||||
Err(e) => {
|
||||
error_msg = Some(e);
|
||||
},
|
||||
}
|
||||
|
||||
if let Some(msg) = error_msg {
|
||||
server.notify_client(entity, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String);
|
||||
|
||||
if let Some(lvl) = a_lvl {
|
||||
let ecs = server.state.ecs_mut();
|
||||
let target = find_target(&ecs, a_alias, entity);
|
||||
|
||||
let mut error_msg = None;
|
||||
|
||||
match target {
|
||||
Ok(player) => {
|
||||
if let Some(stats) = ecs.write_storage::<comp::Stats>().get_mut(player) {
|
||||
stats.level.set_level(lvl);
|
||||
|
||||
stats.update_max_hp();
|
||||
stats
|
||||
.health
|
||||
.set_to(stats.health.maximum(), comp::HealthSource::LevelUp);
|
||||
} else {
|
||||
error_msg = Some(ServerMsg::private(String::from("Player has no stats!")));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error_msg = Some(e);
|
||||
},
|
||||
}
|
||||
|
||||
@ -1059,7 +1118,10 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action:
|
||||
.state
|
||||
.ecs()
|
||||
.write_storage::<comp::InventoryUpdate>()
|
||||
.insert(entity, comp::InventoryUpdate);
|
||||
.insert(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug),
|
||||
);
|
||||
} else {
|
||||
server.notify_client(
|
||||
entity,
|
||||
|
84
server/src/events/entity_creation.rs
Normal file
84
server/src/events/entity_creation.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use crate::{sys, Server, StateExt};
|
||||
use common::comp::{
|
||||
self, Agent, Alignment, Body, Gravity, LightEmitter, Pos, Projectile, Scale, Stats, Vel,
|
||||
WaypointArea,
|
||||
};
|
||||
use specs::{Builder, Entity as EcsEntity, WorldExt};
|
||||
use vek::{Rgb, Vec3};
|
||||
|
||||
pub fn handle_create_character(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
name: String,
|
||||
body: Body,
|
||||
main: Option<String>,
|
||||
) {
|
||||
let state = &mut server.state;
|
||||
let server_settings = &server.server_settings;
|
||||
|
||||
state.create_player_character(entity, name, body, main, server_settings);
|
||||
sys::subscription::initialize_region_subscription(state.ecs(), entity);
|
||||
}
|
||||
|
||||
pub fn handle_create_npc(
|
||||
server: &mut Server,
|
||||
pos: Pos,
|
||||
stats: Stats,
|
||||
body: Body,
|
||||
agent: Agent,
|
||||
alignment: Alignment,
|
||||
scale: Scale,
|
||||
) {
|
||||
server
|
||||
.state
|
||||
.create_npc(pos, stats, body)
|
||||
.with(agent)
|
||||
.with(scale)
|
||||
.with(alignment)
|
||||
.build();
|
||||
}
|
||||
|
||||
pub fn handle_shoot(
|
||||
server: &mut Server,
|
||||
entity: EcsEntity,
|
||||
dir: Vec3<f32>,
|
||||
body: Body,
|
||||
light: Option<LightEmitter>,
|
||||
projectile: Projectile,
|
||||
gravity: Option<Gravity>,
|
||||
) {
|
||||
let state = server.state_mut();
|
||||
|
||||
let mut pos = state
|
||||
.ecs()
|
||||
.read_storage::<Pos>()
|
||||
.get(entity)
|
||||
.expect("Failed to fetch entity")
|
||||
.0;
|
||||
|
||||
// TODO: Player height
|
||||
pos.z += 1.2;
|
||||
|
||||
let mut builder = state.create_projectile(Pos(pos), Vel(dir * 100.0), body, projectile);
|
||||
if let Some(light) = light {
|
||||
builder = builder.with(light)
|
||||
}
|
||||
if let Some(gravity) = gravity {
|
||||
builder = builder.with(gravity)
|
||||
}
|
||||
|
||||
builder.build();
|
||||
}
|
||||
|
||||
pub fn handle_create_waypoint(server: &mut Server, pos: Vec3<f32>) {
|
||||
server
|
||||
.state
|
||||
.create_object(Pos(pos), comp::object::Body::CampfireLit)
|
||||
.with(LightEmitter {
|
||||
offset: Vec3::unit_z() * 0.5,
|
||||
col: Rgb::new(1.0, 0.65, 0.2),
|
||||
strength: 2.0,
|
||||
})
|
||||
.with(WaypointArea::default())
|
||||
.build();
|
||||
}
|
175
server/src/events/entity_manipulation.rs
Normal file
175
server/src/events/entity_manipulation.rs
Normal file
@ -0,0 +1,175 @@
|
||||
use crate::{client::Client, Server, SpawnPoint, StateExt};
|
||||
use common::{
|
||||
comp::{self, HealthChange, HealthSource, Player, Stats},
|
||||
msg::ServerMsg,
|
||||
state::BlockChange,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
terrain::{Block, TerrainGrid},
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use log::error;
|
||||
use specs::{Entity as EcsEntity, WorldExt};
|
||||
use vek::Vec3;
|
||||
|
||||
pub fn handle_damage(server: &Server, uid: Uid, change: HealthChange) {
|
||||
let state = &server.state;
|
||||
let ecs = state.ecs();
|
||||
if let Some(entity) = ecs.entity_from_uid(uid.into()) {
|
||||
if let Some(stats) = ecs.write_storage::<Stats>().get_mut(entity) {
|
||||
stats.health.change_by(change);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSource) {
|
||||
let state = server.state_mut();
|
||||
|
||||
// Chat message
|
||||
if let Some(player) = state.ecs().read_storage::<Player>().get(entity) {
|
||||
let msg = if let HealthSource::Attack { by } = cause {
|
||||
state.ecs().entity_from_uid(by.into()).and_then(|attacker| {
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<Player>()
|
||||
.get(attacker)
|
||||
.map(|attacker_alias| {
|
||||
format!("{} was killed by {}", &player.alias, &attacker_alias.alias)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or(format!("{} died", &player.alias));
|
||||
|
||||
state.notify_registered_clients(ServerMsg::kill(msg));
|
||||
}
|
||||
|
||||
{
|
||||
// Give EXP to the killer if entity had stats
|
||||
let mut stats = state.ecs().write_storage::<Stats>();
|
||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||
if let HealthSource::Attack { by } = cause {
|
||||
state.ecs().entity_from_uid(by.into()).map(|attacker| {
|
||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||
// TODO: Discuss whether we should give EXP by Player
|
||||
// Killing or not.
|
||||
attacker_stats
|
||||
.exp
|
||||
.change_by((entity_stats.level.level() * 10) as i64);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.is_some()
|
||||
{
|
||||
state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, comp::Vel(Vec3::zero()))
|
||||
.err()
|
||||
.map(|err| error!("Failed to set zero vel on dead client: {:?}", err));
|
||||
state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, comp::ForceUpdate)
|
||||
.err()
|
||||
.map(|err| error!("Failed to insert ForceUpdate on dead client: {:?}", err));
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Energy>()
|
||||
.get_mut(entity)
|
||||
.map(|energy| energy.set_to(energy.maximum(), comp::EnergySource::Revive));
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage::<comp::CharacterState>()
|
||||
.insert(entity, comp::CharacterState::default());
|
||||
} else {
|
||||
// If not a player delete the entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete destroyed entity: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>) {
|
||||
let state = &server.state;
|
||||
if vel.z <= -37.0 {
|
||||
if let Some(stats) = state.ecs().write_storage::<comp::Stats>().get_mut(entity) {
|
||||
let falldmg = (vel.z / 2.5) as i32;
|
||||
if falldmg < 0 {
|
||||
stats.health.change_by(comp::HealthChange {
|
||||
amount: falldmg,
|
||||
cause: comp::HealthSource::World,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_respawn(server: &Server, entity: EcsEntity) {
|
||||
let state = &server.state;
|
||||
|
||||
// Only clients can respawn
|
||||
if state
|
||||
.ecs()
|
||||
.write_storage::<Client>()
|
||||
.get_mut(entity)
|
||||
.is_some()
|
||||
{
|
||||
let respawn_point = state
|
||||
.read_component_cloned::<comp::Waypoint>(entity)
|
||||
.map(|wp| wp.get_pos())
|
||||
.unwrap_or(state.ecs().read_resource::<SpawnPoint>().0);
|
||||
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.revive());
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Pos>()
|
||||
.get_mut(entity)
|
||||
.map(|pos| pos.0 = respawn_point);
|
||||
state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, comp::ForceUpdate)
|
||||
.err()
|
||||
.map(|err| {
|
||||
error!(
|
||||
"Error inserting ForceUpdate component when respawning client: {:?}",
|
||||
err
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_explosion(server: &Server, pos: Vec3<f32>, radius: f32) {
|
||||
const RAYS: usize = 500;
|
||||
|
||||
for _ in 0..RAYS {
|
||||
let dir = Vec3::new(
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
rand::random::<f32>() - 0.5,
|
||||
)
|
||||
.normalized();
|
||||
|
||||
let ecs = server.state.ecs();
|
||||
let mut block_change = ecs.write_resource::<BlockChange>();
|
||||
|
||||
let _ = ecs
|
||||
.read_resource::<TerrainGrid>()
|
||||
.ray(pos, pos + dir * radius)
|
||||
.until(|_| rand::random::<f32>() < 0.05)
|
||||
.for_each(|pos| block_change.set(pos, Block::empty()))
|
||||
.cast();
|
||||
}
|
||||
}
|
174
server/src/events/interaction.rs
Normal file
174
server/src/events/interaction.rs
Normal file
@ -0,0 +1,174 @@
|
||||
use crate::{
|
||||
client::{Client, RegionSubscription},
|
||||
Server,
|
||||
};
|
||||
use common::{
|
||||
assets, comp,
|
||||
msg::ServerMsg,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
};
|
||||
use log::error;
|
||||
use specs::{world::WorldExt, Entity as EcsEntity};
|
||||
|
||||
pub fn handle_mount(server: &mut Server, mounter: EcsEntity, mountee: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
|
||||
if state
|
||||
.ecs()
|
||||
.read_storage::<comp::Mounting>()
|
||||
.get(mounter)
|
||||
.is_none()
|
||||
{
|
||||
let not_mounting_yet = if let Some(comp::MountState::Unmounted) = state
|
||||
.ecs()
|
||||
.read_storage::<comp::MountState>()
|
||||
.get(mountee)
|
||||
.cloned()
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if not_mounting_yet {
|
||||
if let (Some(mounter_uid), Some(mountee_uid)) = (
|
||||
state.ecs().uid_from_entity(mounter),
|
||||
state.ecs().uid_from_entity(mountee),
|
||||
) {
|
||||
state.write_component(mountee, comp::MountState::MountedBy(mounter_uid.into()));
|
||||
state.write_component(mounter, comp::Mounting(mountee_uid.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_unmount(server: &mut Server, mounter: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
let mountee_entity = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Mounting>()
|
||||
.get(mounter)
|
||||
.and_then(|mountee| state.ecs().entity_from_uid(mountee.0.into()));
|
||||
if let Some(mountee_entity) = mountee_entity {
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::MountState>()
|
||||
.get_mut(mountee_entity)
|
||||
.map(|ms| *ms = comp::MountState::Unmounted);
|
||||
}
|
||||
state.delete_component::<comp::Mounting>(mounter);
|
||||
}
|
||||
|
||||
pub fn handle_possess(server: &Server, possessor_uid: Uid, possesse_uid: Uid) {
|
||||
let state = &server.state;
|
||||
let ecs = state.ecs();
|
||||
if let (Some(possessor), Some(possesse)) = (
|
||||
ecs.entity_from_uid(possessor_uid.into()),
|
||||
ecs.entity_from_uid(possesse_uid.into()),
|
||||
) {
|
||||
// You can't possess other players
|
||||
let mut clients = ecs.write_storage::<Client>();
|
||||
if clients.get_mut(possesse).is_none() {
|
||||
if let Some(mut client) = clients.remove(possessor) {
|
||||
client.notify(ServerMsg::SetPlayerEntity(possesse_uid.into()));
|
||||
clients.insert(possesse, client).err().map(|e| {
|
||||
error!(
|
||||
"Error inserting client component during possession: {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
// Create inventory if it doesn't exist
|
||||
{
|
||||
let mut inventories = ecs.write_storage::<comp::Inventory>();
|
||||
if let Some(inventory) = inventories.get_mut(possesse) {
|
||||
inventory.push(assets::load_expect_cloned("common.items.debug.possess"));
|
||||
} else {
|
||||
inventories
|
||||
.insert(possesse, comp::Inventory {
|
||||
slots: vec![
|
||||
Some(assets::load_expect_cloned("common.items.debug.possess")),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
],
|
||||
})
|
||||
.err()
|
||||
.map(|e| {
|
||||
error!(
|
||||
"Error inserting inventory component during possession: {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
ecs.write_storage::<comp::InventoryUpdate>()
|
||||
.insert(
|
||||
possesse,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Possession),
|
||||
)
|
||||
.err()
|
||||
.map(|e| {
|
||||
error!(
|
||||
"Error inserting inventory update component during possession: {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
// Move player component
|
||||
{
|
||||
let mut players = ecs.write_storage::<comp::Player>();
|
||||
if let Some(player) = players.remove(possessor) {
|
||||
players.insert(possesse, player).err().map(|e| {
|
||||
error!(
|
||||
"Error inserting player component during possession: {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Transfer region subscription
|
||||
{
|
||||
let mut subscriptions = ecs.write_storage::<RegionSubscription>();
|
||||
if let Some(s) = subscriptions.remove(possessor) {
|
||||
subscriptions.insert(possesse, s).err().map(|e| {
|
||||
error!(
|
||||
"Error inserting subscription component during possession: {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Remove will of the entity
|
||||
ecs.write_storage::<comp::Agent>().remove(possesse);
|
||||
// Reset controller of former shell
|
||||
ecs.write_storage::<comp::Controller>()
|
||||
.get_mut(possessor)
|
||||
.map(|c| c.reset());
|
||||
// Transfer admin powers
|
||||
{
|
||||
let mut admins = ecs.write_storage::<comp::Admin>();
|
||||
if let Some(admin) = admins.remove(possessor) {
|
||||
admins.insert(possesse, admin).err().map(|e| {
|
||||
error!("Error inserting admin component during possession: {:?}", e)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Transfer waypoint
|
||||
{
|
||||
let mut waypoints = ecs.write_storage::<comp::Waypoint>();
|
||||
if let Some(waypoint) = waypoints.remove(possessor) {
|
||||
waypoints.insert(possesse, waypoint).err().map(|e| {
|
||||
error!(
|
||||
"Error inserting waypoint component during possession {:?}",
|
||||
e
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
280
server/src/events/inventory_manip.rs
Normal file
280
server/src/events/inventory_manip.rs
Normal file
@ -0,0 +1,280 @@
|
||||
use crate::{Server, StateExt};
|
||||
use common::{
|
||||
comp::{self, Pos, MAX_PICKUP_RANGE_SQR},
|
||||
sync::WorldSyncExt,
|
||||
terrain::block::Block,
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
use log::error;
|
||||
use rand::Rng;
|
||||
use specs::{join::Join, world::WorldExt, Builder, Entity as EcsEntity};
|
||||
use vek::Vec3;
|
||||
|
||||
pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::InventoryManip) {
|
||||
let state = server.state_mut();
|
||||
let mut dropped_items = Vec::new();
|
||||
|
||||
match manip {
|
||||
comp::InventoryManip::Pickup(uid) => {
|
||||
let item_entity = if let (Some((item, item_entity)), Some(inv)) = (
|
||||
state
|
||||
.ecs()
|
||||
.entity_from_uid(uid.into())
|
||||
.and_then(|item_entity| {
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Item>()
|
||||
.get_mut(item_entity)
|
||||
.map(|item| (item.clone(), item_entity))
|
||||
}),
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity),
|
||||
) {
|
||||
if within_pickup_range(
|
||||
state.ecs().read_storage::<comp::Pos>().get(entity),
|
||||
state.ecs().read_storage::<comp::Pos>().get(item_entity),
|
||||
) && inv.push(item).is_none()
|
||||
{
|
||||
Some(item_entity)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(item_entity) = item_entity {
|
||||
if let Err(err) = state.delete_entity_recorded(item_entity) {
|
||||
error!("Failed to delete picked up item entity: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Collected),
|
||||
);
|
||||
},
|
||||
|
||||
comp::InventoryManip::Collect(pos) => {
|
||||
let block = state.terrain().get(pos).ok().copied();
|
||||
|
||||
if let Some(block) = block {
|
||||
let has_inv_space = state
|
||||
.ecs()
|
||||
.read_storage::<comp::Inventory>()
|
||||
.get(entity)
|
||||
.map(|inv| !inv.is_full())
|
||||
.unwrap_or(false);
|
||||
|
||||
if !has_inv_space {
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::CollectFailed),
|
||||
);
|
||||
} else {
|
||||
if block.is_collectible() && state.try_set_block(pos, Block::empty()).is_some()
|
||||
{
|
||||
comp::Item::try_reclaim_from_block(block)
|
||||
.map(|item| state.give_item(entity, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
comp::InventoryManip::Use(slot) => {
|
||||
let item_opt = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.and_then(|inv| inv.remove(slot));
|
||||
|
||||
let mut event = comp::InventoryUpdateEvent::Used;
|
||||
|
||||
if let Some(item) = item_opt {
|
||||
match item.kind {
|
||||
comp::ItemKind::Tool { .. } => {
|
||||
if let Some(stats) =
|
||||
state.ecs().write_storage::<comp::Stats>().get_mut(entity)
|
||||
{
|
||||
// Insert old item into inventory
|
||||
if let Some(old_item) = stats.equipment.main.take() {
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(slot, old_item));
|
||||
}
|
||||
|
||||
stats.equipment.main = Some(item);
|
||||
}
|
||||
},
|
||||
comp::ItemKind::Consumable { kind, effect } => {
|
||||
event = comp::InventoryUpdateEvent::Consumed(kind);
|
||||
state.apply_effect(entity, effect);
|
||||
},
|
||||
comp::ItemKind::Utility { kind } => match kind {
|
||||
comp::item::Utility::Collar => {
|
||||
let reinsert = if let Some(pos) =
|
||||
state.read_storage::<comp::Pos>().get(entity)
|
||||
{
|
||||
if (
|
||||
&state.read_storage::<comp::Alignment>(),
|
||||
&state.read_storage::<comp::Agent>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(alignment, _)| {
|
||||
alignment == &&comp::Alignment::Owned(entity)
|
||||
})
|
||||
.count()
|
||||
>= 3
|
||||
{
|
||||
true
|
||||
} else if let Some(tameable_entity) = {
|
||||
let nearest_tameable = (
|
||||
&state.ecs().entities(),
|
||||
&state.ecs().read_storage::<comp::Pos>(),
|
||||
&state.ecs().read_storage::<comp::Alignment>(),
|
||||
)
|
||||
.join()
|
||||
.filter(|(_, wild_pos, _)| {
|
||||
wild_pos.0.distance_squared(pos.0) < 5.0f32.powf(2.0)
|
||||
})
|
||||
.filter(|(_, _, alignment)| {
|
||||
alignment == &&comp::Alignment::Wild
|
||||
})
|
||||
.min_by_key(|(_, wild_pos, _)| {
|
||||
(wild_pos.0.distance_squared(pos.0) * 100.0) as i32
|
||||
})
|
||||
.map(|(entity, _, _)| entity);
|
||||
nearest_tameable
|
||||
} {
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(tameable_entity, comp::Alignment::Owned(entity));
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(tameable_entity, comp::Agent::default());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if reinsert {
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(slot, item));
|
||||
}
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
let _ = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.insert(slot, item));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
state.write_component(entity, comp::InventoryUpdate::new(event));
|
||||
},
|
||||
|
||||
comp::InventoryManip::Swap(a, b) => {
|
||||
state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.map(|inv| inv.swap_slots(a, b));
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Swapped),
|
||||
);
|
||||
},
|
||||
|
||||
comp::InventoryManip::Drop(slot) => {
|
||||
let item = state
|
||||
.ecs()
|
||||
.write_storage::<comp::Inventory>()
|
||||
.get_mut(entity)
|
||||
.and_then(|inv| inv.remove(slot));
|
||||
|
||||
if let (Some(item), Some(pos)) =
|
||||
(item, state.ecs().read_storage::<comp::Pos>().get(entity))
|
||||
{
|
||||
dropped_items.push((
|
||||
*pos,
|
||||
state
|
||||
.ecs()
|
||||
.read_storage::<comp::Ori>()
|
||||
.get(entity)
|
||||
.copied()
|
||||
.unwrap_or(comp::Ori(Vec3::unit_y())),
|
||||
item,
|
||||
));
|
||||
}
|
||||
state.write_component(
|
||||
entity,
|
||||
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Dropped),
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
// Drop items
|
||||
for (pos, ori, item) in dropped_items {
|
||||
let vel = ori.0.normalized() * 5.0
|
||||
+ Vec3::unit_z() * 10.0
|
||||
+ Vec3::<f32>::zero().map(|_| rand::thread_rng().gen::<f32>() - 0.5) * 4.0;
|
||||
|
||||
state
|
||||
.create_object(Default::default(), comp::object::Body::Pouch)
|
||||
.with(comp::Pos(pos.0 + Vec3::unit_z() * 0.25))
|
||||
.with(item)
|
||||
.with(comp::Vel(vel))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
fn within_pickup_range(player_position: Option<&Pos>, item_position: Option<&Pos>) -> bool {
|
||||
match (player_position, item_position) {
|
||||
(Some(ppos), Some(ipos)) => ppos.0.distance_squared(ipos.0) < MAX_PICKUP_RANGE_SQR,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::comp::Pos;
|
||||
use vek::Vec3;
|
||||
|
||||
#[test]
|
||||
fn pickup_distance_within_range() {
|
||||
let player_position = Pos(Vec3::zero());
|
||||
let item_position = Pos(Vec3::one());
|
||||
|
||||
assert_eq!(
|
||||
within_pickup_range(Some(&player_position), Some(&item_position)),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pickup_distance_not_within_range() {
|
||||
let player_position = Pos(Vec3::zero());
|
||||
let item_position = Pos(Vec3::one() * 500.0);
|
||||
|
||||
assert_eq!(
|
||||
within_pickup_range(Some(&player_position), Some(&item_position)),
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
109
server/src/events/mod.rs
Normal file
109
server/src/events/mod.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use crate::Server;
|
||||
use common::event::{EventBus, ServerEvent};
|
||||
use entity_creation::{
|
||||
handle_create_character, handle_create_npc, handle_create_waypoint, handle_shoot,
|
||||
};
|
||||
use entity_manipulation::{
|
||||
handle_damage, handle_destroy, handle_explosion, handle_land_on_ground, handle_respawn,
|
||||
};
|
||||
use interaction::{handle_mount, handle_possess, handle_unmount};
|
||||
use inventory_manip::handle_inventory;
|
||||
use player::{handle_client_disconnect, handle_exit_ingame};
|
||||
use specs::{Entity as EcsEntity, WorldExt};
|
||||
|
||||
mod entity_creation;
|
||||
mod entity_manipulation;
|
||||
mod interaction;
|
||||
mod inventory_manip;
|
||||
mod player;
|
||||
|
||||
pub enum Event {
|
||||
ClientConnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
ClientDisconnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
Chat {
|
||||
entity: Option<EcsEntity>,
|
||||
msg: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn handle_events(&mut self) -> Vec<Event> {
|
||||
let mut frontend_events = Vec::new();
|
||||
|
||||
let mut requested_chunks = Vec::new();
|
||||
let mut chat_commands = Vec::new();
|
||||
|
||||
let events = self
|
||||
.state
|
||||
.ecs()
|
||||
.read_resource::<EventBus<ServerEvent>>()
|
||||
.recv_all();
|
||||
|
||||
for event in events {
|
||||
match event {
|
||||
ServerEvent::Explosion { pos, radius } => handle_explosion(&self, pos, radius),
|
||||
ServerEvent::Shoot {
|
||||
entity,
|
||||
dir,
|
||||
body,
|
||||
light,
|
||||
projectile,
|
||||
gravity,
|
||||
} => handle_shoot(self, entity, dir, body, light, projectile, gravity),
|
||||
ServerEvent::Damage { uid, change } => handle_damage(&self, uid, change),
|
||||
ServerEvent::Destroy { entity, cause } => handle_destroy(self, entity, cause),
|
||||
ServerEvent::InventoryManip(entity, manip) => handle_inventory(self, entity, manip),
|
||||
ServerEvent::Respawn(entity) => handle_respawn(&self, entity),
|
||||
ServerEvent::LandOnGround { entity, vel } => {
|
||||
handle_land_on_ground(&self, entity, vel)
|
||||
},
|
||||
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
||||
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||
handle_possess(&self, possessor_uid, possesse_uid)
|
||||
},
|
||||
ServerEvent::CreateCharacter {
|
||||
entity,
|
||||
name,
|
||||
body,
|
||||
main,
|
||||
} => handle_create_character(self, entity, name, body, main),
|
||||
ServerEvent::ExitIngame { entity } => handle_exit_ingame(self, entity),
|
||||
ServerEvent::CreateNpc {
|
||||
pos,
|
||||
stats,
|
||||
body,
|
||||
agent,
|
||||
alignment,
|
||||
scale,
|
||||
} => handle_create_npc(self, pos, stats, body, agent, alignment, scale),
|
||||
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
frontend_events.push(handle_client_disconnect(self, entity))
|
||||
},
|
||||
|
||||
ServerEvent::ChunkRequest(entity, key) => {
|
||||
requested_chunks.push((entity, key));
|
||||
},
|
||||
ServerEvent::ChatCmd(entity, cmd) => {
|
||||
chat_commands.push((entity, cmd));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Generate requested chunks.
|
||||
for (entity, key) in requested_chunks {
|
||||
self.generate_chunk(entity, key);
|
||||
}
|
||||
|
||||
for (entity, cmd) in chat_commands {
|
||||
self.process_chat_cmd(entity, cmd);
|
||||
}
|
||||
|
||||
frontend_events
|
||||
}
|
||||
}
|
77
server/src/events/player.rs
Normal file
77
server/src/events/player.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use super::Event;
|
||||
use crate::{auth_provider::AuthProvider, client::Client, state_ext::StateExt, Server};
|
||||
use common::{
|
||||
comp,
|
||||
comp::Player,
|
||||
msg::{ClientState, PlayerListUpdate, ServerMsg},
|
||||
sync::{Uid, UidAllocator},
|
||||
};
|
||||
use log::error;
|
||||
use specs::{saveload::MarkerAllocator, Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
|
||||
pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
||||
let state = server.state_mut();
|
||||
|
||||
// Create new entity with just `Client`, `Uid`, and `Player` components
|
||||
// Easier than checking and removing all other known components
|
||||
// Note: If other `ServerEvent`s are referring to this entity they will be
|
||||
// disrupted
|
||||
let maybe_client = state.ecs().write_storage::<Client>().remove(entity);
|
||||
let maybe_uid = state.read_component_cloned::<Uid>(entity);
|
||||
let maybe_player = state.ecs().write_storage::<comp::Player>().remove(entity);
|
||||
if let (Some(mut client), Some(uid), Some(player)) = (maybe_client, maybe_uid, maybe_player) {
|
||||
// Tell client its request was successful
|
||||
client.allow_state(ClientState::Registered);
|
||||
// Tell client to clear out other entities and its own components
|
||||
client.notify(ServerMsg::ExitIngameCleanup);
|
||||
|
||||
let entity_builder = state.ecs_mut().create_entity().with(client).with(player);
|
||||
// Ensure UidAllocator maps this uid to the new entity
|
||||
let uid = entity_builder
|
||||
.world
|
||||
.write_resource::<UidAllocator>()
|
||||
.allocate(entity_builder.entity, Some(uid.into()));
|
||||
entity_builder.with(uid).build();
|
||||
}
|
||||
// Delete old entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete entity when removing character: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event {
|
||||
let state = server.state_mut();
|
||||
|
||||
// Tell other clients to remove from player list
|
||||
if let (Some(uid), Some(_)) = (
|
||||
state.read_storage::<Uid>().get(entity),
|
||||
state.read_storage::<comp::Player>().get(entity),
|
||||
) {
|
||||
state.notify_registered_clients(ServerMsg::PlayerListUpdate(PlayerListUpdate::Remove(
|
||||
(*uid).into(),
|
||||
)))
|
||||
}
|
||||
|
||||
// Make sure to remove the player from the logged in list. (See AuthProvider)
|
||||
// And send a disconnected message
|
||||
{
|
||||
let players = state.ecs().read_storage::<Player>();
|
||||
let mut accounts = state.ecs().write_resource::<AuthProvider>();
|
||||
let mut clients = state.ecs().write_storage::<Client>();
|
||||
|
||||
if let Some(player) = players.get(entity) {
|
||||
accounts.logout(player.uuid());
|
||||
|
||||
let msg = ServerMsg::broadcast(format!("{} went offline.", &player.alias));
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Delete client entity
|
||||
if let Err(err) = state.delete_entity_recorded(entity) {
|
||||
error!("Failed to delete disconnected client: {:?}", err);
|
||||
}
|
||||
|
||||
Event::ClientDisconnected { entity }
|
||||
}
|
1046
server/src/lib.rs
1046
server/src/lib.rs
File diff suppressed because it is too large
Load Diff
@ -10,12 +10,12 @@ const DEFAULT_WORLD_SEED: u32 = 5284;
|
||||
pub struct ServerSettings {
|
||||
pub gameserver_address: SocketAddr,
|
||||
pub metrics_address: SocketAddr,
|
||||
pub auth_server_address: Option<String>,
|
||||
pub max_players: usize,
|
||||
pub world_seed: u32,
|
||||
//pub pvp_enabled: bool,
|
||||
pub server_name: String,
|
||||
pub server_description: String,
|
||||
//pub login_server: whatever
|
||||
pub start_time: f64,
|
||||
pub admins: Vec<String>,
|
||||
/// When set to None, loads the default map file (if available); otherwise,
|
||||
@ -28,6 +28,7 @@ impl Default for ServerSettings {
|
||||
Self {
|
||||
gameserver_address: SocketAddr::from(([0; 4], 14004)),
|
||||
metrics_address: SocketAddr::from(([0; 4], 14005)),
|
||||
auth_server_address: Some("https://auth.veloren.net".into()),
|
||||
world_seed: DEFAULT_WORLD_SEED,
|
||||
server_name: "Veloren Alpha".to_owned(),
|
||||
server_description: "This is the best Veloren server.".to_owned(),
|
||||
@ -107,6 +108,7 @@ impl ServerSettings {
|
||||
[127, 0, 0, 1],
|
||||
pick_unused_port().expect("Failed to find unused port!"),
|
||||
)),
|
||||
auth_server_address: None,
|
||||
// If loading the default map file, make sure the seed is also default.
|
||||
world_seed: if load.map_file.is_some() {
|
||||
load.world_seed
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user