Veloren: An Owner's Manual

This manual is the primary source of documentation for Veloren, both for users and for developers. It aims to document the features of Veloren, the manner in which it is developed, and describe the inner workings of both the game and the engine. It also aims to discuss future plans the development team has for Veloren.

Image of Veloren | Taken by @Piedro0

Image of Veloren | Taken by @Pfau

Image of Veloren | Taken by @Pfau

What is Veloren?

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.

Beautiful town | Taken by @Pfau

Veloren is fully open-source, licensed under GPL 3. It uses original graphics, musics and other assets created by its community. Being contributor-driven, its development community and user community is one and the same: developers, players, artists and musicians come together to develop the game.

What status is the project currently in?

After rewriting the engine from scratch (old game can be found here) we're now at a stage where multiple features are introduced quite rapidly.

Who develops Veloren?

Veloren is developed by a community of volunteers that have one unifying aim: creating a bright, colourful, fun world that can be explored and interacted with in unique and interesting ways.

Website

The Veloren website contains weekly blog updates, developer information and download links. You can find it at www.veloren.net

Discord

The Veloren community (both developers and players) are most active on the Veloren Discord server. You can join the server using the link below:

https://discord.gg/BvQuGze

Social Media

We're on Youtube and Reddit and don't forget to follow us on Twitter.

Download

Visit veloren.net to find all available options.

Veloren for Players

Thank you for your interest in playing the Game!

If you want to kickstart your playthrough you can read through the Getting started guide. As the game often updates several times a day we currently recommend using Airshipper the official Veloren launcher.

Much fun playing the game!

Veloren for Players

This section of the book will explain basic game concepts for players and general tooling.

Note: Please keep in mind that Veloren is not even in alpha yet and gameplay can't be considered fully fledged out. Some gaming aspects mentioned are still heavily in development.

Reporting Bugs

  1. First thing to do will be to check our gitlab issues if this bug is already known.
  2. If not create an issue with the Bug template and try to describe the bug as good as possible, include screenshots if needed.
  3. Upload the log file for additional information which can help us identify the issue. See below how to get logs. (Note: make sure no sensitive information is included in the log files)
  4. Submit the issue

Tip: Incase you do not want to create an gitlab account you can join our discord and report the bug in #bugs-and-support.

Collect Logs

If you encounter problems with Veloren, we might ask you for logs or a trace. This tutorial shows you how to collect the logs, depending on your operating system, and the way you have installed Veloren.

By default Veloren server and Voxygen will both produce logs. They are printed in the terminal/cmd and to a file, called voxygen.log.<todays_date>. It even prints where the file is located to terminal/cmd:

Nov 25 01:40:14.388  INFO veloren_voxygen::logging: Setup terminal and file logging. logdir="/mnt/games/cargo-build/debug/userdata/voxygen/logs"

By default the granularity is INFO, but please provide logs on TRACE level (as shown below). Search for a message called Tracing is successfully set to TRACE to verify TRACE level is enabled.

Linux and MacOS

Airshipper

  1. Start airshipper with -vv argument.
  2. When the game starts it will print to the terminal the location of the log file. Check Airshipper page.

Compiled

  1. Start voxygen with TRACE level in terminal:
    RUST_LOG="trace" ./target/debug/veloren-voxygen
    # or RUST_LOG="trace" cargo run
    
  2. Copy trace from terminal or the log file mentioned above.

Windows

Airshipper

  1. Opening a CMD.

    On Windows press Windows key + R. Then type cmd and hit enter.

  2. Type airshipper run -vv and hit enter.
  3. Run the game (till you encounter the problem).
  4. The logs should be located in %Appdata%/airshipper/profiles/default/userdata/voxygen/logs Or check Airshipper page.

Compiled

Git Bash

-> See Linux/Compiled above

Cmd
  1. Open a CMD.
  2. Go to your veloren folder with the cd command, e.g. cd C:\Users\<Your Username>\Desktop\veloren.
  3. Write set RUST_LOG=trace&& veloren-voxygen.exe and hit enter (exactly like here, without whitespace before &&)
  4. The logs will now be printed to the CMD and the folder userdata\voxygen\logs or %appdata%\veloren\.

Changelog

The changelog can be found in the repository.

Roadmap

The Roadmap is currently work in progress.

If you are planning on playing multiplayer, make sure you register at account.veloren.net before connecting to an auth-enabled server. Otherwise, things remain mostly the same for both singleplayer and multiplayer. Keep in mind that every aspect of the game is subject to change, and this guide may not be 100% up to date when you read it.

Character Creation

character creation

Welcome to the character creation screen! Currently only aesthetic choices are shown; eventually this interface will be expanded to contain lore on each species, guidance on chosen weapons, and starting stats of each species. As of now, however, your choice of species matters little.

There are currently six playable species in Veloren; Human, Orc, Dwarf, Elf, Undead, and Danari. The Danari are the only species you won’t be familiar with; they’re a special species created for the game.

As well as choosing your species, here you choose your starting weapon. There are no species restrictions on weapons, and choosing your starter weapon does not lock you into that choice; as you play you’ll find weapons of the other types and can switch to those with no penalty.

You can set your hair style and colour, skin colour, eye details, eye colour, and - only available to some species - accessories. Use the randomize button (the dice icon) to randomize your character's appearance and name.

When you’ve finished creating your character, enter a name, and then click Create. Choose your character from the list, click Enter World, and you’ll enter the game world.

Getting Started

getting started

You’ll enter the world in a little village nestled at the foot of a mountain. This is the default world and will be the same for every character you make. This is a pre-generated world; it is possible to generate your own world with a new seed, but this process takes a long time, and so this current world has been pre-generated for your convenience.

Look around your interface. At the top left you’ll see that you can press F1 to show keybinds; have a look at this. On the bottom right, you’ll see a few icons and their corresponding keybinds.

  • P - Opens spells (not currently implemented)
  • M - Opens the map (this isn’t the final map; the map system is being reworked)
  • O - Opens your social tab
  • N - Opens your settings
  • B - Opens your bag / inventory
  • C - Opens the crafting menu

You can change most key bindings in the settings.

Movement follows the conventions of other similar games, but you will want to know how to pull out your glider (Shift) and how to climb vertical surfaces (hold Space).

Inventory and Items

Press B to open your inventory.

Items in your inventory can be used by right-clicking them, which usually either equips it on your character, swapping with whatever gear was in its slot, or consumes the item, applying its effects.

Inventory slots can be assigned to hotbar slots by clicking and dragging the item stored in the slot to the hotbar. Any items in that inventory slot will then appear in the assigned hotbar slot. Hotbar slots can be reassigned by dragging an item in a different inventory slot to it. Using an item from a hotbar slot has the same effect as right-clicking it from the inventory.

managing your inventory

Here, 7 will use the Dwarven cheese, but if I run out of cheese and another item takes its slot in the inventory, for example, 7 will use that item instead.

Depending on your equipped weapon, some hotbar slots will be reserved for abilities.

If your inventory is getting cluttered or full, you can rearrange the items by dragging them around, or throw them away by dragging them outside the inventory window. Items you throw away can be picked back up as long as you stay near them.

There are many items to collect, and they come in the form of tools, crafting materials, gear, and consumables.

Gear items are items that can be equipped on your character. They can be crafted from materials or looted from enemies and chests and come in several rarities. Gear is one of the main ways to progress your character.

Consumables are items that are, well, consumed on use. They come in several varieties, the most prominent of which are food and potions. Food affects you at a certain rate for its duration while potions take effect instantaneously.

a mushroom

If you look carefully you may notice certain objects in the world will highlight when you move your crosshair near them. This indicates you can interact with them (E by default), which will usually give you an item.

Crafting

the crafting menu

You can open the crafting menu with the C key. You'll see a list of craftable items, some of which may be greyed out - that means you have insufficient materials or tools to craft them. Items can be crafted with materials either gathered from the world or dropped by enemies. Food items can be crafted into even more effective foods, and some basic weapons and armor can also be crafted.

The fireworks are for celebratory use only. Ignite responsibly.

Weapons And Combat

a challenging encounter

As you wander the world of Veloren, you'll encounter all manner of animals, monsters, NPCs, and maybe even players. In general, predatory animals and monsters will attack you on sight, while docile creatures and NPCs will ignore you unless attacked.

Each weapon has a left-click basic attack and one or more abilites on right click and the hotbar. Each weapon has its own playstyle, and serve to replace conventional 'classes.'

Using more than one weapon by equipping it to the 'offhand' will increase your combat options immensely! Switch to your offhand weapon by pressing Tab.

Dodging enemy attacks is important. Middle-click will swiftly roll you out of the way. Use sideways movement to avoid ranged attacks while closing in or returning fire. If you use ranged weapons against a melee attacker, try to keep your distance.

Use a training dummy (found in towns) to familiarize yourself with your weapons' attacks and abilities.

character progression

Defeating enemies earns you experience, which allows you to level up your character.

Getting levels allows you to get skill points to spend in a skill tree. There is currently one skill tree for each weapon, and a general combat skill tree.

general combat skill tree

The general combat gives access to some general skills, as well as unlocking weapon-specific skill trees.

bow skill tree

When you earn X experience points, they go into the following skill tree experience bar:

  • X for general combat
  • X/2 for main weapon, if you've unlocked the corresponding skill tree
  • X/2 for secondary weapon, if you've unlocked the corresponding skill tree

So, if you get 4 XP when killing something with the bow equipped as main weapon, the sword equipped at secondary, both unlocked, you'll get 4 XP in general combat, 2 in bow, 2 in sword.

If you have two bows equipped, you'll have 4 XP in the bow skill tree. If you have one bow as main weapon and nothing as secondary, you'll also get 4 XP in the bow skill tree.

Taming

In the course of playing the game, you'll be able to craft Collars, which let you tame creatures. The current maximum number of creatures you can have tamed at once is three. You can only tame non-hostile creatures. Choose carefully!

Tamed creatures will follow you around and will attack anything that attacks you. They can be healed with a healing sceptre.

Due to pathfinding issues, pets may have difficulty keeping up with you when you go off gliding or delve into dungeons.

Currently, pets do not persist through logins. Don't get too attached.

Map

map

Access the map by pressing M. You'll see icons representing dungeons, towns, and cave entrances along with brown lines denoting roads. The difficulty level of a dungeon is represented by the number of dots that appear above the icon. Your current location is initially at the center of the screen, but the map can be shifted around by clicking and dragging. The mouse wheel zooms in and out. There is also a minimap seen in regular play at the top right.

Campfires

campfire

Campfires are interspersed across the world, marked by tall pillars of smoke. Approaching a campfire will show the message ‘Waypoint Saved’. When you die, you will respawn at the last campfire that saved your waypoint. Your waypoint persists through logins, so you can start each session approximately where you left off!

Sitting near a campfire (with the K key) will heal you over time. If you ever need respite from your travels, campfires are what you should look for!

Gliding

glider

Getting a hang on gliders may cost you a few broken bones, but once you get a feel for it you may find yourself looking for any excuse to explore a high place.

Pre-flight

Pressing the Shift key will equip the glider. You can run around with it equipped, but you won’t be able to roll or draw your weapon at the same time.

You're going to need a steep slope or a cliff with enough vertical margin that you have time to redirect your fall into a glide. If you need to jump for some extra initial elevation it is recommended that you deploy the glider mid-jump, or else it will push against you on the way up. Another trick is to roll-jump for an extra initial boost.

Take-off

Once airborn, you probably want to let go of any movement keys, as you'll mainly control the glider by looking in the direction you want to orient it towards. The controls used for movement on the ground are used for quickly adjusting pitch and direction while gliding.

Gliding in Veloren uses a simulated model of aerodynamics; the only acceleration you get is from falling due to gravity, and your only control mechanism is angling the glider against the wind in a way such that the produced lift and drag pushes you in the desired direction. These forces scale with the square of airflow velocity relative to you, so gaining and maintaining speed is important.

Angle of attack

The angle of attack is your pitch relative to the direction of air flow. It's zero when the glider is perfectly aligned with the wind, positive when pitched up and negative when pitched down. By default (when there is no control input) the glider is slightly pitched up relative to the direction you're looking to make you glide most efficiently (in terms of distance) while looking straight into the wind.

You get the best performance out of small angles of attack, so pitch and swoop with smooth, minute movements. Pitching too much will cause you to stall; an effect which causes the lift produced to drop sharply, putting you at risk of losing too much speed due to drag with little ability to regain control.

Hints

  • A controlled stall can be used to great effect for braking, but if you overdo it you may find yourself gliding backwards (or not at all).
  • Look out for cliffs or steep inclines and pitch down to swoop into a glide.
  • Lift scales linearly with angle of attack (for small angles), but the ("induced") drag scales exponentially. Balancing lift and drag is key to controlling your velocity, elevation and ultimately gliding distance.

Dungeons

Dungeons are marked on the map with a 'ruins' icon, along with a difficulty level. Dungeons with difficulties 0-3 are doable by a solo player; above that, you may want to find a group.

On entering the dungeon you’ll descend a long, spiral staircase that will eventually exit at the first dungeon level, deep below ground. Each level has a series of corridors and rooms containing hostile enemies and treasure chests. The most rare and powerful loot in the game can be obtained from dungeons!

dungeon internals

Dungeons are dark - equip your lantern by pressing the G key!

Make sure you’re well stocked up on healing items and have some levels under your belt before attempting your first dungeon!

Caves

Caves are marked with cave entrance icons on the map.

cave mouth

Several resources can be found in caves, some of which are exclusive to them. Are you prepared to plumb the depths, adventurer?

Grouping Up

When playing in a multiplayer server, you may wish to team up with other players in order to conquer content without losing track of or hurting each other. The grouping function is your friend here. To invite other players to your group, press O to open the player list, click on the character you want to invite, and press Invite. Hovering over the character names will show you the player's account name.

player menu and grouping up

Additional Hints For Beginners

Houses can contain useful items and equipment for you to pick up!

You might want to practice combat with small animals first; attacking denizens will get you killed quickly. Consider instead what help they may offer you in a time of need!

Once you have enough leather scraps you can craft yourself some initial armor. Look for Apples or Mushrooms and Twigs (common in moderate forests). Combine them in the crafting menu to get Apple/Mushroom Sticks which give superior healing.

Nighttime can make it easier to spot campfires. If you're alone in the night, light sources are likely indications of safety.

You may have noticed that you're able to crouch with Ctrl. This is more than just an animation! If a threat is in your path, consider sneaking around it. Once you draw your weapon, though, the jig's up.

Useful Commands

/home - Teleports you to the default spawn and resets your waypoint. This is your best option if the map has changed and you enter the game stuck in solid ground.

/w - global chat

/r - regional chat

/g - group chat

/t - direct chat

Admin Only

/give_item [filepath] [amount] - Gives you items. Use Tab to autocomplete.

/tp [player] - Teleports you to a player.

/spawn [argument 1] [argument 2] - Press Tab to cycle through options. Allows you to spawn entities.

/goto [x,y,z] - Teleports you to the specified coordinates.

/debug - Gives you some special equipment ;)

Airshipper

Airshipper

Airshipper is a cross-platform Veloren launcher taking care of keeping Veloren up to date. Currently due to our irregular and frequent updates we recommend using it.

Download

Visit the website of the developer to download it.

Files

Logs, screenshots, assets will all be located in the respective profile depending on your operating system.

OSPath
Windows%appdata%/airshipper
Linux~/.local/share/airshipper
MacOS~/Library/Application Support/airshipper

