This Week In Veloren 96

This week, Veloren 0.8 was released! We take a look at how the release went, and
some of the feedback. We also hear from /u/o11c about some of their experience
on large multiplayer projects.

– AngelOnFira, TWiV Editor

Contributor Work

Thanks to this week’s contributors, @xMAC94x, @Yusdacra, @ubruntu, @Nicknakin,
@XVar, @azymohliad, @AngelOnFira, @imbris, @Sam, @Snowram, @Treeco, @zesterer,
@timsueberkrueb, @Pfau, @DaforLynx, @James, and @Slipped!

0.8 was released last weekend! There is a new article on GamingOnLinux about
the Veloren 0.8 release. This was our biggest release ever, and it amazed the
entire core team that so many people came out. @ubruntu has been working on
small bug fixs and is currently working on adding chat commands for inviting,
kicking, promoting, and leaving a group. @Snowram worked on adding and animating
fish.

@Christof asked around for help on how to die less quickly in combat against bow
wielding bandits, and then collected the answers into a beginner’ hint section
of the book. They also started to work on a minimum viable solution for selling
inventory items that you gather over time and buying stuff you need instead.
@Christof also got teloren, Veloren’s terminal frontent, working on 0.8.0.

@Sam worked on abilities being automatically greyed out when you have
insufficient energy. Previously, the UI had hardcoded values it checked, leading
to some abilities greying out at incorrect values since they were checking
outdated numbers. @Sam also fixed a bug with the leap potion of the bow’s
skillbar ability, and did a few miscellaneous combat tweaks. These primarily
affected energy regen of melee weapons, and the bow’s primary attack.

@Yusdacra improved the Nix dev environment so now it has a Cachix setup meaning
that Veloren dependencies can be stored globally and everyone can benefit since
they won’t need to compile the same stuff again. It now also sets up git-lfs
automatically, adds a direnv setup, and includes nixGL commands for non-NixOS
users. This means they can actually run Voxygen now, since OpenGL programs on
non-NixOS compiled via Nix is problematic. The Voxygen package now includes a
.desktop file too.

Veloren 0.8 Launch

Veloren lauched 0.8 this weekend! This launch includes some massive new systems,
as well as a multitude of other changes. Here is a small part of the changelog,
but make sure to check out the full changes
here.

- New level of detail feature, letting you see all the world's terrain at any view distance.
- Point and directional lights now cast realistic shadows, using shadow mapping.
- Added leaf and chimney particles
- Some more combat sound effects
- Beehives and bees
- Fireflies
- Fullscreen modes now show two options (exclusive and borderless)
- Added banlist and `/ban`, `/unban`, and `/kick` commands for admins
- A new dungeon boss (venture there and discover it yourself)
- Adaptive stride setup for more dynamic run behavior
- Several new animals
- Buff system
- Flight
- Persistent waypoints (start from the last camp fire you visited)
- Ambient SFX emitted from terrain blocks
- Initial implementation of real-time world simulation
- Travellers that explore the world
- Innumerable minor improvements to world generation
- Aurora Borealis (localised entirely within the kitchen)
- Block-based voxel lighting
- Animals now have customized attacks and AI

Launch party!

This was not only the biggest update to Veloren yet, but also the biggest launch
party. At the peak, there were 112 players online. This gave us a lot of insight
into how scaling works. Here are some quick facts:

  • 5 minutes before the release party, we had to restart the server as we forgot
    to increase the player cap past 40.
  • We used a 32 core dedicated server from Hetzner. Although we had 128GB of ram
    on the server, we only used around 5GB.
  • The release party had the peak number of players 30 minutes after it started.
  • Normally, release parties go for around 2 hours before we get down to fewer
    than 10 players. This time, we went down to 30 players after two hours, and
    hovered there for around 24 hours.
  • We used around 100GB of bandwidth for the launch.
  • The server cost around 4 Euros, or $4.84 USD for the 7 hours it was up.
  • Players started reporting higher latency and some rubber-banding issues at the
    peak player count, but it was still playable.

We learned a lot from the release, and got a ton of important feedback. The most
important item is how balence works for new players entering the spawn town. It
was very difficult to spawn in and make it out of the initial area alive. There
were also a lot of comments about how we could work on other character balance
in the future.

From the stress test and release, we found many areas that need improvement on
the scalability side. Our main game thread struggled a lot, which implies that
we aren’t assigning tasks to other threads as much as we need to be. Futher,
there are lots of improvements to be made within physics, AI, and other
compute-heavy tasks. We also need to work on how we are sending data over the
network to players. There are likely massive gains we can make, which would
allow bandwidth to be improved significantly.

What People Are Saying About the Launch

GamingOnLinux

Release party just now was chaos, in a good way 🙂 I think there were around
100 players fighting a giant dragon

– Julius

Installation was super-smooth though, totally unexpected. It’s just ~125MB, I
expected something measured in GB. But then, makes sense, voxels + procgen…

– riidom

Going to snag this for my boy to check out. He was watching all 15 minutes and
really liked what he saw. Great to hear this in Rust!

– techieg33k

Looks amazing. Definitely the most impressive FOSS RPG project that I have
seen to date (no offense to Flare).

– nate

The timing of this release is funny for me because I just played the game with
my kids yesterday before I knew there was a big update. I was pleasantly
surprised to see a lot of things added to add interest to what was a more
empty game. We used the Airshipper launcher which updated the game and then we
played on a local server that was launched by browsing to the installation
folder and running the server-cli program.

– 14

Reddit

Veloren is shaping up beautifully, can’t wait to see what’s to come.

– /u/SmurfTaters

Idea Drop by /u/o11c

Context: On a Veloren 0.8 release post to Reddit, /u/o11c gave some insight
from their own development experience. Although not all of it will be the path
that Veloren takes, the points are valuable nonetheless.

LPT: don’t ban botters too harshly, you want to get them to work for you. Also,
be aware that people will use a bot client just because they want to avoid some
particular pain point with the official client. So, steal their patches. Finally
… no matter what you do, you will never block all bots. So instead focus on
making sure bots don’t ruin people’s fun.

Also a random dump of other things I’ve learned, mostly from doing everything
the wrong way (some items already filtered because I saw you did them right
already, but I admit I haven’t looked hard yet):

  • Never use TCP. ISPs will rudely inject RSTs into long-running streams, and you
    can’t realistically ask people to install a firewall rule to drop them.

  • Likewise, always encrypt everything. QUIC made life easier but there are
    numerous alternates. Multiple streams are probably overrated.

  • Always have a “frontend” server that proxies client connections from the
    start; this allows transparently moving them to a different backend server
    when they move between in-game regions (which is essential for scalability).
    You cannot rely on clients to successfully connect to a second server
    themselves. Not all players need to use the same frontend server either. Load
    typically isn’t an issue, but this may be useful for working around crappy ISP
    peering in some countries.

  • Think about ways players can organize:

    • A friend is a one-way, individual, relationship. There should be
      user-labeled levels of friends (“rivals” might be a level).

      • Some of this should sensibly be viewed from a web interface, not just
        in-game
    • A guild is a large long-term group of many players, possibly with ranks.
      Sometimes it would be convenient if it’s possible to join multiple guilds,
      but not too many.

    • A party is a small short-term group of players intending to do combat
      together; they may or may not be part of the same guild.

      • In practice, people often treated them as long-term. I suspect that it
        would be smart to systematically discourage that (e.g. by automatically
        breaking parties on logout … but perhaps adding them to a “recently
        partied” friend group so that people can re-party them easily), and
        encourage creation of guilds.

      • Most dungeons can reasonably be set to take (or even create/enforce) a
        single party, but particularly large dungeons might not, since it’s hard
        to keep track of too many people.

    • People might cooperate in combat even without joining a party

    • People or parties might compete in combat (“kill-stealing”)

    • Make sure people who play support get xp/loot appropriately

  • Figure out (in-world) instancing so that guilds/individuals can have private
    space, multiple groups can run a dungeon at once if enabled, …

  • Figure out how accounts can have multiple characters, how that interacts with
    friendship and server privileges, and what happens when they are deleted.
    Please do it in a sane way, I still have nightmares of that code … (hint:
    “character id” should almost always be what is used, “account id” might not
    even exist at all except as a database-internal detail)

    • It may even be feasible to share accounts between official servers and
      third-party servers, but this may cause concerns about identity leaking if
      not done carefully
  • Consider eliminating username/passwords entirely in favor of public and
    private keys; invent a system of securely sharing an account between your
    computer and your phone that is immune to user error. (computer to phone is
    easy enough with a QR code, but you need two-way communication – if you think
    you don’t, you don’t understand the problem yet. Perhaps use the server as
    mediator? Should each device have its own private key that the server
    aggregates into a single account?)

  • If you allow in-app signup, someone will spam you and create millions of
    accounts and waste both storage and namespaces. Use a web browser so it can do
    it with captchas etc.

  • Always make sure each logical packet’s size is known directly (this will
    generally not be the same as underlying UDP packets). This allows both
    skipping of totally-unknown packets, and size increase of existing packets.

    • It may be useful to have a special packet that says “wait for the next N
      packets (or bytes?) together before handling any of them”
  • Have a “display a message” packet that works no matter what the state of the
    client (on the login screen, in-game, …). Possibly they should include a
    textdomain (I favor client-side translation – just send the .mo files as part
    of your updates (remember to include the fallback language-without-country
    stuff if you download only needed languages); that way, the client can log the
    unlocalized version; multiple languages in a single process is hard anyway).

    • This should be distinct from “display message in chat” (which may be
      translated if it’s from the server, but set no textdomain if it’s from a
      player), “display message in NPC dialog” (also translated). Support for
      formatting and pluralization must be included.

    • You will have at least 3 textdomains involved (client, server code, server
      script), might as well make it unlimited (different scripts (dungeons?) or
      even different parts of the server code might have different textdomains) –
      think about people running a fork.

  • Make “connection to the server” a separate class than “info about the player”,
    even though they both need to point to the other (Rust will probably fight you
    here too – neither owns the other (connection is owned by the event loop;
    player is owned by the map), but the former limits the latter’s lifetime). The
    latter should not even exist until auth’ed (no more “spoof or eventual segv”
    if you don’t tape up all the holey ducts).

  • Player shops are best done with special server support, not pure scripting or
    client hacks based on the trade dialog. Trust me, it has been tried.

  • As a rule, always limit things by configuration-file policy, not code (user
    name length, packet size/buffer/throughput; in particular, not having a hard
    inventory limit removes a lot of special cases in scripts)

  • Make a “transaction” script command to gain/lose items at once and handle
    failure cleanly (consider stacking and equipment)

  • Have a general-purpose “object inventory” class that gets reused sanely, with
    a special UI for moving objects between them arbitrarily. Don’t specially
    implement player inventory, player storage, chest inventory, monster
    inventory, …

    • A few cases are special, but should still share a lot: shops (have prices
      and maybe randomness too?), equipped items (know what slot they’re equipped
      in – note, I’m increasingly leaning toward equipment-separate-from-inventory
      as opposed to equipment-as-part-of-inventory; really only the “transaction”
      script command should care), mob drops (might be random, maybe combining sub
      inventories?) – maybe use “struct of arrays” rather than “array of struct”?
      I wish languages gave support to that.

    • Make everything stackable if all properties match (padding bits are evil;
      upgrade slots are tricky), even if you want it to display unstacked (Rust
      probably helps you here)

    • Large arrays are slow! we’ve all played games like that. At the very least,
      if you display by category you should consider storing it by category.

  • Whenever you have jagged “array of arrays”, be sure all the arrays share an
    allocation to keep the cache happy (also applies to set/map contents). E.g.
    load item definitions in the order given, then make a map pointing to them
    (Rust might fight you here, but with split arrays you probably don’t have
    usable references anyway). Packet dispatch similar since IDs should be chunky.

  • Lots and lots of “hard typedef”s for all the IDs/indices floating around where
    pointers are inappropriate.

  • Consider what happens to a “high-score list” if a character is deleted (and
    possibly recreated) (merge this into the other list’s entry about charid).

  • XML is a wonderful thing if you keep it away from humans.