Troubleshooting

Incase airshipper does not open or display correct you can use the cli by

  1. Opening a terminal

    On Windows press Windows key + R. Then type cmd and hit enter.

  2. Type airshipper run and hit enter

  3. Enjoy the game.

Userdata Folder Structure

The userdata folder unifies all player and server configuration in a single place, which should be transferrable between different Veloren installations from v0.8 onwards.

The folder will be next to your Veloren executable, or in your repository's root if self-compiling. See where Airshipper stores files if using the launcher.

Airshipper currently stores the items under the voxygen heading outside of userdata, but this will change in the future.

settings.ron (voxygen edition)

Contains all of the settings accessible from the in-game GUI, and usually shouldn't require manual intervention.

The main thing not accessible from main game menus is gamepad keybindings, which can only be changed by directly editing the file.

profile.ron

Contains hotbar infomation, per-character and per-server. Should never need to be manually modified.

settings.ron (server edition)

This file is intended for manual editing, and should never be overwritten by the game. If the file is in an invalid state, the server will emit a warning in including the position of the error, create a settings.template.ron file full of the default values, and start up with all default values.

SettingDescriptionDefault value
gameserver_addressAddress and port the game server will listen to. Note that clients will use the port 14004 by default. Changing the port will require to specify it in the client too."0.0.0.0:14004"
metrics_addressAddress and port the game server will expose prometheus metrics."0.0.0.0:14005"
auth_server_addressWhen using Some(<value>): The value is the IP address or domain the game server and client will use. If you want to disable authentication, you replace Some(...) with None.Some("https://auth.veloren.net")
max_playersMaximum number of players connected to the game server.100
world_seedseed number used to setup the random generation of the world.59686
server_namedisplayed server name"Veloren Alpha"
start_timeServer daylight start time in seconds.32400
map_fileSets which map to load. See here for allowed values.None
max_view_distanceThe maximum view distance that clients may request. Useful for low-RAM servers.Some(30)
banned_words_filesList of files containing words to be censored. None are distributed by default.[] (Empty array)
max_player_group_sizeThe maximum party size players can have, for purposes of XP sharing and ignoring friendly fire.6
client_timeout(secs: 40, nanos: 0,)

description.ron

Contains the introductory chat message clients get when entering the server, as a quoted string. Can be multiple lines.

Example:

"This is the best Veloren server"

whitelist.ron

Contains a list of whitelisted account IDs, and is considered disabled if empty. Heavily recommended to use the /whitelist add/remove ingame command, rather than manual editing.

Example: Result of using /whitelist add Treeco and /whitelist add treeco2.

[
    "6f15b915-074f-f78d-df88-34fb33e4e13f",
    "3445349e-d03c-64bf-6ecf-a15806275a1f",
]

banlist.ron

Contains a list of banned accounts, and reasons. Heavily recommended to use the /ban and /unban ingame commands, rather than manual editing.

Example: Result of using /ban Treeco General nuisance and /ban treeco2 alt account.

{
    "6f15b915-074f-f78d-df88-34fb33e4e13f": (
        username_when_banned: "treeco2",
        reason: "alt account",
    ),
    "3445349e-d03c-64bf-6ecf-a15806275a1f": (
        username_when_banned: "Treeco",
        reason: "General nuisance",
    ),
}

admins.ron

Contains a list of admin account IDs. Heavily recommended to use admin add/remove from the server's TUI, rather than manual editing. There is no in-game command to permanently add admins, for security reasons.

Example: Result of using admin add Treeco.

[
    "ee193d08-8f5a-4862-a279-1a8c4bd357f3",
]

If you have the TUI disabled or are otherwise unable to use it, you can instead use the server CLI to add/remove admins.

Example:

veloren-server-cli admin add Treeco

settings.ron (server-cli edition)

The settings in this file govern the warning period the server gives for automatic shutdowns for updates.

Hosting a Server

This section will describe how to setup a Veloren server.

Note: There are multiple ways to setup a Veloren server therefore this book explains the most common and easiest approaches.

Veloren is composed of the Veloren client (veloren-voxygen), the game server (veloren-server-cli), and the authentication server (auth-server).

When setup with authentication support (default), the game server is using the authentication server to log the player in.

Note: Without authentication the server will allow anyone to log in with any name including those with admin access!

Setup local game server

If you want to play with your friends and do not have a dedicated server follow these instructions.

Note: You will need access to the router and knowledge about port forwarding.

  1. Setup
    1. Port forward 14004 on your router.
  2. Launch game server veloren-server-cli(.exe)
    • check Airshipper section to find out where the files are
    • or download Nightly from the website.

      Note: The assets folder is required to be next to the game server.

  3. Give out your IP
  4. Optionally disable authentication by replacing auth_server_address: Some(...) with auth_server_address: None

Enjoy the game!

Setup dedicated game server

If you want to run a dedicated Veloren server 24/7 follow this.

Note: You will need access to the server, docker, docker-compose installed and we assume general command line and docker-compose knowledge.

  1. Setup
    1. Create folder /opt/veloren-server (feel free to name it differently).
    2. Copy docker-compose.yml from the repository into the folder.
    3. If needed open port 14004 (14005 for metrics) in your firewall.
  2. Start it
    1. Run docker-compose up -d as root.
    2. View logs with sudo docker logs veloren-game-server-master.

You are done!

Note: This will automatically keep the game server updated to the latest nightly release.

Building the Veloren Server for Raspberry Pi

The model of Raspberry Pi you have and the operating system your Pi is running can significantly impact your installation steps and Veloren's performance on your Pi. For the best performance we recommend using the Raspberry Pi model 4 and a 64-bit OS like Ubuntu Server, which is available through the Raspberry Pi Imaging utility. There is a 64-bit beta of Raspberry Pi OS as well.

Note: The amount of RAM on the Pi 4 does not matter. Even the official Veloren server uses well under 2GB of memory most of the time.

Cross-compiling or not

Cross-compilation means setting up a toolchain for the Raspberry Pi's instruction set on a separate computer in order to compile the Veloren server binary which you then copy onto the Pi.

Direct compiling is preferred if:

  • You don't want to install and set up as many things on your computer
  • You want to be up and running in fewer steps

Cross compiling is preferred if:

  • You have a computer setup for development
  • You want the compilation step to be faster

Note: Even with cross-compilation it is necessary to clone the assets folder from the code repository onto the Pi as these files are not compiled into the server binary.

Direct Compiling

Log in to your Pi using ssh.
ssh <username>@<ip> and enter password Install the Rust programming language on the Pi if it's not already installed.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Install Git LFS in order to download the audio and visual assets along with the source code.

sudo apt install git-lfs

Git clone the Veloren codebase. You may need to generate SSH keys on your Pi for your gitlab account.

git clone https://gitlab.com/veloren/veloren.git

Compile the server binary from the code in the Veloren directory.

cd veloren
cargo build --bin veloren-server-cli --release

Run the server binary, optionally with -h or --help to see the list of arguments you can supply.

./target/release/veloren-server-cli

Note: Compilation on a Raspberry Pi 4 running Ubuntu Server 64-bit can take around 30 minutes for an optimized build.
Note: Remember to replace <username> by your own username and <ip> by the ip address of your Raspberry Pi!

Cross Compiling

Installing Dependencies

Install the following dependencies on the cross compiling machine and the Raspberry Pi:

Cross Compiling PC:

  • git
  • git-lfs
  • cargo (which is installed along with Rust)
  • docker

Raspberry Pi:

  • git
  • git-lfs

Preparing the Raspberry Pi

First we need to clone the Veloren git repository.

git clone https://gitlab.com/veloren/veloren

Next we can reset the repository to the state of the last stable release. To do that we have to find out the git hash of that version. This can be found on https://gitlab.com/veloren/veloren/-/releases. The git hash is the sequence in the bottom left corner of the release. This step can be skipped if you want to play with the very latest updates.

cd veloren
git checkout <git hash>

Next we have to create some folders to later put the binary in.

mkdir target
mkdir target/release

Next, we need to add an environment variable to our .profile.

nano ~/.profile

In the last line of this file, add the line export VELOREN_USERDATA="$(pwd)/userdata

Lastly, we reload our environment to use the new variable

. ~/.profile

Cross Compiling the code

Again we have to clone the Veloren repository to our cross compiling machine, reset it to the state of the latest stable release and setup git-lfs.

git clone https://gitlab.com/veloren/veloren
cd veloren
git checkout <git hash>
git lfs install
cargo install cross

The install command will be different depending on which OS you have running on the Raspberry Pi.
If you are using a 64-bit OS like Ubuntu Server with an ARMv8 instruction set, run the command:

cross build --target aarch64-unknown-linux-gnu --bin veloren-server-cli --release

If you are using Raspberry Pi OS which is currently 32-bit and using ARMv7 instruction set for backwards compatibility reasons, run the command:

cross build --target armv7-unknown-linux-gnueabihf --bin veloren-server-cli --release

Note: You may need to register an account on docker hub and run the docker login command in the terminal to access the docker image required for cross-compilation.

When the compilation process has finished, we can move the binary to the Raspberry Pi. If you have ssh enabled on your Raspberry Pi, you can use the scp command.

scp target/<instruction set>/release/veloren-server-cli <username>@<ip>:~/veloren/target/release/veloren-server-cli

Note: <instruction set> refers to either aarch64-unknown-linux-gnu or armv7-unknown-linux-gnueabihf, whichever one you used to compile. Remember to replace <username> by your own username and <ip> by the ip address of your Raspberry Pi!

Creating a systemd service

On the Raspberry Pi we can now create a systemd service for the server. To do that, create a service file with the content below.

sudo nano /etc/systemd/system/veloren-server.service

Note: If you cross-compiled the binary, add an environment variable to the Service section: Environment="VELOREN_USERDATA=/home/veloren/target/release/userdata"

[Unit]
Description=Veloren Server
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
User=<username>
WorkingDirectory=/home/<username>/veloren
ExecStart=/home/<username>/veloren/target/release/./veloren-server-cli

[Install]
WantedBy=multi-user.target

Now our service is ready and can be started.

sudo systemctl start veloren-server.service

To watch how the server starts and to see debug info, you can use the following command.

sudo journalctl -f -u veloren-server.service

In case you want to start the server everytime the Raspberry Pi boots, you can enable the service.

sudo systemctl enable veloren-server.service

Monitoring the server

When the server is running, you can watch your Pi's performance with tools like htop or set up a Prometheus server to scrape metrics from your Veloren server at the addresshttp://<ip>:14005/metrics.

Generating a Custom World

  1. Open your singleplayer or server's settings file. See here.

  2. Set your custom world_seed, and map_file to Some(Save).

  3. Launch your game as normal, whether you're doing it through singleplayer or the server cli.

  4. The generation process can take a significant length of time, with little indication that it's running properly. 10 minutes on a good CPU is expected, for standard-sized worlds. Eventually, it will load into the new world.

  5. The world will be saved in a maps folder, as a binary file. Set map_file to Some(Load("maps/<filename>.bin")),, else it will try to regenerate it each time.

map_file Options

ValueDescription
NoneLoads the default world map, located in the assets/world/map folder.
Some(Generate)Generates a new world, using world_seed, and starts the server using it. Does not save the resulting world file.
Some(Save)Same as with Generate, but will save the world as a binary file in a maps directory.
Some(Load("maps/example.bin"))Loads a map from file

Advanced Methods

If you are able to compile the game, you can generate maps with custom sizes and terrain scales, through the same steps as above.

To change map size, edit the x and y values here. Each increment will double map scale. Be warned that worldgen times similarly increase, and that actually loading larger worlds can be quite RAM-heavy.

To change terrain scale, edit here. 4.0 is based on earth-like values, and gives a much grander scale to terrain than the default.

Map Viewer

If you are able to compile, you can also try an example map generator and viewer application. Run the following command from your local repository, depending on preferred terminal.

Unix-like:

RUST_LOG="info,veloren_world=debug" cargo run --release --example water

Windows, cmd:

set RUST_LOG=info,veloren_world=debug&& cargo run --release --example water

Windows, PowerShell:

$env:RUST_LOG="info,veloren_world=debug"; cargo run --release --example water

By default it will load the default world from the assets folder. Input a custom seed here, and change two lines below if you want to generate or load a different world.

This method will indicate progress through world generation, progressing from Erosion iteration 0 through to 99, and so is recommended for larger worlds.

Once the map loads, the default view shows temperature and humidity overlays. Press T and H to disable them, respectively, and M to enable real map colours. F4 will take a screenshot.

The map viewer is somewhat unresponsive, so you may need to hold keys for a moment for them to take effect.

Troubleshooting

If the default map loads despite changes you have made, double check you haven't mistyped any of the settings. In particular, you must not remove the trailing commas from the .ron files.

Troubleshooting

In case you've hit an issue this section might help you resolve it.

Audio issues

In case the game crashes with something similar like:

INFO veloren_voxygen::logging: Setup terminal and file logging. logdir="<some_path>"
thread 'main' panicked at 'build_output_stream failed with all supported formats: FormatNotSupported', /root/.cargo/registry/src/github.com-1ecc6299db9ec823/rodio-0.10.0/src/engine.rs:158:18
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace

If you are using Arch Linux, try installing the pulseaudio-alsa package, pacman -S pulseaudio-alsa.

Otherwise you have to manually disable audio by

  1. making sure Veloren is closed.
  2. locating settings.ron (See where Airshipper stores files)
  3. edit it and replace output: Automatic with output: Off. It should look like:
    audio: (
        master_volume: 1,
        music_volume: 1
        sfx_volume: 1,
        max_sfx_channels: 10,
        output: Off, // The important line!
    ),
    
  4. Save file and retry. In case it didn't help visit Reporting Bugs page.

PS4 or other controller not working

Currently only XInput controllers are supported on Windows. This means that controllers like the PS4, Switch, and some older generic controllers won't work with Veloren.

In order to work around this, a program such as DS4Windows can be used. Both a text and video tutorial on how to use DS4Windows can be found here.

Veloren for Contributors

Thank you for your interest in contributing!
Make sure to atleast read the Introduction section and if you want the For Developers section.

Otherwise feel free to jump around as you need.

Veloren for Contributors

This section of the book will give an overview on how to contribute to Veloren no matter if you got a degree or taught yourself. Shall it be code, assets, creatures, bug fixes, or the book itself!

Note: In this guide we assume basic computer knowledge and some curiosity to learn about what we do here by yourself.

Installation

If you want to contribute to Veloren in any form you probably need to deal with Veloren's source code. Therefore these basic tools have to be installed to interact with it.

Note: Throughout the book we will mention alot of commands. Therefore we highly recommend getting comfortable with a terminal.

Git

Keeping track of Veloren's history

For Windows, The 'Git for Windows' suite is a sensible way to install Git, along with a set of tools that'll make it easier for you to use.

On Linux Git is most likely already installed, if it isn't, use your distribution's package manager to install it.

On recent MacOS versions you will be prompted to install Git the first time you run it. Otherwise install it using Homebrew via brew install git or MacPorts via port install git.

Git LFS

Keeping track of the really big giants out there (aka. asset files)

Git LFS is a Git extension used to store large files like images and audio files. You need to install it in order to download the assets.

  1. For Windows you can download an installer here.

    On Linux you can use the package manager to install Git LFS, usually the package is called git-lfs.

    On MacOS you can use Homebrew via brew install git-lfs or MacPorts via port install git-lfs.

  2. Git LFS needs to be set up by running git lfs install or git-lfs install (macOS) in a terminal.