Cave exploring at night. See you next week!

Support our devs!

Veloren Open Collective

This Week In Veloren 91

This week, we have lots of writeups. @Sam talks to us about the buff system.
@Adam is working on designing an improved alignment system to improve hostility
calculations between entities. @xMAC94x wrote about the CI issues we’ve been
having for the past few months, and what has been done to resolve them.

– AngelOnFira, TWiV Editor

Contributor Work

Thanks to this week’s contributors, @xMAC94x, @Sam, @Bbenton91, @imbris, @Adam,
@Capucho, @AngelOnFira, and @zesterer!

This week, @James has been working on a way for SFX to be mapped to blocks. This
will be used for ambient sounds like crickets, birds, etc. This system is still
in the works. @McKol has been working on updating information about Veloren on
the IGDB, which Twitch uses to categorize its games.

@Pfau created 2 new sets of armour, finished the buffs UI, and added the first
underwater sprites. This week he’ll create box art for Twitch, draw some ring
concepts, and add more underwater sprites. @Sam has been doing a lot of work on
the buff system. @Gemu finished up some sword models, and is working on a few
new animal models to improve biome diversity.

Buffs by @Sam

Hey, @Sam here. Recently I’ve been working on player buffs. Buffs are separated
into several components:

  • kind: the main identifier of what a buff is. It is used to construct what
    effects the buff has. Kinds could include bleeding, enflamed, crippled, and
    stunned.
  • data: contains data common to all buffs. Currently consists of the buff
    strength and the buff duration, but may expand later to include other fields.
  • effects: what effects the buff has. The currently implemented effects are a
    health change over time (so this covers both damage and healing), and changing
    max health.
  • time remaining: time remaining on the buff. It is different from the
    duration stored in data though because the duration in data is static and
    references the initial duration.
  • categories: a list of descriptors to the buff, such as physical, heat,
    environmental, etc. They don’t currently have a use, but will be used later
    when spells and other abilities are added that may have effects such as:
    immune to all heat-related debuffs, or something similar.
  • source: what applied the buff. If it’s a player, this can be used to award
    xp if something dies due to damage over time effect.