Note: git-lfs has a known bug with working off remotes. If you plan to work off a fork, refer to this section.

Rust

Keeping us safe and sound to build a reliable and efficient game

Rust is a general-purpose programming language we use. Refer to the FAQ section to learn why.

The recommended way of installing Rust is through Rustup. Follow the instructions carefully and everything should be good.

Local repository setup and maintenance

This section describes the initial setup and maintenance of your local repository.

Note: To understand the following we highly recommend reading about git!

Download source code

Note: Veloren needs git LFS installed before cloning to be able to download the assets. If you already cloned the repository before setting up git LFS use these steps to get the assets downloaded.

Clone the repository

git clone https://gitlab.com/veloren/veloren.git

Change your working directory to the cloned repository

cd veloren

Note: All commands in this chapter from now on should be executed from there.

Basic repo navigation

Changing branches

In order to try out new unmerged or unfinished features, you may want to switch to a different branch.

To switch to a developement branch

git checkout <branch_name>

To switch back to master

git checkout master

Updating

To download the latest changes and update your current branch

git pull

To download the latest changes without merging them into your local branch

git fetch

The help command

Git also offers a help command with detailed information about other commands

git help <optional subcommand name>

Modifying the source code

If you want to modify the source code, refer to the developer section.

To discard changes you've made to the source code

git reset --hard

Keep in mind that this deletes all the changes without a way to recover them.

To discard your changes with ability to restore them later

git stash

To restore stashed changes

git stash pop

Cleaning old build files

Over time as dependencies get updated, the old compiled versions start to take up a lot of space. To delete them type

cargo clean

NOTE: Keep in mind that cargo will need to recompile all dependencies which can take a long time.

Updating the Rust toolchain

We use a rust-toolchain file in the repository which will automatically update your rust toolchain to whichever version we use. There shouldn't be any additional effort needed on your side.

Compiling Veloren

This section covers building Veloren from rust source with cargo and running it.

Note: all commands need to be run from the repository.

Required Libraries

On Windows you will need Visual Studio Build Tools.

Note: Install "C++ tools" and "Windows 10 SDK", you won't need to install Visual Studio itself so scroll down and open "Tools for Visual Studio <year>" to find the latest buildtools.

On Linux you need to have GTK3 installed.

On Debian systems additional libraries may need to be downloaded, below is a non-exhaustive list:

  • libglib2.0-dev
  • libcairo2-dev
  • libasound2-dev
  • libpango1.0-dev
  • libatk1.0-dev
  • libgdk-pixbuf2.0-dev
  • libgtk-3-dev
  • libxcb-shape0-dev
  • libxcb-xfixes0-dev
  • libudev-dev
  • libxkbcommon-x11-dev
  • libxcb-xkb-dev

And a one liner to download and install them all:
sudo apt install libglib2.0-dev libasound2-dev libcairo2-dev libpango1.0-dev libatk1.0-dev libgtk-3-dev libxcb-shape0-dev libxcb-xfixes0-dev libudev-dev libxkbcommon-x11-dev libxcb-xkb-dev

On Fedora you can follow this awesome guide.

Note: Feel free to open an issue incase these dependencies are incorrect.

Compile and Run Veloren

Run this in a terminal to compile and run Veloren:

cargo run

To compile without running, use cargo build instead.

Note: The initial compilation will take from 5min up to 30min therefore grab a tea and some snacks and come back later.

Compile and Run Veloren Server

cargo run --bin veloren-server-cli

Logging output

We use tracing to collect logs. They can be filtered by setting the RUST_LOG environment variable to the respective level (error, warn, info, debug, trace).

For all available filtering options visit the docs.

Tip: this works both for the server and client

Optimized Release builds

By default debug builds are created which compile faster but run a bit slower than optimized release builds. Unlike many other projects, we've set them up so they're fast enough to be playable. If you want to get optimized builds, add the --release flag when calling cargo. Keep in mind that compiling release might be very slow!

If you want get familiar with cargo we recommend the Cargo Book.

Before you contribute

In case you want to contribute code (or added the assets yourself to the game) continue reading. If you do not want to add your assets yourself skip to Contributing Assets.

Git Workflow

Forking

In case it is your first contribution your first step will be to create a fork of Veloren in GitLab and clone it.

In case you have access to the Veloren repository you develop directly on the repository.

When working on a fork instead of on a branch in the main repo, you'll need to do the following for the time being due to a bug in git-lfs:

  1. Configure git-lfs to ignore smudging:

    git config filter.lfs.smudge "git-lfs smudge --skip -- %f"
    git config filter.lfs.process "git-lfs filter-process --skip"
    
  2. Add Veloren as your upstream remote:

    git remote add upstream https://gitlab.com/veloren/veloren
    
  3. Go ahead and run git lfs pull upstream, and continue to do so when new assets are added to the repo.

Feature Branches

We develop in feature branches, so before you start contributing, create your own branch:

git checkout -b <your_nickname/some_branch_name>

In your own branch you can then create commits and push them to your branch how you like it. (see Rule For Commits)

Rules For Commits

We want our codebase to be clean and make it easy to keep track of our past, this also benefits our future productivity. So there are a few rules regarding code style everyone needs to follow to get accepted.

  1. Split your code in reasonable sized logical chunks of work.

    Often a feature can be split up in smaller sizes of work, try to make use of it and structure your feature using commits. Don't just make a big commit that contains all the changes, don't make 100 commits that change one line.

  2. Name each commit.

    Find a good caption for each commit and name it. fix things is too unspecific, if a issue exists link it and it's title, e.g. fixing #123 - UDP buffer overflow when too many players are on the server.
    If no issue exists, evaluate creating one. Otherwise, describe shortly but precisely what a commit is about, e.g. fixup several issues in physics related to collisions.
    Tip: If you struggle finding one title that covers you whole commit, it might be better to split in 2 parts next time.

  3. Use git commit --amend in case you forgot to include something in your commit.

    For example you pushed your change and now the format check is failing, then instead of creating a separate commit fixing this commit, run cargo fmt locally, run git add and then run git commit --amend and git push -f to fix the incomplete commit instead of creating a new one.
    The same applies to smaller fixes like spelling errors introduced by yourself, fix the commit where the mistake was made instead creating a new one on top.
    Exception: Sometimes fmt changes their rules and untouched code becomes invalid, in case the formatting failure is related to fmt and not us, it's okay to use apply fmt on whole codebase. Please don't include other changes in such a commit.

  4. Fixup your commits afterwards.

    In case you didn't create clean commits in the first place, you can squash and change the names now. First, count how many commits your branch contains, e.g. in git status. Use the number in git rebase -i HEAD~4, e.g. for 4 commits on top of master. Read the instructions in the editor. You can change a commit name by modifying the text in a line. To Squash Commit 2 and 3 into a single commit, write squash in front of the 3rd commit, don't change the second one

    Run fmt on every commit in your branch git filter-branch -f --tree-filter "cargo fmt" $(git merge-base origin/master HEAD)..HEAD

  5. Keeping your feature up to date

    If some time has passed since you started your feature branch, rebase it on top of master from time to time to avoid merge conflicts. See below for more information.

Rebase Strategy

The codebase uses the rebase strategy therefore do not merge master in your feature branch!

First, make sure you have no uncommitted work, e.g. by creating a new commit.
Run git fetch --all and git rebase origin/master.
If it returns with no error you are fine, if it returns some, fix the errors and follow the instructions. Tip: In case of an error use git status to show next instructions.

Getting your contribution into the game