Buffs will not stack, but instead the strongest buff of each kind will apply its
effects, and the other buffs of that kind remain “inactive”. While inactive,
they still tick down their duration, and will replace the stronger one if it
decays before the weaker one.

Note that the stacking of buffs applies to buff kinds and not buff effects, so
for example, you can be under multiple damage over time effects, such as
bleeding, enflamed, and poisoned, you just cannot be under the effects of
multiple bleeding debuffs.

The branch that added buffs was primarily focused on getting a buff system into
the game. Now that it has merged you won’t notice to much difference yet.
Currently, the only buff you will encounter for now is a bleeding debuff, which
has a 10% chance to be inflicted by melee attacks. However, more buffs are on
the way. Specifically, food is being reworked to provide a regenerative buff
instead of an instant health restoration.

Alignment and Hostility by @Adam

This week I began looking into how we could refactor the alignment system to
support more complex scenarios. I haven’t any solution in place yet, but I’m
going to provide a bit of background on the problem itself.

Current State

The alignment system as we have it is just a simple enum:

pub enum Alignment {
    /// Wild animals and gentle giants
    Wild,
    /// Dungeon cultists and bandits
    Enemy,
    /// Friendly folk in villages
    Npc,
    /// Farm animals and pets of villagers
    Tame,
    /// Pets you've tamed with a collar
    Owned(Uid),
    /// Passive objects like training dummies
    Passive,
}

However, this is too restrictive, even for simple ownership. Imagine something
is Owned: despite having the Uid of the owner, we don’t have the information
about who that owner is hostile to. If we wanted to have owned entities to
divert to the owner’s hostility, then we would then need to pass possible
owners’ hostility information for both entities when we wanted to check
hostility. And then what happens if those owners are also owned? You can start
to see that this current implementation needs to be more robust. From here I
will outline what the system needs to be able to do.

System Requirements

  1. System needs to support dynamic Settlement hostility. A world may have any
    number of settlements, but an aligned entity may only be aligned to one
    settlement at a time.
  2. System needs to support racial hostility. An aligned entity always has a
    race. Races have varying hostility toward other races.
  3. System needs to support individual hostility. An aligned entity may have
    overrides for specific other entities. For instance, if a neutral NPC is hit
    by a player entity, that NPC should be able to become hostile to that player
    entity.
  4. System needs to support ownership. An aligned entity may be owned by another
    entity. If an entity is owned, it may or may not want to divert to the
    owner’s hostility.
  5. A reputation system may or may not be implemented. If it is, this system need
    to be able to incorporate that.

Analysis

An additional requirement that I want to achieve is a single lookup between
entity Uids, something like:

hostility.is_hostile_to(Uid)

So we do not need to pass in the other information that an entity may or may not
have, such as Ownership or Settlement data whenever we want to check
hostility.

Looking forward, we will likely have many components (i.e. Race, Settlement,
Ownership) that will affect one entity’s hostility to another. Because we
don’t want to require hostility checks to ask for all these different
components, how do we manage all these memberships when these components are
updated?

So really, this problem boils down to two challenges:

  1. How do we effectively manage the hostility of an entity given the various
    sources of that determine it, without requiring developers to remember to
    update hostility any time they update those other sources?
  2. How do we keep the hostility check call as simple as possible? Ideally
    entity_a_hostility.is_hostile_to(entity_b)

In the next few weeks, hopefully we can find some resolution to these questions!

Fixing CI by @xMAC94x

Two months ago we broke our CI. This included documentation generation and code
coverage. We lost information on how our test coverage was doing and our
documentation was frozen. Fixing it required some fiddling with various public
crates and some communication with external maintainers. We even found a bug in
Rust itself while doing so.

Long story short, we needed to switch our Docker CI image from debian9 to Ubuntu
18.04 as the git lfs package was causing problems on Debian, and newer
versions require a higher glibc version. We had to upgrade the Rust toolchain
as this fixed a problem in macro handling which caused a big in the
documentation cargo doc.

To fix tarpaulin we had to fix multiple errors. First of all, wayland-client
0.23 was causing problems so we had to update this to 0.27. However, we depend
on some crates which have not done this update, so we did it ourselves. As
glutin, winit, and conrod are quite dependant on each other. packed_simd
also had a problem that was fixed. The
RustC compilation
problem

we found isn’t yet fixed, so I introduced a workaround in our affected vek
code.

All in all, many things broke – I hope we can simplify our custom clone
repos
in the future: winit, vek, gfx-rs,
rusttype, and conrod can hopefully all be replaced with iced. Now our CI
is faster, down from 55 mins to 35 for a full run. I will look at it further
after I do some performance fixes for Veloren 🙂

The mountains to explore. See you next week!

Support our devs!

Veloren Open Collective