Create a Merge Request

  1. Once your feature is ready for review create a MR in GitLab out of your branch from your-nickname/your-branch-name to master.
  2. Make sure to select Delete source branch for the MR. Feel free to add additional information to the description.
    Note: If you didn't bother following Rules For Commits above, please cleanup your branch now or consider setting the squash commits option for your MR.
  3. Then create a post in the discord (if you have access in #programmers or in a working group channel otherwise in #general) and mention @Code Reviewer, someone will look over the MR and will work with you together to get it merged.

Contributing Assets

If you never worked before with git and just want to contribute assets, post them in #veloren-art on discord and ask if this can be added to the game. Make sure you own the rights to the assets and agree it being publicly available under GPLv3 license.
Tip: Checkout the Artists section.

After your first contribution

First off, congratulations on your first contribution and thank you for helping out!
After your first contribution you should get the Contributor role on discord which gives you access to important channels and access to the repository.

For future contributions develop on a feature branch, in the Veloren repository, such as our CI tests can run through and assure you that your work is following the basic rules.

Joining a team

If you have loved contributing so far and want to help out further consider joining a working group by letting the respective team lead know.

Tip You can join multiple groups and are free to work outside of the groups' focus. Mainly it helps to find out who to talk to for a specific part of Veloren.

Audio (led by Aeronic (@Aeronic#8377) & badbbad (@badbbad#5150))

Discussing, sharing and approving of music and SFX that will be featured in Veloren.

Members:

  • alfy (@alfy#2440)
  • Anthonyhme (@Anthonyhme#7923)
  • BearPrince (@BearPrince#9687)
  • Belosceani (@Belosceani#2722)
  • CinchBlue (@CinchBlue#4950)
  • DaforLynx (@DaforLynx#0296)
  • Dan Tomlinson (@Dan Tomlinson#3395)
  • Dean (@Dean#8369)
  • desttinghim (@desttinghim#7648)
  • djleo (@djleo#9001)
  • Eden (@Eden#5844)
  • Ellinia (@Ellinia#3221)
  • GameTracks (@GameTracks#8088)
  • ilijapantelic (@ilijapantelic#7530)
  • jimbles (@jimbles#1407)
  • jey133 (@jey133#8652)
  • Juli199696 (@Juli199696#4232)
  • MisterTeapot (@MisterTeapot#6718)
  • octoshrimpy (@octoshrimpy#4419)
  • Ryxalis (@Ryxalis#2269)
  • sharpie (@sharpie [UTC+1]#4797)
  • sheldon_K (@sheldon_K#0637)
  • Snow (@Snow#6325)
  • TheBoiRoy (@TheBoiRoy#6737)
  • TheSadFish (@TheSadFish#0395)
  • tumor (@tumor#3144)
  • wraithlord_koto (@wraithlord_koto#8366)
  • 未来シルバー (@未来シルバー#9339)

Assets & Visual Design (led by @Pfau#4686)

3D models, 2D pixel art/icons, UI design, concept art.

Members:

  • Demonic (@Demonic#5646)
  • Gemu (@Gemu#3901)
  • SrMizuki (@SrMizuki#7663)
  • WelshPixie (@WelshPixie#9174)
  • zesterer (@zesterer#3131)

Combat (led by Sam (@GoldFalcon9#7302))

Weapons, armor, combat abilities, enemies, and other combat-related things.

Members:

  • Adam (@Adam#3081)
  • CinchBlue (@CinchBlue#4950)
  • Demonic (@Demonic#5646)
  • James (@James M#8698)
  • Jollyfish (@Jollyfish#9021)
  • Nancok (@Nancok#4576)
  • Nero (@Nero#6962)
  • Pfau (@Pfau#4686)
  • Qutrin (@MCLowicz#3632)
  • Sam (@GoldFalcon9#7302)
  • Shinji (@Shinji#2986)
  • Slipped (@Slipped#6555)
  • Timo (@Timo#6669)
  • xMAC94x (@xMAC94x#2493)
  • zesterer (@zesterer#3131)

Game Design (led by Silentium (@Silentium#2318))

Works on designing how the game is played by the player. Designs the way the player interacts with the world, and the system they use to do so.

Members:

  • Adam (@Adam#3081)
  • Irvin Rivas (@BrownSugar (Irvin Rivas)#8843)
  • Comfy (@Comfy#8528)
  • Cutler (@Cutler (Callial)#9296)
  • DaforLynx (@DaforLynx#0296)
  • DoNeo (@DoNeo#1402)
  • Felixander (@Felixader#2540)
  • Jollyfish (@Jollyfish#9021)
  • dorf (@dorf#0022)
  • Kalculate (@Kalculate#1997)
  • Mr. Dan (@Mr. Dan#9231)
  • Nancok (@Nancok#4576)
  • Sgt-Chef Nightwalk (@Sgt-Chef Nightwalk#7734)
  • RonVal4 (@RonVal4#5635)
  • Sam (@GoldFalcon9#7302)
  • Sharp (@Sharp [GMT +2]#3429)
  • Slipped (@Slipped#6555)
  • The Dip (@The Dip#5237)
  • Timo (@Timo#6669)
  • Tortolonch (@Tortolonch#2150)
  • Warspawn (@Warspawn#9707)
  • zesterer (@zesterer#3131)

Meta (led by AngelOnFira (@AngelOnFira#8441))

Heads up CI, the website, the Discord, and lots inbetween.

Members:

  • Acrimon (@Acrimon#2954)
  • Mckol (@Mckol#3307)
  • Sharp (@Sharp [GMT +2]#3429)
  • Songtronix (@Songtronix#4790)
  • Timo (@Timo#6669)
  • xMAC94x (@xMAC94x#2493)
  • zesterer (@zesterer#3131)

Rendering (led by imbris (@imbris#4559))

Graphics, windowing, and UI logic for Voxygen.

Members:

  • Capucho (@Capucho#9388)
  • Sharp (@Sharp [GMT +2]#3429)
  • xMAC94x (@xMAC94x#2493)
  • zesterer (@zesterer#3131)

Server & Multiplayer (led by xMAC94x (@xMAC94x#2493))

Create servers that can host many people, and make multiplayer experience as great as possible.

Members:

  • Acrimon (@Acrimon#2954)
  • Geno (@Geno#5369)
  • xMAC94x (@xMAC94x#2493)
  • zesterer (@zesterer#3131)

Testing (led by YuriMomo (@YuriMomo#3795))

Testing the game by using specialized tools and making sure new features don't introduce any bugs.

Members:

  • AsyncTheory (@ProTheory8#3931)
  • James (@James M#8698)
  • Sharp (@Sharp [GMT +2]#3429)
  • Songtronix (@Songtronix#4790)
  • Treeco (@🌶Treeco🌶#1412)
  • xMAC94x (@xMAC94x#2493)
  • YuriMomo (@YuriMomo#3795)
  • zesterer (@zesterer#3131)

UX (led by Songtronix (@Songtronix#4790))

We improve Veloren's user experience by applying well-known UX practices to existing features and collecting information, bug reports from users to work out which Quality of Life improvements are needed the most.

Members:

  • Gemu (@Gemu#3901)
  • Snow (@Snow#6325)
  • YuriMomo (@YuriMomo#3795)
  • zesterer (@zesterer#3131)

Worldgen (led by zesterer (@zesterer#3131))

World generation, world simulation and terrain generation.

Members:

  • Acrimon (@Acrimon#2954)
  • Geno (@Geno#5369)
  • Sharp (@Sharp [GMT +2]#3429)
  • Treeco (@🌶Treeco🌶#1412)
  • xMAC94x (@xMAC94x#2493)
  • zesterer (@zesterer#3131)

1) How come you chose Rust for this game? It's not exactly the most mature language for game development.

Rust may seem like an unusual choice for a project like this. It's a new language that has yet to properly prove itself in production environments, and is still undergoing relatively rapid changes. We chose Rust because it has several unique features that we believe will come to benefit the project in the long-term."

Rust is safe Code written in vanilla Rust cannot trigger undefined behaviour. Rust's design helps us avoid a plethora of bugs common in other compiled languages such as dangling pointers, buffer overflow, invalid/null pointers, data races, array bound errors, and many more. This makes it particularly suitable for a large collaborative project such as this because it makes it difficult for new code to introduce difficult to fix bugs into the codebase.

Rust is fast Rust is a compiled language that doesn't require a garbage collector, exceptions, or many of the other runtime systems that make other languages so slow. In most scenarios, well-written Rust is at least as fast (and often faster) than well-written C++.

Rust is modular Rust comes with the Cargo build system and package manager. It allows Rust to be compiled in a modular manner, borrowing other libraries (known as crates) from the rest of the Rust ecosystem with ease.

Rust is portable Rust's compiler uses LLVM to target most major hardware platforms. By using Cargo as its build system, it allows for consistent compilation across many platforms. No more searching for header files or fighting linker errors!

Rust is well-designed Rust's syntax is designed to be user-friendly (wherever it can without compromising on features) and well-suited to system programming. Its unique combination of low-level control and safety makes it perfect for building both game engines and high-level game logic.

2) Why use gitlab over github?

Gitlab has better integrated ci/cd, offers everything else what Github has. Both of them are only a service on top of git on the computer, so not too much different.

3) What noise function do we actually use?

Perlin, worley, simplex, value, gradient, and a few Zesterer invented

4) How can I help? Do I have to be part of a team? I dont have experience in Rust but really want to learn and help.

Generally speaking, you can help in every area you want to. You don't have to become part of a team. But as soon as you helped a bit and showed that you are interested you can get in the respective "team".

5) How is the movement/physics/mechanics programming handled?

That's implemented in common, which is a crate for code that's common between both the server and the client (because the server needs to be the ultimate authority on physics, but the client needs to do physics prediction so that lag/latency doesn't look bad)

6) Does it have all the OOP stuff you need for a project like this? Are there times when you are coding in Rust and there is something you feel could be expressed much better in C++? I originally dismissed Rust when I first encountered it since it looked like it was just C with different syntax and some functional programming stuff, but I heard recently it is supposedly much more than that.

Although Rust has features that on the surface appear to make it an object-oriented language (it has objects, methods, interfaces, etc.) it's not actually an object-oriented language. One of the common trip-ups new developers make is to try to force Rust to behave like an OO language when a particular problem is better solved in a more Rust-y way

7) What flexibility does Rust provide that Unreal Engine does not? Is it simply because Rust makes the project modular while Unreal Engine is not modular enough for open source development?

A voxel game is a rather specific kind of game. It deals with a lot of data that most engines simply aren't really designed to deal with. Along with that, a lot of the game is procedural, something that existing asset-driven engines aren't too well equipped to deal with either. An existing engine, for us, would provide relatively few advantages despite providing several pretty significant disadvantages

8) Do you guys ever want to take this project to a fundraising platform like Kickstarter so that someone can work on it full time? Is it possible that this could become a goal one day? Or will this never be a goal?

We probably won't go that route, though I guess it's hard to say anything for sure. We see this as a volunteer project. It's more likely that any fundraising we do would go towards maintaining servers and such.

9) I got a question on how to compile the game?

Everything should be described in the Contributors section.

Troubleshooting

Incase you've hit an issue this section might help you resolve it.

Git LFS

If LFS is not installed and setup properly the lfs pointers will not be replaced with actual assets. This produces an error when running Voxygen where it complains about the validity of whatever filetype it is trying to load.

Check status

To check if Git LFS works correctly:

git lfs status

When LFS was not setup before cloning the repo

To setup LFS and download the asset files on Linux or Windows

git lfs install
git lfs fetch
git lfs checkout

MacOs

git-lfs install
git-lfs fetch
git-lfs checkout

Git pull/rebase failed due to a smudging error/404

This is a known bug with git-lfs itself and has a nice workaround. Refer to this section here.

When using Mingw64 (Windows)

Git LFS fails download the files properly. The main issue seems to be that the askpass program is not spawned when using a normal CMD prompt, preventing Git LFS from authenticating via SSH to retrieve the temporary access token. Setting the SSH_ASKPASS, GIT_ASKPASS and DISPLAY variables seems to solve this issue:

SET "SSH_ASKPASS=C:\Program Files\Git\mingw64\libexec\git-core\git-gui--askpass"
SET "GIT_ASKPASS=%SSH_ASKPASS%"
SET "DISPLAY=required"

Migrating from submodules

If you used the previous submodules system, you can deactivate it with:

git submodule deinit --force --all

Dependencies

Make sure you have all runtime dependencies installed.

Developers bug-free sky

Introducing the power of Rust development

Installing an IDE

Before being able to comfortably modify Rust code you will need to install an IDE.

TIP: Checkout the rust tools page to find common IDEs which support Rust and install the respective plugins.

Learning Rust

There are a lot of ressources to learn about Rust.

For the basics we recommend taking a look at the book and the cookbook.

After that you can start familiarizing yourself with the codebase.

ECS

Veloren uses an Entity Component System (ECS). This is a relatively new paradigm in game engine development that competes against traditional object-oriented design models that make heavy use of hierarchy, inheritance, and polymorphism. It encourages game developers to design data structures in a way that allows for efficient batch processing of data on modern CPU architectures with deep caches by storing batchable data contiguously in memory.

Traditionally, the representation of entities within a game are done like so:


#![allow(unused)]
fn main() {
struct Entity {
    position: Vec3<f32>,
    velocity: Vec3<f32>,
}
}

When representing multiple entities, it's common to use an array-like data structure.


#![allow(unused)]
fn main() {
struct World {
    entities: Vec<Entity>,
}
}

However, this comes with problems. Modern CPUs have deep caches, meaning that they are significantly faster at accessing memory that is closer in the address space to memory they have already accessed. The most efficient storage formats pack data to be processed very close together in memory. Our current representation looks like the following:

| Entity 0            | Entity 1            | Entity 2            |
|---------------------|---------------------|---------------------|
| position | velocity | position | velocity | position | velocity |

Let us imagine a 'typical' operation that we'd like to perform: applying gravity to the entities. This involves adding 9.81 in vertical velocity to each of the entities. Unfortunately, the access pattern of this operation involves touching each velocity field, but skipping each position field. These gaps in our data make the operation slower. In technical jargon, we call this a lack of 'cache coherency'.

All is not lost.

A design pattern you might have heard of is called Struct Of Arrays, or SOA. It suggests that instead of packing the data associated with each entity together in one place, we should instead group data according to its purpose.


#![allow(unused)]
fn main() {
struct World {
    positions: Vec<Vec3<f32>>,
    velocities: Vec<Vec3<f32>>,
}
}

Now our representation is much more densely packed and our application of gravity to entities is more efficient because we no longer need to skip over the position data while iterating through entity velocities.

| Entity 0 | Entity 1 | Entity 2 |     | Entity 0 | Entity 1 | Entity 2 |
|----------|----------|----------| ... |----------|----------|----------|
| position | position | position |     | velocity | velocity | velocity |

ECS is this idea taken to its logical conclusion: entities as associated but separate collections of components, with each type of component stored in its own distinct storage buffer. This approach comes with more advantages too: because components are stored separately, it is easy and fast to add or remove components from an entity as the game is running, thereby altering the behaviour of the entity and its capabilities. This is a remarkably powerful technique and allows for what amount to the ability to radically alter the behaviour of the game and the entities within it while the game is running and without sacrificing performance.

The specific ECS crate that Veloren uses is SPECS. You can read more about ECS and the specifics of how SPECS works here. If you're looking to work on Veloren, I strongly recommend reading this resource from cover to cover (it's quite short).

Project Architecture

When we started developing Veloren, we set out to with several aims in mind.

  • Modularity: The project should be composed of many interlocking pieces
  • Performance: The project should avoid architectural choices that constrain performance
  • Avoiding lock-in: The project should avoid forcing the use of certain components where possible

Crates

Veloren is split into several Rust crates. The purpose of this is twofold.

  1. Reduce compilation time by allowing parallel processing of crates during compilation

  2. Allow users of the Veloren ecosystem to only depend on specific parts of the project

Frontends

If you've spent any time around games with an active modding scene, you've no doubt come across games that have had entirely new graphical frontends written for them that allow you to play the game using different graphics or that view interesting game data that the default game doesn't usually display, such as the Legends Viewer of Dwarf Fortress. These frontends are often hackily constructed, requiring direct memory access to the game's RAM, lack significant features, or require the main game to be running in parallel.

Veloren ditches this approach and instead makes frontends a first-class abstraction. voxygen, the default graphical client frontend, has no inherent coupling with the internal client library. It's possible to write entirely new frontends that display the game's contents in a vastly different way on top of the generic client library. Examples of such alternative frontends include:

(Is something missing from this list? Feel free to add it)

voxygen

Read the docs for this crate

Voxygen is the default client frontend for Veloren and is the program that users most commonly interact with. It is a fully maintained and features a fully 3D view of Veloren's world and makes use of all of the game's client features.

server-cli

Read the docs for this crate

server-cli is the default server frontend for Veloren. It allows running Veloren servers via a simple command-line interface that includes both a 'basic' mode (streaming output to stdout/stderr) and a more complex mode that allows the inputting of server commands via a simple ncurses-style terminal interface.

Backends

As mentioned, Veloren implements core gameplay functionality through headless developer-facing libraries. These are:

server

Read the docs for this crate

The core server implementation of Veloren, with all the bells and whistles. It includes APIs for server frontends to query game state, inject events (such as server-wide broadcasts), and load plugins. In the future, we imagine a plethora of server frontends catering to many requirements: simple graphical frontends for players wanting to host a LAN game with friends, or a powerful CLI server frontend backed by a web control panel that allows precise control over game state and advanced features such as live maps or civilisation stats viewable on the web.

client

Read the docs for this crate

The core client implementation of Veloren. It is non-prescriptive about how a client frontend displays data and allows clients to connect to servers through one of many 'modes' including chat-only, spectator, and character. The default client frontend, Voxygen, is 3D, but there's no reason as to why other client frontends couldn't implement displaying the world using isometric graphics, Dwarf Fortress style layered 2D graphics, or any other representation one could imagine.

world

Read the docs for this crate

The core world generation implementation of Veloren. The use of this crate is a little more nebulous since world generation and simulation are under active development, but this crate is intended to allow viewing, generating, and simulating a Veloren world without a client or server being associated with it. In the future we imagine frontends existing that allow viewing aspects of world history like Dwarf Fortress' Legends Viewer, or editing of world data to allow for custom worlds and scenarios.

Utility crates

Many other crates in the ecosystem are utility crates that contain functionality common to many other crates. These are not intended for consumption by anything but the core Veloren libraries and should be treated as an implementation detail. These are:

common (and its sub-crates)

Read the docs for this crate

Implements behaviour and contains definitions that are common to both client and server.

network

Read the docs for this crate

Contains the raw implementation of Veloren's networking utilities.

Plugins

Veloren has recently gained a plugin API that allows for the development of additional features on top of the core Veloren experience. While the plugin API is still extremely experimental, we envisage it soon becoming the standard way to expand the game's features. Plugins are written in any language that may compile to Web Assembly (WASM), although Rust is so far the only language with official support and bindings. Plugins are fully sandboxed and get shared with clients when connecting to a server. Plugins may have server-side and client-side behaviour contained within the same package.

There are several crates associated with plugins:

plugin-rt

Read the docs for this crate

The core plugin runtime. This crate facilitates communication with the host engine and manages plugin hooks and IO.

plugin-api

Read the docs for this crate

Contains type defintions that constitute the plugin interface used to communicate with Veloren. This includes types and structures used to represent in-game state and communicate about changes in that state.

plugin-derive

Read the docs for this crate

Includes utility procedural macros for setting up a plugin, automating some of the more complex APIs provided by plugin-rt.

Debugging

This section covers some helpful debugging tips within the Veloren project. This should help if you wish to explore the code base at runtime, or work on implementing a feature.

Compiling With Debug Symbols

In order for debug symbols to be generated for the project, the debuginfo profile must be used. You can build the project with debug symbols included by running this command:

cargo build -Z unstable-options --profile debuginfo

Visual Studio Code

Follow this guide to setup your vscode installation.

After that make the following modifcations to launch.json (remember to build with the cargo command listed above!)

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(Windows) Launch",
      "type": "cppvsdbg",
      "request": "launch",
      "program": "${workspaceRoot}\\target\\debuginfo\\veloren-voxygen.exe",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${workspaceRoot}",
      "environment": [],
      "externalConsole": true
    },
    {
      "name": "(OSX) Launch",
      "type": "lldb",
      "request": "launch",
      "program": "${workspaceRoot}/target/debuginfo/veloren-voxygen",
      "args": [],
      "cwd": "${workspaceRoot}"
    }
  ]
}

Troubleshooting

Note: Some users have reported needing to run VSCode as an administrator for debugging to work correctly. If you notice an error message similar to below, then try re-running VSCode as administrator.

Unable to open 'panicking.rs': Unable to read file (Error: File not found
(c:\rustc\7dbfb0a8ca4ab74ee3111e57a024f9e6257ce37c\src\libstd\panicking.rs)).

Performance Analysis

You want to improve velorens performance? But you don't know how to best measure velorens performance ? This page is a collection of tools that are already integrated in veloren and can be used to help you collecting data.

Compiling With Release & Debug Symbols

Most tools work better with debug symbols AND in release mode: You can build the project with debug symbols included by running this command:

cargo build -Z unstable-options --profile releasedebuginfo

Integrated tooling

Prometheus

Prometheus allows you to gather internals of veloren during runtime. The data points are aggregated (e.g. all entities, state tick time spend in physics system). It allows to get a quick, rough look into velorens performance, but is not very detailed.

Prometheus statics are exported by default whenever you run veloren-server-cli Open your webbrowser http://localhost:14005 to see the raw values.

You can import this data into prometheus via this tutorial: https://prometheus.io/docs/prometheus/latest/getting_started/ Add localhost:14005/metrics to your /etc/prometheus/prometheus.yml. Connect to http://localhost:9090 and enter tick_time, execute and switch over to Graph mode.

You can connect your prometheus service to a Grafana instance: https://prometheus.io/docs/visualization/grafana/ You'll find example dashboards in the veloren infrastructure repo (not yet available): https://gitlab.com/veloren/infrastructure/-/blob/master/veloren-infra/templates/grafana-dashboads-gameserver.yaml

Tracy

Tracy https://bitbucket.org/wolfpld/tracy/src/master/ enables you to track the time spend in certain spans based on trace spans. It allows to get a detailed level into certain blocks of code, when they execute and how long they take.

Enable tracy support with the feature tracy:

cargo -Zunstable-options -Zpackage-features run --bin veloren-voxygen --no-default-features --features tracy,gl --profile releasedebuginfo

Connect to the running process via you tracy tool https://aur.archlinux.org/packages/tracy/

'cargo build -Z timings'

When you want to analyse compile time, you can use cargos feature -Z timings. It will output a .html file with individual compile times, and dependencies and a total graph showing inactive, active and indling projects.

External tooling

rust: flamegraph

follow the installation tutorial here: https://github.com/flamegraph-rs/flamegraph execute it via either cargo flamegraph or simply flamegraph and provide the location to the executable. Once you close veloren a flamegraph.svg file will be created you can open with your browser

Visual Studio 2019 Comminity Edition (Windows only)

visual studio has a build in profiler, follow this tutorial and attach it to the running veloren program: https://docs.microsoft.com/de-de/visualstudio/profiling/cpu-usage?view=vs-2019

valgrind/cachegrind

you can use valgrind/cachegrind profiler to generate a output file and later analyse it via valgrind. Tutorial: https://www.valgrind.org/docs/manual/cg-manual.html

Artists creative heaven

Introduction into the art of adding artistic content to Veloren

Saying 'Hello' in one of the art-channels

This is the best way to get in contact with our art-team on Discord. Plus you already get an idea of how we work together as a team and maybe even get to know some of us.

Artists are showing off their creations on those channels to get feedback or an 'OK' from Core-Devs that their model is ready for being animated and placed into the game.

Contributing Voxel Models

Welcome to the section about getting your voxel models into Veloren!

Software to create Voxel files

Most of our artists use the freely available MagicaVoxel from ephtracy. It's available for Mac, Windows and runs with Wine on Linux.

Of course you are not obliged to use this software but it's highly suggested as some block properties are determined directly with palette parameters shown in Magica.

At some point we might create our own voxel editor with built in model rigging and animating.

Magica is easy to begin with but hard to master. YouTube is always a great idea to start with.

You should totally have a look at controls in Magica as they highly optimise your workflow.

Our GitLab assets repo will give you an idea of how assets are organized and what kind of style we try to achieve with them.

If you want to create armour/weapon models it's suggested taking a look at the reference models For hairstyles you might want to take a look at the hairstyle reference.

For detailed info about how to create and add them to the game have a look at the guides section of the book.

Some basics you should know about

Our world consists of big, "landscape" sized blocks and small scale blocks. Houses, trees and everything directly placed in the procedurally generated world is made from landscape blocks. Objects, figures and sprites (i.e. crops) are made from small scale blocks.

One landscape block is 11x11x11 small scale voxels big.

While creating models you should always keep their scale in mind. A character model is around 22 small scale voxels tall and 10 voxels wide. So that's 1x2 landscape blocks.

Objects/figures made from small scale voxels can be arbitrarily scaled to change their size in relation to the world.

I made a model, it's approved by Core-Devs, what now?!

The best way to make your creations available to us is to upload them into our google drive model directory.

If you can't find a folder that fits your model you can just create a new one.

Whenever we look for new models and don't have them available in our local asset repositories we will resort to this drive.

How do I get the rank of an artist?

Self motivated model creation, shwoing general excitement about Veloren and high quality content along with a moderate to high activity on the Discord will earn you the rank of an Artist. Becoming a Contributor is easier and only involves showing any kind of motivation to contribute to Veloren.

What is the art-and-assets working group?

This working group is for the most dedicated and skilled artists having constantly contributed to the project. They are the ones newcomers should listen to and take suggestions from.

Is creating art for a game like playing it and mostly fun?

Yes and no. Creating models will feel like work. Usually following an initial rush of motivation right after discovering Veloren people quickly realize how much there is to do and that they can't invest the required time into this hobby due to their other commitments. We are used to that so please don't feel bad if you have to vanish for some weeks.

Keeping intrinsic motivation and working self organized isn't for everyone.

Seeing people actually playing with your models and ideas feels like a massive achievement though. This is why most of us chose to become artists on this project:

To create a beautiful world for everyone to enjoy.

If you feel like you want to join us on this great journey: We are looking forward to welcome you to our team and seeing your creations!

Contributing Audio

The best way to contribute audio is to first visit the Discord, ask for a contributor role, and start a conversation in the #audio channel.

All sound files should be in .ogg format, exported at around 120 kbps bitrate. The quick and easy way to do this is to export as a wav, open it in Audacity, and export it again as a .ogg at compression level "5".

Sound Effects

Most sound effects go in an appropriate folder in assets\voxygen\audio\sfx\.

Sound effects that emit from an in-world source, like footsteps (but not like ambient wind) should be exported as mono.

There are two main ways of triggering a sound effect in-game: sfx events and outcomes.

Sfx events retrieve sfx from the sfx.ron file. They are mostly triggered by any of the various mod.rs's in voxygen\src\audio\sfx\.

Outcome sfx are retrieved from sfx.ron and handled in voxygen\src\audio\sfx\mod.rs. Outcomes are emitted from whichever code is related to it, be it combat, server event, etc. The outcome must also be added to common\src\outcome.rs.

There are also ambient sfx, used for things like wind. The code for it is in voxygen\src\audio\ambient.rs, the files are in assets\voxygen\audio\ambient, and its manifest is assets\voxygen\audio\ambient.ron.

Be sure to have your sfx tested in-game before trying to merge it; ensure it sounds right and plays at the right volume. Be sure to get a second opinion from the Discord channel!

Music

Music files are found in assets\voxygen\audio\soundtrack\. The game retrieves the files via the soundtrack.ron file. Music should be normalized at -1dB after mastering.

It is customary to check in with one of the audio leads on Discord to get your music approved for the game.

Exploration

The game plays exploration music as single, standalone tracks in the background. When one track ends, some time passes before another track plays. Which track is played is determined by which site, biome, and time of day the player is in.

The available sites currently are the overworld, dungeons, caves, and towns.

The available biomes currently are Grassland, Forest, Desert, Snowland, Lake, Mountain, Ocean, and Jungle. Savannah and Swamp biomes are planned, but don't exist in the code.

It is worth noting that biomes are descriptive, not prescriptive, when it comes to code. The world doesn't generate based on biomes, and biomes are determined based on existing chunk data.

Also note that music may play in more than one biome and time of day, though we generally want to minimize this as more music gets added, to give each biome a more distinct tone.

The times of day are day and night.

The best way to get a feel for the tone of the soundtrack is to simply listen to it. The music should, in general, be anywhere on the electronic-acoustic spectrum, though not fully on either side. They should have soft starts and endings since they come in at essentially random times.

Combat

The current implementation of the combat music system is as follows:

"Combat" as a state is when the player comes within 40 units of either 4 or more enemies or enemies with a combined health of ~300. Upon the player entering "combat", start is played. It then loops into loop as "combat" continues, before either fading out or playing end when the aforementioned thresholds are not met.

  • start is a short intro attached to a loop. Length: 22 to 65 seconds (2 to 5 second intro, 20 to 60 second loop).

  • loop is the main portion of the combat music. Since the music only comes in when the player is in a fairly dire situation (mobs of enemies, hard enemies, and bosses), the intensity of the music should be fairly high. Length: 20 to 60 seconds.

  • end is a short cadence coming off the end of the loop, and is only played if the fadeout doesn't complete before the loop ends. Should give a sense of finality. Length: 2 to 5 seconds.

It is important for the transitions and loops to be smooth. This means the loop must be "exported as loop"; the tail (the residual release/reverb at the end) of the loop must also bleed into its beginning, as well as into the end.

Translators multilingual society

Introduction on translating content to your language.

Translate the Game

File and directory structure explained

You can see the localization files inside the assets/voxygen/i18n directory. Each directory in this directory represents a language (or a variant of it). The directories are named after the ISO 639-1 codes. (en/, de_DE/, pt_BR/ etc.)

Veloren uses a key-value system to translate content. Files use the RON data serialization format (.ron). They must be formatted in UTF-8, without BOM. Each file contains a metadata section, a key-value localization section and lastly a key-list localization section. Files also include font settings that will be used in the game and a convert_utf8_to_ascii option, which can be used when translating a language which has characters that aren't in the font(s) Veloren uses. Don't be afraid to ask the addition of these characters in our Discord. Each file includes how to format the file at the top of it.

The metadata section includes a display name and an identifier for the language. The display name may be freely changed but the identifier should stay the same after the introduction of a new language:

metadata: (
    language_name: "English",
    language_identifier: "en",
)

The string_map section includes the key-value localization pairs:

string_map: {
    /// Start Common section
    // Texts used in multiple locations with the same formatting
    "common.username": "username",
    "common.singleplayer": "Singleplayer",
    "common.multiplayer": "Multiplayer",
    "common.servers": "Servers",
    // and more...

Multiline strings must be written like this:

        // Message when connection to the server is lost
        "common.connection_lost": r#"Connection lost!
Did the server restart?
Is the client up to date?"#,

The vector_map section includes the key-list localizations. A localization from a list is randomly selected when the game needs it:

vector_map: {
    "loading.tips": [
        "Press 'G' to light your lantern.",
        "Press 'F1' to see all default keybindings.",
        "You can type /say or /s to only chat with players directly around you.",
        "You can type /region or /r to only chat with players a couple of hundred blocks around you.",
        "To send private message type /tell followed by a player name and your message.",
        // and more...

Localization test explained

Veloren includes a localization test for translated languages. This test gathers information about every key and compares them with the reference language (English, en/ directory). Then it classifies and counts these comparisons and prints them in a neat way for translators to inspect. This guide explains where to find the test and how to read the results of it.

Open this link where you can see a list of pipelines currently running in our CI system. Try to find a relatively new (top is new) pipeline which is already green and contains the title build in Stages column, tap on it and you should find a unittests field. Not every pipeline might have one and it is not essential to get the newest one, just find unittests and you are good to go. Then click on it. A page will open with a black box and a lot of text. You have to scroll up a bit and find something like what we see below:

-----------------------------------------------------------------------------
Overall Translation Status
-----------------------------------------------------------------------------
            | up-to-date | outdated | untranslated | unused   | errors  
uk_UA       |     713    |     7    |       0      |     1    |       0
pt_BR       |     699    |    18    |       3      |     3    |       0
PL          |     702    |     2    |      16      |     3    |       0
zh_CN       |     673    |     6    |      41      |     7    |       0
es_ES       |     663    |    13    |      44      |     7    |       0
de_DE       |     670    |     0    |      50      |     7    |       0
fr_FR       |     656    |    11    |      53      |     8    |       0
ja_JP       |     655    |     8    |      57      |     8    |       0
es_la       |       6    |   346    |     368      |     3    |       0
tr_TR       |     340    |    12    |     368      |     2    |       0
ru_RU       |       5    |   343    |     372      |     3    |       0
no_nb       |       6    |   328    |     386      |     2    |       0
nl          |       3    |   305    |     412      |     3    |       0
it_IT       |       1    |   229    |     490      |     4    |       0
pt_PT       |       1    |   135    |     584      |     5    |       0
zh_TW       |       1    |   133    |     586      |    12    |       0
sv          |       1    |   104    |     615      |    13    |       0
vi_VI       |      74    |     2    |     644      |     1    |       0

45.29% up-to-date, 15.45% outdated, 39.27% untranslated
-----------------------------------------------------------------------------

This is the indication that you did everything right and got to the statistics. This is an overview page that shows you the available languages and if there are some missing or outdated translations. Let's say we want to focus on the Ukrainian one, uk_UA. We have to scroll further up till we get the detailed information:

Tip: Use Ctrl+F to search for your language! Tip: If you can't find your language, press 'Complete Raw' to get full log.

-----------------------------------
"assets/voxygen/i18n/uk_UA.ron"
-----------------------------------
Key name                                                    | assets/voxygen/i18n/uk_UA/_manifest.ron  | assets/voxygen/i18n/en/_manifest.ron    

	[NotFound]
buff.desc.cursed                                            | None                                     | 531c38c3ad08af5370bfa2cd5f501224b55b4c22
buff.title.cursed                                           | None                                     | 531c38c3ad08af5370bfa2cd5f501224b55b4c22

	[Unused]
hud.settings.cancel_inputs_on_chat                          | ef8926bc5c10fe80bfe7ee8d69763d710c229afb | None                                    

	[Outdated]
common.stats.poise_res                                      | 4717ad25a8fb5856951360d4e01eb741eb289fe4 | 6772e71aaac034ac03917e67988176cbf2772cb1
gameinput.autowalk                                          | 4e7aba5b3f8a2499785bc4f1776eddbdaf6fe96d | a17bb0ad733e0dbe2c649ebb31a94990db438b01
gameinput.secondary                                         | 4e7aba5b3f8a2499785bc4f1776eddbdaf6fe96d | 2e24fcf165545405e13a3eb5f8b3e3eeadf3f1c0
hud.auto_walk_indicator                                     | 4e7aba5b3f8a2499785bc4f1776eddbdaf6fe96d | a17bb0ad733e0dbe2c649ebb31a94990db438b01
hud.map.dungeons                                            | 7be836318bbae1e26b03500b4235eef22e6be4e4 | db573f6b2dc5ae91f33296b07709c4c66c6e4c16
hud.settings.refresh_rate                                   | 7be836318bbae1e26b03500b4235eef22e6be4e4 | 60e2ed3e7dfad7c88fc7c6c1fa2fefefec6240a4
main.login.network_wrong_version                            | 4717ad25a8fb5856951360d4e01eb741eb289fe4 | 2e08c2f76f29e98b29f997bcff6456c373c8117a

711 up-to-date, 7 outdated, 1 unused, 2 not found, 0 unknown entries
98.75% up-to-date, 0.97% outdated, 0.00% untranslated

Here we have detailed information about all language keys used in the Ukrainian translation (uk_UA directory) in comparison with the reference English translation (en directory). Here is what the State section for keys mean:

  • Unused: The key exists in the Ukrainian translation but not in the English translation. These are keys that have been used before in the game but have been removed since they weren't needed or have been renamed. These are safe to remove.

  • NotFound: The key exists in the English translation but doesn't exist in the Ukrainian translation. Here we need your translation!

  • Outdated: The English translation was changed but the Ukrainian translation wasn't updated. This might be a hint that the Ukrainian version is outdated and needs an update. Note that this doesn't necessarily mean that the English localization has been changed in a way that it affects the meaning - it might simply have been a typo fix.

  • Unknown: This signifies that an error occured while getting information about this key. This is not an user issue, so you don't need to worry about these keys. But if you've found this, please tell us about it.

Guide to translating the game

Guides to help you translate the game, for certain situations. If you get stuck at any step or need more help (or just have questions) ask us in our Discord server.

If you are not familiar with Gitlab or Git, feel free to ping @git-wizard, who will always be on deck to help with this.

Create a translation from scratch

If there is no translation for your language, you can create one by following these instructions. Remember to replace the Turkish language example with your own language!

First of all, you need a copy of the Veloren repository. Refer to the working with git section for this.

After you have obtained a copy of the repository, navigate to the assets/voxygen/i18n directory. Here you'll see a list of translations, and the reference English directory (en/). Make a copy of the en/ directory, named after your language. For example, if you want to translate Turkish, your directory would be named tr_TR/.

Then, you can start editing the file! First go to the metadata section in _manifest.ron. This section has the display name and the identifier for your translation. Change language_name to a human readable name in your language (eg. Türkçe (Türkiye), means Turkish (Turkey) in English) and change language_identifier to the identifier of your language (usually same with the file name, eg. tr_TR). Also change the:

/// Localization for "global" English

section to show your language. For example:

/// Localization for Turkish (Turkey)

And that's it! You can now start translating any key you want. Don't forget to preview the changes ingame by compiling and running the game, and setting the language setting to your new in-translation language. If there are missing characters, don't worry, you can ask us about these in our Discord server.

Tip: you don't need to compile game to test your changes, you can just copy your changes to assets directory in app folder. If you have installed the game via Airshipper look in these directories

Tip: You can set convert_utf8_to_ascii option to true to convert everything to ASCII, so that the missing characters can be seen properly.

After you are happy with your translation, commit your changes with a proper commit message, such as Added Turkish (Turkey) translation. Create a new branch with name <yourusername>/add-<language>. Then push your changes and create a merge request (don't forget to read and tick all the boxes!). You can notify us on the Discord server that there is a new translation available (in the #translation channel).

Improving an existing translation

If you are lucky, you may already find that there is a translation for your language. If you see that it has some missing, incorrect translations or you think that you have a better translation for something, then this guide is for you! Don't forget to replace the Ukrainian example with your own language (if you aren't translating the Ukrainian language).

Okay, let's say we want to add the correct translation for the Ukrainian of buff.desc.cursed. So we first want to look it up in the reference language (English) assets/voxygen/i18n/en/buff.ron. After the key we find this English sentence: Cursed. We know that the Ukrainian equivalent would be Проклін (Well, some of us do :P). Then, we open the translated language (Ukrainian) assets/voxygen/i18n/uk_UA/buff.ron.

If you are already a developer in Gitlab you'll now see an edit button. If you don't, no need to worry! You can do the same operations with your fork.

Tip: Keep both the reference language and the translated langauge open, so that you can find in which place your entry is missing by looking at the line numbers.

Once you are editing the file on Gitlab, go to the place where our entry is missing and add it here:

"buff.title.cursed": "Проклін",
"buff.desc.cursed": "Вас прокляли.",

Getting information about translation

If you are able to compile, you can use special tool to get information about translation. Basically, it is the same programm that runs on our CI, but you can cut out unneded information. For example, you want to get information about Ukrainian translation.

$ cargo run veloren-i18n --features=bin -- uk_UA

You can also run it with --help argument to find out more ways to use it.

Note: arguments go after --, it's where cargo arguments end and actual arguments start.

Note: you need to commit your changes for veloren-i18n to know about it.

Push changes

When you're done, enter a specific commit message such as update the <language> translation. In our example this would be update the Ukrainian translation. You'll then create a target branch with name <yourusername>/update-<language>. An example would be juliancoffee/update-uk_UA. Finally, you'll need to create a merge request. Read and check the boxes to agree that your code will be under the GPL3 license.

If you don't have Developer role, you will need to push your changes to your fork and then create MR from your fork to main repo.

$ git remote add fork https://gitlab.com/<yourusername>/veloren
$ git push fork <yourusername>/update-uk_UA

You now requested to change the file, and we will then take a look at it and if we found it okay we will merge it into the game! From that point on your contribution will land in the master branch, and in 24 hours will be shipped to you via Airshipper.

Help review pending translations

Someone may have done a translation for your language already. To check this, you can take a look at the Merge Requests page. See if you can find a pending merge request with your language (e.g. de_DE). Ask in our Discord server for reporter privileges so you can open it and add your thoughts / reviews to it.

If you found one, clicking on the Changes tab when you're in the MR page you can see the details of this change. Red lines are old ones that are removed and replaced by green lines. If you disagree with something that was added, you can start a discussion by clicking on the respective line. If you don't see any issues with the proposed changes, feel free to approve the MR or comment on it. It's important that you approve the MR for translations as we the developers don't have the knowledge to verify the language (and we don't want to put everything through Google Translate). So you help us to verify others contributions resulting in improved game quality.

For Game Designers

Section under construction. Be careful out there.

Writing a Proposal by @Silentium

The first thing to remember when writing a design proposal is that any idea might not make it into the game due to the consensus of the design team. Unfortunately, we can't include all ideas, but with some compromising and some creative thinking, we can usually extract important aspects of the idea. Even ideas that don't get used in their full state can still provide some very important insight into other aspects of the game's design, so do not think that an unused idea is invalid. Every idea is useful, just maybe not necessarily in the way it was intended. The more the better.

The second thing to understand is that the proposal needs to keep from being too detailed. The goal is to do the least amount of work possible to effectively communicate the fundamental principles and reasoning of an idea. This is because if the idea isn't well-received, or if it needs some reworking or changes, they can be done without subverting a large portion of the work done to write the proposal. No idea proposed to the game design team is used verbatim.

The third thing is to be open to changing ideas. As previously stated, no idea is used without some changes being made first. As long as the proposer keeps an open mind about what can be changed, their idea will be very useful to the design of the game. This also helps uphold healthy conversation grounds.

Fourth is to read all preceding documentation. Almost everything we are currently doing is built off previous work. We have a very specific order in which we are designing gameplay elements and systems, and some of them need to be addressed before others. Make sure not to presume we haven't designed an element of play, and make sure not to read too deeply into the potential implications of a system we have designed. The proposals are written to be specific, including only that which has been explicitly been decided on. Items out of its scope may still be up in the air.

The last thing is that everything is changeable, even the stuff we have already written in our official documentation. By this, I do not mean we are open to redesigning the whole thing again. We are very proud of the work we have done and would rather not scrap it. That being said, if anyone thinks there is a problem with a system we have designed or a fundamental fatal design choice we have made, we are open to hearing about the criticism. Not only that, but we fully expect some systems to not prove viable when we start playtesting. As a result of this, we will likely have to rework some of our systems and come up with more iterations.

Each time we begin a new topic of design, I will make a post to let the team members put their ideas into one document. We all collaborate on this to get the final proposal finished so keep an eye out for those in #game-design!

As long as someone can keep these principles in mind while they write their proposal, they will be very effective in contributing their ideas to the game design team. Please let me know if you have any questions about any of this, or if you need clarification. I love when people take an interest in our work, so please don't hesitate to message me!

Cheers ✨👍✨ @Silentium

Writers vocabulary hub

An art style worth a look.

Contribute to this book

You can find the source for this book at our GitLab, feel free to make changes, correct errors and add more content.

Common elements

Consistency is key. Therefore here are common elements used in the book and how they should be styled:


content

Note: Highlight important notice which the reader should keep in mind.


Short description for experienced people

More detailed description for beginners and newcomers.


Use <br/> for linebreaks.


Rust syntax highlighting works good for .ron files. (Use rust,ignore to avoid making them runnable)

Brown: (
    vox_spec: ("armor.chest.grayscale", (-7.0, -3.5, 2.0)),
    color: Some((90, 49, 43))
),

content
Tip: notice which can help reduce time and effort.


DevOps automated introduction

Section under construction. Be careful out there.

Provide a CI runner

We need to host our own CI runners, if you have a spare pc or server and have some spare compute time for Veloren, it would help us a lot.

What is CI?

Continuous Integration (CI) is a set of automated tests and tasks that run on the Gitlab repo every time code is added. This means that Merge Requests have a lower chance of breaking the codebase, and we can automatically get builds of the contributed code, among other benifits.

We need your computer to help us with the testing. It's not free to run CI, so we use our own computers to do it.

Technology

We use gitlab runners in combination with docker.

Requirements

  • = 2 cpu cores. we don't cause constant 100% load, but when a compilation even comes it, more cores help to quickly process them.

  • 50gb of free hdd space. The docker image contains a cache and is about 20gb additional caches can take up some more space.
  • = 1 Mbit/s internet. you don't need fast internet, but the runner will connect to the internet from time to time. a flat rate is ideal.

Install a runner

First, you will need to contact a maintainer to get a Gitlab runner token. For this, either message @xMAC94x, or @AngelOnFira. We will probably make sure you've been with the project for a bit, as we don't want to give the token to just anyone.

You will need to have Docker installed, as all of the builds use it. You can follow instructions here,

Clone our Veloren CI repo and start the helper script from the runner directory

git clone https://gitlab.com/veloren/veloren-docker-ci
cd veloren-docker-ci/runner
./launch-runner.sh
# provide the token from above
# provide a name in the style: `<discord-username>-<descriptor>`. So for example, @angelonfira's might be `angelonfira-server-1`.

The script will take about 10 minutes and when it's done a docker container is started in the background. It will only take processing power when there are Pipelines to be done.

Currently, the script is Linux only, if you run Windows your best bet is to create a Linux VM (e.g. using Virtualbox and Ubuntu Server 20.04)

Update a runner

In order to use caches effectively, we need at least gitlab-runner v13.8.0. If your runner is older, please recreate the runner, you can use the update-runner.sh script. Note, it is interactive and requieres your input

# 1. update repo veloren-docker-ci repo
git pull
# 2. go in runner folder
cd runner
# 3. execute update script
./update-runner.sh
# provide if you want to prune old images, choose yes except if you have certain images you want to keep or run other docker containers
# provide the CONTAINER ID that reflects the old image
# see the Install section above for re-setting up the runner

Guides

This section of the book will provide guides for common modifications to Veloren like adding new Armor.

Guide: Adding armour to Veloren

\_ made by @Pfau

\_ updated by @BottledByte

Image1

What you need

An IDE of your choice (A programme that lets you view and edit code)

Examples: VSCode, Atom, Notepad++

A Voxel Editor (To create the armour model)

Example: Magicavoxel

The character template. (Can be opened with any voxel-editor that supports layers; i.e. Magicavoxel.)

This is also included in the Veloren client’s assets.

Getting Started

Before creating your armour in a voxel editor there are a few things you should know:

Image2

In order to place “skin” (parts that are not covered by armour) to your work you have to use whatever colour is stored in the 1st (light tone) and 5th (dark tone) slot/index of your palette.

Note: The important thing is not the colour but the position on the palette to get the right result.

Image3

Armour can be coloured in the code, too.

To do that just use greyscale colours in your model. (Later you will learn how to apply the colours with code.)

Another thing you might notice is the neck added to the chest armour. This is needed to not make the head appear disconnected from the body when it’s turned.

Importing the model and adding it as an item to the game

To make the game actually load your creation there are several steps you have to follow.

They can be done in any order.

Copying the .vox into the asset folder

Make sure to export your model(s) as .vox and NOT just copy a saved .vox file from magicavoxel. Just copying will result in a ~10x bigger file size.

Image4

The file path inside the assets folder is something like

assets/voxygen/voxel/armor/<Armor Type>/<Model Name>

So for a chest armour called “leather_vest-0.vox” it is:

assets/voxygen/voxel/armor/chest/leather_vest-0.vox

Naming scheme for .vox files

Single words are parted with an underscore (“_”)

Counting starts at zero.

Numbers are added with a single dash(“-”) in front of them.

Your item name should always end with a number, unless you are absolutely positive there isn't going to be an alternative version/design of the item

Load the file and store it inside the code

Those are the file paths you will need after opening the root folder of Veloren in your IDE:

assets/voxygen/voxel/humanoid_armor_<armour type>_manifest.ron

ONLY needed for armour with .vox files

(sets the filepath and offsets of the .vox)

assets/common/items/armor/<armour type>

(create a new .ron in here to create an ingame item)

assets\voxygen\item_image_manifest.ron

(create a new entry in here to add an item image to the item)

common/src/comp/inventory/item/armor.rs

Note: ONLY needed for armour with .vox files

(list your new armour style in here)

Veloren has 12 types of armour

Types in bold need a 3D .vox file

  • Head
  • Neck (Necklaces)
  • Tabard
  • Shoulder
  • Chest
  • Hand
  • Lantern
  • Belt
  • Ring
  • Back (Capes, Backpacks...)
  • Legs
  • Feet

Armour types that need a 3D .vox file need to be listed in every file above.

Example for adding a new cape

1. New entry in assets\voxygen\voxel\humanoid_armor_back_manifest.ron

Image5

The offset will be determined at a later point! Just keeping the numbers from the example you copied should be good for now.

Image6

Copy this part (make sure to include the brackets and comma!) and paste it:

Image8

Fill in the name of the item style (kind). This is the name you’ll use to later to match up assets.

Note: color: None indicates that grey parts won’t be recoloured.

To colour those parts put in “color: Some((<R>, <G>, <B>))” here.

Example of recoloured armour
"Brown": (
    vox_spec: ("armor.chest.grayscale", (-7.0, -3.5, 2.0)),
    color: Some((90, 49, 43))
),

2. New entry in assets/common/items/armor/<armour type>

Image9

Copy and paste one of the existing .ron files (Note: Use numbers here too).

Item(
    name: "New Cape",
    description: "Example Item",
    kind: Armor(
        kind: Back("NewCape"),
        stats: (20),
    ),
)

Edit the file to have the right kind. (The same kind you put in before.)

Image10

description field in Item creates a tooltip text similar to this.

3. Add a new item image in assets\voxygen\item_image_manifest.ron

You can either use a .png or .vox file as an item image.

Example for a .png:

// Lanterns
Lantern("Black0"): Png(
    "element.icons.lantern_black-0",
),

Example for a .vox:

Armor(Back("Short0")): VoxTrans(
    "voxel.armor.back.short-0",
    (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0,
),

In order to find the right posing numbers for the .vox it’s often a good idea to look for a similar item.

Armor(Back("NewCape")): VoxTrans(
    "voxel.armor.back.new_cape-0",
    (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0,
),

You can use the same .vox as the actual 3D asset shown equipped on the character later.

Image11

4. Finding the right offset for your item

In order to test your item in-game you need to compile your game now.

Your new item will only be available locally, so make sure to connect to a local server or choose “Singleplayer”.

To drop the item into your inventory use the chat command /give_item:

/give_item common.items.armor.back.new_cape

Image12

When equipping your new item you might be presented with this sight.

To set the right offset you need to revisit ssets/voxygen/voxel/humanoid/<armour type>_manifest.ron

The values in there can be hot-reloaded. That means just saving them will immediately take effect ingame.

"Admin": (
    vox_spec: ("armor.back.admin", (-5.0, -1.0, -0.0)),
    color: None
),

They represent the coordinates:

(X, Y, Z)

X = Left (lower the number) and Right (increase the number)
Y = Back (lower the number) and Forth (increase the number)
Z = Up (increase the number) and Down (lower the number)

Change the numbers until you get the desired offset.

Image13

Done. You added a new armour style and item to Veloren. :)

But just in case something went wrong, a little troubleshooting advice

It may happen that your armor displays as a big pink box with a question mark (in the world or in the inventory).

If this happens, some entry is invalid, probably due to a typo in your style (kind; like "NewCape") or one of the asset paths. See the log for details to pin-point the source.

If the game panics when loading your armor, it mostly means that the syntax of one or more entries in .ron files got garbled (like a missing parenthesis or a quote mark).

Guide: Adding sprites to Veloren

\_ made by @Pfau

Image1

What you need:

  • An IDE of your choice (A programme that lets you view and edit code)

    Examples: VSC, Atom, Notepad++

  • A Voxel Editor (To create the sprite model)

    Example: Magicavoxel

  • A guide on how to compile and run Veloren on your OS.

  • A locally cloned branch of Veloren's nightly version.

Getting Started

Before creating your sprites there are a few things you should know:

  • Sprites act like landscape sized blocks that get replaced with small scale models.
    They can either be set to behave like air (no collision at all) or as solid objects.

  • Things like grass should have no collision. While scarecrows and windows should.

  • As of now they will always act like a single row of up to three lanscape blocks (33 small scale voxels) above each others. That means you can control the collision height of your sprite but not the width.
    Every part of a sprite that exceeds the x and y-axis bounds of a single block will not clip with figures and objects.

  • Sprites act as immovable objects like blocks. They can't be moved around like figures or objects.

  • Sprites can be set to give players a certain item when picked up.

  • Sprites can have certain spawning and orientation rules.

Naming scheme for .vox files:

  • Single words are parted with an underscore (_)
  • Counting starts at zero.
  • Numbers are added with a single dash(-) in front of them.
  • Your model name should always end with a number, unless you are absolutely positive there isn't going to be an alternative version/design of the item

Load the file and store it inside the code

voxygen/src/scene/terrain.rs

Here you define how many variations your sprite can have, how much it sways in the wind and which model(s) to load from the asset folders.

common/src/terrain/block.rs

Here you define the sprites' properties like collision, orientation, the height of their "collision frame" or if they can be collected.

common/src/comp/inventory/item/mod.rs

Sprites listed here will drop items when collected and marked as collectible in block.rs.

world/src/block/mod.rs / world/src/site/dungeon/mod.rs

Sprites are part of worldgen and will be treated as blocks with certain spawning rules within 'world':

Step by Step: Addition of an example sprite

  1. In voxygen/src/scene/terrain.rs find a match that looks like this:

    match kind {
        BlockKind::Window1 => Some(SpriteConfig {
            variations: 1,
            wind_sway: 0.0,
        }),
    

    Copy and paste one of the code-blocks i.e. ...

    BlockKind::Door => Some(SpriteConfig {
            variations: 1,
            wind_sway: 0.0,
        }),
    

    ... and put it behind the last entry. Replace the parts inside according the sprite you want to add.

    Much lower you'll find a something called sprite_models: vec!.

    Inside it is every sprite type model variation listed. Here you can set the offsets for them, too.

    If you want to center your model (and set the .vox file's workspace to fit your model) it's the negative half of your models dimensions for x and y. The z-axis determines if the model is supposed to sink into the ground.

    Example:

    (
         (BlockKind::Velorite, 0),
         make_models(
             "voxygen.voxel.sprite.velorite.velorite_ore",
             Vec3::new(-5.0, -5.0, -5.0),
             Vec3::one(),
         ),
     ),
    

    This loads the first model variant for Velorite. Counting starts at 0. The model's Dimensions are 10.0, 10.0, 10.0. Using Vec3::new(-5.0, -5.0, -5.0) will center the model on the block it's spawned. Doing offcentered models is possible but only recommended for non-colliding sprites. Putting in -5.0 will put the sprite model 5 blocks below the ground. Most models use 0.0 though to not have any offset at all.

    If there's less variants listed in this vec than stated in the match above the client might panic. So if you put in a "3" above you need at least 3 variants stated as variants "0", "1" and "2" below.

  2. In common\src\terrain\block.rs

    First add your new BlockKind to the END(!) of the BlockKind enum directly at the file's beginning.

    Next thing you'll have to do is define whether your sprite behaves like air/clips with figures. pub fn is_air is the function you're looking for. Add your new BlockKind to the bottom of this matchand set it to either true or false. Not adding it to this list will make your sprite solid.

    pub fn is_opaque and is_solid are probably self explanatory and work just like the is_airfunction.

    Next is pub fn get_height. In order to be able to define a collision height for your sprites in the next function they need to be solid.

    How do the numbers in there work?

    1.0 are corresponding to 11 small scale voxels. So if your model is 18 small scale voxels tall you will need to put in 18/11 = 1.64 in here.

    Note: 3.0 is the maximum height as of now and this collision height will apply to all sprite variants of a single type

  3. Next we'll define if your sprite is_collectible.

    Only add it to this function if you want to make it collectible. This match may also hold Blocks that are meant to be collectible at a later point but are not yet.

    In order for them to actually add an item to the player's inventory they need to be listed in

    common/src/comp/inventory/item/mod.rs

    In there you are looking for the try_reclaim_from_block function.

    How items are added will be addressed in another guide but adding one of the existing ones for testing purposes is recommended at this point.

Making sprites spawn in the world

When and how sprites spawn is mostly determined by worldgen code.
This guide will give a short insight into how this works for things like mushrooms in the open world.

world/src/block/mod.rs is the file determining the spawning variables for them.

BlockKinds that are supposed to spawn under certain conditions are stored in arrays:

let flowers = [
    BlockKind::BlueFlower,
    BlockKind::PinkFlower,
    BlockKind::PurpleFlower,
    BlockKind::RedFlower,
    BlockKind::WhiteFlower,
    BlockKind::YellowFlower,
    BlockKind::Sunflower,
    BlockKind::Mushroom,
    BlockKind::LeafyPlant,
    BlockKind::Blueberry,
    BlockKind::LingonBerry,
    BlockKind::Fern,
];

Further down spawning rules are applied to them. Details about those might be addressed in another guide about world generation.

Although at this point we can consider this guide..

Done. You added a new sprite to Veloren. :)

Guide: Adding Weapons to Veloren

\_ made by @BOB17368

Image1

What you need:

  1. An IDE of your choice (A programme that lets you view and edit code)

    Examples: VSC, Atom, Notepad++

  2. A Voxel Editor (To create the weapon model)

    Example: Magicavoxel

  3. A compiled version of veloren(So you can edit the game's files)

Optional:

  1. A character template. (Can be opened with any voxel-editor that supports layers; i.e. Magicavoxel.)

  2. Access to the veloren google drive model directory.

Some things you might want to know before you start creating your weapon:

1. Veloren has 12 types of weapons:

  • Polearms
  • Tools
  • Shields
  • Daggers
  • Sword
  • Axe
  • Longbow/Shortbow
  • Staff

The non-bolded ones are either a work in progress or have not started their development yet.

2. The Veloren google drive files are useful becuase it allows you to use the existing weapon models as format

3. You need to have your model(s) approved by the core-devs if you want your model to make it into the actual game

4. The handelbar for the weapon has to be at the most, three voxels long and three voxels wide

Importing the model and adding it as an weapon to the game

To make the game actually load your creation there are several steps you have to follow.

They can be done in any order.

Step 1: Export and copy the .vox file into the asset folder:

Before you export you models please double check that you have...

  1. Exported your model(s) as .vox and NOT just copied a saved .vox file from magicavoxel. Just copying will result in a ~10x bigger file size.

  2. Made sure that there is No extra space can be shaved of without sacrificing voxels.

Go to the filepath below and paste your .vox file and rename it according to the naming scheme below.

assets/voxygen/voxel/weapons/<Weapon Type>/<Model Name>

The filepath to the file should look something like this by the time you finish

assets/voxygen/voxel/weapon/sword/long_2h_fine-0.vox

Naming scheme for .vox files:

  • Single words are parted with an underscore (“_”)

  • Counting starts at zero.

  • Numbers are added with a single dash(“-”) in front of them.

  • Your weapon name should always end with a number, unless you are absolutely positive there isn't going to be an alternative version/design of the item

Step 2: Create a .ron file:

1. Create a New Entry in

assets/common/items/weapons/<weapon type>
Copy and paste one of the existing .ron files of the same type of weapon you aim to create and edit the parts encapsoulated in [ ] to your preference
Item(
    name: "[Crude Mallet]",
    description: "[Two-Hand Hammer\n\nPower: 10-12\n\nBreaks bones like sticks and stones.\n\n<Right-Click to use>]",
    kind: Tool(
        (
            kind: Hammer([BasicHammer]),    
            equip_time_millis: 500,
            power: 1.30,
        )
    )
)

Note: From now on when I refer to the "Weapon Kind" I am talking about the case-sensitive name you put for "BasicHammer"

Step 3: Create a new entry in the weapon manifest file.

Go to this file path and open humanoid_main_weapon_manifest in a text editor

assets/voxygen/voxel/humanoid_main_weapon_manifest.ron

Copy and Paste a module of code where the same type of weapons you are adding are grouped (make sure to include the brackets and comma!)

Then adjust WornIronAxe0 with your "Weapon Kind"

Axe(WornIronAxe0): (
        vox_spec: ("weapon.axe.2haxe_worn_iron-0", (-1.5, -3.0, -4.0)),
        color: None
    ),

The offset will be explained at a later point!
Just keeping the numbers from the example you copied should be good for now.

Optional: color: None is used for giving a weapon a specific tint. Just specify the rgb values by replacing None with Some((<R>, <G>, <B>))

3. Step 4: Adding the armour style to tool.rs

Open common/src/comp/inventory/item/tool.rs in a text editor

and add your "Weapon Kind" to the respective enum

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ShortbowKind {
    BasicShortbow,
    WoodShortbow0,
    WoodShortbow1,
    LeafyShortbow0,
}

Step 5: Add a new item image in image manifest file:

Find where the code for your weapon type is located and copypaste it in the same location

You can either use a .png or .vox file as an item image. But it is only pratical to use a vox model for weapons.

Example for a .vox:

Tool(Longbow(WoodLongbow1)): VoxTrans(
        "voxel.weapon.longbow.longbow_wood-1",
        (0.0, 0.0, 0.0), (90.0, 90.0, 0.0), 1.0,
    ),

In order to find the right positioning values for the weapon, it’s often a good idea to look for a similar item.

Armor(Back(NewCape)): VoxTrans(
    "voxel.armor.back.new_cape-0",
    (0.0, 0.0, 0.0), (-90.0, 180.0, 0.0), 1.0,
),

You can use the same .vox as the actual 3D asset shown equipped on the character later.

Step 6: Finding the right offset for your item:

In order to test your weapon in-game you need to compile your game now.

Your new item will only be available locally, so make sure to connect to a local server or choose “Singleplayer”.

Drop the weapon into your inventory by using the following chat command:

Full Command:

/give_item common.items.weapons.<weapon type(ex: staff)>.<Weapon Kind>

Tip: You can type /give_item common.items.weapons. and then press tab to cycle through available weapons

If you dont like how your character holds your weapon you have to mess with the weapons offsets.

To set the right offset you need to revisit assets/voxygen/voxel/humanoid_main_weapon_manifest.ron

And then tweak the offsets marked below until satisfied

Sword(LongFine4): (
    vox_spec: ("weapon.sword.long_2h_fine-4", (<x offset>, <y offset>, <z offset>)),
    color: None
),

The values in there can be hot-reloaded. That means just saving them will immediately take effect ingame.

They represent the coordinates:

(X, Y, Z)

X = Left (lower the number) and Right (increase the number)
Y = Back (lower the number) and Forth (increase the number)
Z = Up(increase the number) and Down (lower the number)

Change the numbers until you get the desired offset.

Done. You added a new weapon style and item to Veloren. :)

Guide: Using the const_tweaker crate to modify values at runtime

\_ made by @Pfau

What you can do with it

The const_tweaker crate allows you to modify constants at runtime. This can be used to tweak animations, UI widget positions and much more!

Short guide by example

Add a new const to your file:

#[const_tweaker::tweak(min = -100.0, max = 20.0, step = 1.0)]
const EXAMPLE: f64 = 10.0;

Notice the attached attributes above. Every new const needs their own!
This will create an f64 type that can be changed between -100.0 and 20.0 in steps of 1.0.
Adding your changeable value into the code looks like this:

Foo + *EXAMPLE = Sum

Notice how it has to be dereferenced in order to work.

Const_tweaker is feature gated in Veloren/voxygen. So you need to compile with cargo run --features tweak.

Note: go into voxygen folder for this as the compiler won't accept running --features from the root directory.

This will create a local web GUI at 127.0.0.1:9938 where you can play around with the values.

Web GUI

Guide: Adding Weapon Skills to Veloren

\_ written by @Sam with additions from @James

Define your skill

The first thing you need to do is define your skill and figure out what parameters it will need. It is important to do this as sometimes the new skill can be handled by one of the current character states, and if it’s close, then only 1 or 2 additional could be added to a pre-existing character state.

Adding your skill

Weapon attack skills are handled by character states. As of this edit, these are the following character states (though ComboMelee is in the works and TripleStrike is on the way out).

pub enum CharacterAbilityType {
      BasicMelee,
      BasicRanged,
      Boost,
      ChargedRanged,
      ChargedMelee,
      DashMelee,
      BasicBlock,
      TripleStrike(Stage),
      LeapMelee,
      SpinMelee,
  }

These character states can be (and are) reused for multiple skills. Your new skill may fit easily into one of these states. The state may need to be adjusted slightly to accomodate your skill. The third alternative is the addition of a new character state. All three of these possibilities are covered in the following sections.

If your skill can be handled by an existing character state

Go to common/src/somp/inventory/item/tool.rs

Once there, add an additional attack to the weapon which you are adding an attack to. Note that a weapon’s abilities are handled by a vector, with the first ability tied to M1, the second to M2, the third to skillbar slot 1, and additional ones to more skillbar slots (only skillbar slot 1 might be implemented right now).

If your skill requires modifying an existing character state

Go to common/src/states/.

Open the corresponding character state file. In the Data struct at the top, add the additional fields you need, you’ll also need to add it to every section in the code that updates the character state data (the compiler will throw errors if you forget). Then proceed through the code and change any logic necessary.

Go to common/src/comp/ability.rs.

Look for where the fields of the character state are defined. They are defined in 3 places. The top one determines what fields are read from tool.rs, the second one reads from tool.rs, and the third one constructs the Data struct in the character state file. Sometimes you may only need to add the new field to the 3rd section, though usually you’ll need to add your new field to all 3.

Go to common/src/comp/inventory/item/tool.rs.

Once there add your new attack to the weapon you want. Also, if any weapon already had the attack, update the fields to include the new variable you added.

If your skill requires creating a new character state

Go to common/src/states/.

Create a new file, and give it a name appropriate to the character state you are adding. It can be helpful to start by copying a file from a similar character state. Create the logic for your character state, and determine what fields will need to be passed into the state in the Data struct at the top.

Also, in this section, you will need to determine what kind of attack your character state is. Currently there are systems for melee attacks, projectiles, and explosions. There will soon be support for shockwaves and beams. If you need some other system to handle your attack, ping me (@Sam) for assistance.

Go to common/src/states/mod.rs.

Add your character state to this file, keep it sorted alphabetically

Go to common/src/comp/ability.rs.

In each location every other character state is handled, add your new character state (it can be helpful to search in the file for “leapmelee” or “leap” and just add your character state stuff in each location. The locations to be careful of your logic though are the 3 locations where the fields of your character state are defined (see section for modifying character state for more detail) and where it checks the requirements of entering the character state (though you can almost always just copy what other character states do).

Go to common/src/comp/inventory/item/tool.rs.

Add your new character state as an attack for the weapons you want to add it to. Ensure that the fields in this location match the fields passed into the first section of ability.rs.

Go to common/src/comp/character_state.rs.

Add your character state to the CharacterState enum (ideally include a short description too). Look in the implementation of CharacterState, if yours is described by any of the functions, add it there.

Go to common/src/sys/character_behavior.rs.

Search for any character state (e.g. LeapMelee). Add your character state in the 2 locations the other character states are handled with the same logic.

Go to common/src/sys/stats.rs.

Add logic for how your character state affects natural energy regen here. (If it’s an attack or consumes energy it should disable natural energy regen).

Adding an Ability to the Skillbar

(When skill trees are implemented this section will be updated)

As every weapon currently implemented already has at least 2 abilities, the chances are high you will need to either add your ability to the skillbar or move a currently existing ability to the skillbar.

In the tool.rs file, the ordering of abilities in the vector determines what slot they’ll be assigned to. The first slot is M1, the second M2, the third skillbar 1, the fourth skillbar 2, etc. So when adding your ability, ensure that it is in the correct location in the abilities vector.

Go to voxygen/src/hud/hotbar.rs

Add the weapon type you are adding the third skill to the logic when determining the value for the bool should be present. It should be as simple as adding:

else if let ToolKind::Hammer(_) = &kind.kind {
    true
}

Add an icon in assets/voxygen/element/icons. Skill icons should be 20 x 20 pixels with transparent corner pixels. Check the pins in the assets or veloren-art channel on Discord to find the appropriate color pallet. Ping @Pfau on Discord to see if someone will design a better icon or to get yours approved.

Go to voxygen/src/hud/img_ids.rs.

There is a section around line 140 listing all M1 and M2 icon locations. Add your skill here with the path to its icon. If your skill is going on the skillbar, add your skill/icon pair to the "Icons" section around line 270 instead.

Go to voxygen/src/hud/skillbar.rs.

If your skill is replacing an M1 or M2 skill, add it to the match around line 620 for M1 and around line 700 for M2.

For M1 and M2, add an arm to the match expression around line 730 to make the skill icon fade when self.energy.current() is over the energy drain of your skill. If your skill does not have an energy drain (and should always be available), you do not need to put your skill in this match.

If your skill is going in the hotbar, follow the steps below:

Add a name and description of the skill around line 800 of voxygen/src/hud/skillbar.rs. Just look at the logic of the skills currently there. This will add a tooltip and a name for the skill.

Go to voxygen/src/hud/slots.rs.

Around line 90 add your skill to the HotbarImage enum.

Around line 120 make the appropriate weapon match return the proper skill from the HotbarImage enum.

Around line 130 add your skill to the match. Copy the logic of one of the other skills herebut make energy.current() < ENERGY_DRAIN where ENERGY_DRAIN is the energy drain of your skill. This will make the icon fade when the player does not have enough stamina.

In the image_id function around line 165, add the path identifier for your skill (copy the logic of the other skills).

Animations

Ping @Slipped on Discord. You can also dig through previous MRs (like this one: https://gitlab.com/veloren/veloren/-/merge_requests/1171/diffs) if you want to see how to do it yourself (this’ll show where to add stuff, and show you what code for other animations looks like).

Changelog

Go to CHANGELOG.md Add a line saying you added an attack

Modding the game

You don't always need to know how to code!

Writing a plugin

This guide will show you how to write a simple server-side (and client-side) plugin for Veloren. Please note that both the plugin API and the process for plugin development is under active development and we currently make no stability guarantees about APIs, tooling, etc. If in doubt, please check this page to find the latest information.

It's also important to remember that the plugin API is still very new and not much functionality is supported. We hope that by publishing this tutorial and encouraging people to experiment with the plugin API that we might start to build consensus on a future direction and future features for the API. If you're interested in helping out, reach out to us!

Example code

You can find the code for this example plugin here.

Requirements

  • Knowledge of basic Rust

  • Ability to use simple unix-like terminals and commands

  • An up-to-date instance of Veloren (a local repository is preferred for compatibility purposes)

Note: Having problems? Feel free to ask in #learning on the Veloren Discord server.

Overview

Plugins for Veloren are written in Web Assembly (herein referred to as 'WASM'), a code format originally designed for high-performance, memory-safe web executables, but also perfectly suited to a variety of other applications. This implies the following things about Veloren plugins:

  • They are sandboxed, and so are safe to run client-side automatically

  • WASM does not yet have a well-defined host ABI, so communication with the game engine is event-driven

  • They are portable and will work on all architectures and platforms

  • Plugins are managed by the server and get sent to clients when they connect to a server, so joining a plugin-enabled server is a seamless process.

Setting up

We assume that you're using either a Unix-like system, or some environment with similar properties (like WSL or Cygwin).

Note: from now on, where you see my_plugin, replace this with the name of your plugin.

First, create a new cargo project.

cargo new --lib my_plugin

Plugins have multiple entrypoints (i.e: ways to request things from the plugin) that may be invoked by the game. To ensure we make all of these appropriately accessible to the game, add the following to Cargo.toml:

[lib]
crate-type = ["cdylib"]

Because Veloren's plugin API is unstable, we're going to depend on Veloren's git repository. In Cargo.toml, add the following to [dependencies]:

veloren-plugin-rt = { git = "https://gitlab.com/veloren/veloren.git" }

The veloren-plugin-rt crate wraps the plugin API, event handler derive macros, and a series of other useful bits and pieces together into what we colloquially call the 'plugin runtime' (rt).

Because we need to compile our plugin to a WASM module, first ensure that the appropriate toolchain is installed (and works) by running the following:

cargo build --target wasm32-unknown-unknown

If you get a error[E0463]: can't find crate for 'core', you can install the relevant version of core using the following rustup command:

rustup target add wasm32-unknown-unknown

Veloren's codebase currently requires the nightly version of the Rust compiler (we hope for this not to be the case in the future), and so you also need it. If you are not already using nightly, you can set a directory-specific override for this with the following command (ensure you are in the my-plugin directory before running this):

rustup override set nightly

Packaging the plugin

Plugins are packaged in uncompressed (compression may later be supported, but is not currently) tar archives with the extension .plugin.tar. Each archive contains:

  • A file with the name plugin.toml that specifies plugin metadata

  • And any number of WASM modules (conventionally with the extension .wasm)

my_plugin.plugin.tar
  |- plugin.toml
  |- foo.wasm
  `- bar.wasm

The format of plugin.toml is TOML. The required fields are quite simple, as the example shown below demonstrates (you can omit the comments):

# The name of the plugin (lowercase, no spaces)
name = "my_plugin"

# A list of paths to WASM modules in the plugin (this can be used to group
# plugins together in a rudimentary way until we implement dependencies).
modules = []

# Plugins required by this plugin (currently unsupported, keep this empty)
dependencies = []

We'll want to start off by creating a single module. Let's give it the same name as our project. Start off by adding it to the modules list like so:

modules = ["my_plugin.wasm"]

To package the plugin, we can copy the compiled WASM module from the previous steps located at target/wasm32-unknown-unknown/debug/my_plugin.wasm into a packaging directory of your own making, along with the plugin.toml, and then use the tar command (or your favourite tar-capable archive manager) to package them up. The following command, executed from within the packaging directory, should work fine:

tar -cvf ../my_plugin.plugin.tar *

You might want to automate this process with a script because you'll be doing it often. A simple shell script would likely suffice.

In the future, we'd like to create a cargo subcommand that automates this step, but this hasn't yet been done.

For reference, I just use a simple shell script with the following contents:

cargo build --target wasm32-unknown-unknown
cp target/wasm32-unknown-unknown/debug/my_plugin.wasm build_dir/.
cd build_dir
tar -cvf ../my_plugin.plugin.tar plugin.toml my_plugin.wasm
cd ..

Running the plugin

To run the plugin, simply copy it into the plugins directory in the asset directory of Veloren. The plugin will be sent over the network to connecting clients, so it's only important that it's accessible to the server (or Voxygen if you wish to run the plugin in singleplayer).

In my case, this just involves copying the final archive to assets/plugins/my_plugin.tar within my local repository and running the game.

When a server starts (or when singleplayer is started) you should see messages similar to the following in the console:

INFO veloren_common_state::plugin: Searching "/home/zesterer/projects/veloren/assets/plugins" for plugins...
INFO veloren_common_state::plugin: Loading plugin at "/home/zesterer/projects/veloren/assets/plugins/my_plugin.plugin.tar"
INFO veloren_common_state::plugin: Loaded plugin 'my_plugin' with 1 module(s)

If you got this far, congratulations: you've officially created a plugin for the game!

Handling events

At this point, it's worth taking a brief look over the documentation for the plugin API, here. Although we are depending on veloren-plugin-rt, the similarly-named veloren-plugin-api crate is exported by it for our convenience. We're now ready to write the first event handler for our plugin.

In lib.rs, enter the following:


#![allow(unused)]
fn main() {
use veloren_plugin_rt::{*, api::{*, event::*}};

#[event_handler]
pub fn on_load(load: PluginLoadEvent) {
    emit_action(Action::Print(String::from("Hello, Veloren!")));
}
}

This is worth taking a little time to explain, especially if you're not so familiar with Rust.


#![allow(unused)]
fn main() {
use veloren_plugin_rt::{*, api::{*, event::*}};
}

Here, we import the necessary macros, types and functions we need to write our plugin.


#![allow(unused)]
fn main() {
#[event_handler]
pub fn on_load(load: PluginLoadEvent) { ... }
}

Here, we declare a new functon that accepts a PluginLoadEvent. We use the event_handler attribute to tell the runtime that we'd like to use this function as an event handler that will be called when the event of the specified type occurs.

In this case, the on_load event simply gets called once when the plugin is first loaded during server startup.


#![allow(unused)]
fn main() {
emit_action(Action::Print(String::from("Hello, Veloren!")));
}

We've already mentioned a way to receive inputs to the plugin, via event handlers. How do we act upon those events? Through Actions! An Action is a thing that you want the server to perform, and you can use the emit_action and emit_actions function to have the server perform them.

If you run the server with the newly compiled plugin, you should now see the following in the server console:

INFO veloren_common_state::plugin::module: Hello, Veloren!

If you're running the game in singleplayer, you'll see this twice: once for the internal server, and once for the internal client (once it's received the plugin from the server).

Chat commands

We're going to expand our plugin such that when we type /ping into the chat, the player gets the response Pong!. Why? No specific reason, but it's a good demonstration of chat functionality.

Add the following to lib.rs:


#![allow(unused)]
fn main() {
#[event_handler]
pub fn on_command_ping(chat_cmd: ChatCommandEvent) -> Result<Vec<String>, String> {
    Ok(vec![String::from("Pong!")])
}
}

The only thing to explain here is something that might be a little unexpected given the previous example: the return type.

Every implementer of the Event trait (such as PluginLoadEvent, ChatCommandEvent, etc.) also specifies a response that is required of it. In the case of PluginLoadEvent, the response is simply (), which is why we didn't need to explicitly return anything from the on_load event. The return type for ChatCommandEvent is different, however: it expects either a list of message responses to the command, or an error message should the command syntax be invalid.

If you run the game with the newly compiled plugin and then enter the world, you should be able to type /ping into the chat and receive a Pong! as a response.

Global state

There's a final feature of the plugin API to talk about before this tutorial ends: the management of global state.

If you're more than a little familiar with Rust, that might sound like a scary word: but given that event handlers are themselves 'global' (i.e: they're communicating with just a single instance of the game that loaded the plugin they are in), it also makes sense that any data you want to store about the state of the game in your plugin must also be global.

Thankfully, the plugin API has a feature for this!

To define the type of your global state, you can add the global_state attribute above a type like so:


#![allow(unused)]
fn main() {
#[global_state]
#[derive(Default)]
struct State {
	ping_count: u64,
}
}

It's also important that it implements the Default trait: this is needed because we do not yet provide a way for the global state to be initialised via the on_load event handler.

To access this global state in an event handler, simply add a second parameter to the event handler like so:


#![allow(unused)]
fn main() {
#[event_handler]
pub fn on_command_ping(chat_cmd: ChatCommandEvent, state: &mut State) -> Result<Vec<String>, String> {
    state.ping_count += 1;
	Ok(vec![format!("Pong! The total number of pings so far is {}", state.ping_count)])
}
}

Now any player can use /ping and the server will tell them how many times the command has been used since the server started!

Reducing plugin size

See min-sized-rust for information about reducing the size of Rust binaries.

Future topics

Possible future topics to cover include:

  • More event handlers
  • Modifying entity attributes
  • Persistent plugin state
  • Controlling NPC AI
  • More plugin API features
  • Plugin-specific assets

Internals

| Image source cogs

This section of the book will provide documentation on how inner systems work

Worldgen (WIP)

(this document is constructed from interview with zesterer#3131)

  • Geological stage
  • Filling stage
  • Reshaping stage

Geological stage

  • Using noises "generate" properties like altitude, rock strength, humidity, temperature (...) which are used as inputs. *
  • Run erosion algorithm basing on inputs generated by noises. **
  • Produced output is an altitude map with a few other simple attributes like humidity (but not much else). ***

(It is saved as .bin file in the game's assets)

* Noises can be imagined as random texture. You can see an example of translating noise levels straight into heightmap here click here to open an image

** You can see a nice simple example of erosion algorithm here (you can skip to 3:52) click here to open youtube video

*** You can imagine altitude map as bare map with only "shape" - river valleys, basins, mountain ridges etc.

Filling stage

(Happens when you actually run singleplayer/server)

Using data from Geological stage places lakes and rivers.

Compute tree density, desert dunes...

(e.g. Using "shape" and some metadata it is figuring out if some hole in the ground should be filled with liquid)

The output looks like this:

worldgen_simchunk_def
(at this stage, contains_waypoint, path, cave, sites and place are empty)

Reshaping stage

| local, temporary, natural elements

(Exact quote from zesterer is pretty self-explainatory:)
So these are things like cliffs and caves
We layer these on top of the world using a variety of techniques
Exactly how isn't too important
Caves and paths are actually done using the same system, known as the "way" system
It basically just provides a method of connecting up long elements between chunks

Wiring

General system overview

Shallow definitions:

  • Circuit component is a backbone of whole system. It stores information about from where and to where data has to be passed.
  • Wire is part of Circuit (one circuit can have many wires) it stores data from what entity and under what field name value has to be passed to what entity and field name.
  • Wiring component is a "worker" it knows what data it has and what has to be performed when executing it.
  • OutputFormula takes inputs (and own config) and produces output.

If something can't be calculated, but given place has to result in some value - it will result in f32 0.

Each wiring component has 3 informations.

  1. Inputs (data injected by Circuit)
  2. Outputs (information of how to compute output)
  3. Actions (information about what should happen if inputs are in given state)

Each wiring tick has 3 stages:

  1. Compute (take each Wiring component, feed inputs into output formulas, produce outputs)
  2. Transport (get outputs of previous step and using Circuit pass them around)
  3. Dispatch (get inputs (note that we are using modified state by transport) and dispatch logic)

Compute

  • Take all wiring components and map them
  • Calculate each outputs output formula
  • Construct outputs with type HashMap<E, Hashmap<F, V>> where:
    • E is an specs::Entity that holds certain wiring component.
    • F is a String representing field name.
    • V is a resulting value of OutputFormula. (atm all results are f32)

Transport

  • Take circuits and for each circuit take attached wires *
  • Take every wire and for each wire *
  • Get from what entity, from what field, to what entity and to what field from wire.
  • Perform something like to what entity.inputs[to what field] = from what entity[from what field] **

* It is basically "take every wire" in loaded server data

** Keep in mind that we are using product of Compute step as right hand of this simplification.

Dispatch

  • For each action in every wiring component
  • If wiring action formula output* is more or equal to action threshold...
  • Dispatch each effect attached to the wiring action

* Calculated using inputs

Output formulas

Constant

Outputs constant value.

Input

Outputs value of input field under given name.

Logic

Allows composing output formulas. It takes left and right output formulas which are calculated, and then performs logic on them.

Available logic kinds (I refer to left and right output as side):

  • Min - return value of lower side.
  • Max - return value of higher side.
  • Sub - return value of left - right.
  • Sum - return value of left + right.
  • Mul - return value of left * right.

Sine wave (not implemented)

Takes server time and returns sine of it.

OnCollide

If physics captures collision for attached entity (touching_entities) returns given value.

OnInteract (not implemented)

If something interacts (e.g. player is "pulling" lever) returns given value.

OnDeath

Returns value * count of entities that died in last tick in given radius.

Actions

Spawn projectile

Spawns projectile using projectile constructor.

Set block collidability (not implemented)

Modifies properties of block on certain coordinates.

Set light

Changes LightEmitter properties of attached entity.