This is the first post of (hopefully) many to come from my Python program that pulls Steam Community Market data for video game skins. If you want to learn how I got all this data, check out that tutorial.
For those who may not play Rust, I’ll give a quick overview of the game to give you a better understanding of what “skins” are and what they mean to people who play Rust. Rust is a survival game made by FacePunch Studios where every player spawns on the beach of an island with nothing more than a rock, a torch, and a quickly depleting store of calories. Spawning into the game is the only part of the game with any structure. Everything from here on out is up to you and the few hundred other players who are also stranded on the island. Generally, people will chop down trees, collect some stone, and build a base to have somewhere secure to stay and stash their loot. However, there are no rules on how to play Rust. If, say, you don’t want to spend time chopping trees and gathering stone and opt instead to craft a bow and arrows and kill someone who is collecting these resources and take them all for yourself, you can do that. If say, your neighbor keeps chopping down all the trees in the area, you can blow up his base with rockets. If you want to make a hotel for people to stay in, you can do that. If you want to construct a gladiator arena or build a wall across the entire island, you can do these things too.
The game play of Rust doesn’t come from finishing a level or defeating a boss. The game play is emergent from interacting with other players on the island. However, the game makers certainly have a direction for the game in mind. Sure, there are BBQ grills and guitars, but there’s also guns. Lots and lot of guns and other ways to defend your base and destroy your enemies. And there’s nothing sweeter than conquering the island and looking cool while doing it. Looking cool is where “skins” come in.
In Rust, players can get skins for their items to make them look different than the default, run-of-the-mill look all the other players have. The only way to get skins is to purchase (with real money) them from FacePunch Studios or to buy them (again, real money) from other players who have the skins you want. Aside from looking cool, skins don’t make your guns better or your armor stronger. Despite gaining no real advantage aside from vanity and flex, players will spends tens to even hundreds of dollars on single skins to stand out and look unique.
As outlined above, there’s two main ways to get skins. The first is to buy directly from FacePunch Studios. Every week (or so), FacePunch will pick a handful of skins submitted by artists. For one week only, players can purchase these skins for a fixed price from their Store, generally $0.99 between $1.99. A portion of every sale goes back to the artist. However, once a new week rolls around with new skins, the skins from the previous week are gone from the Store. If you want to get an older skin you need to go to the Market. The Market lets players buy and sell skins directly to one another. A seller can set any price they want and buyers can purchase these or submit limit orders to buy a skin at or below a given price when/if one become available. This is not so different from the stock market where traders buy and sell shares in companies. However, Rust skins in particular are quite unique with their Store feature. The one-week-Store means there is always a fixed supply of a given skin after that week. Afterwards, market forces on the community Market dictate the price of these limited goods. What are these market forces? Let’s find out.
I collected all of this data at the start of March 2019, so skins and prices are til then. I have data for 1,642 items which are mostly skins but also things like cloth/wood/metal and gambling boxes.
Aside from pulling all the data from the Market as outlined in my other post, quite a bit of clean up, extra data collection, and data guessing went into this data set. The Market gives me the daily median price and total volume (number of sales) for every item. However, that is all the info it gives me. Once I collected all of this information I then painstakingly went through and added more data to each item such as, what item was the skin for? What type of item was it (eg, gun, armor, clothing)? How good (workbench tier) was the item? I automated this the best I could but there is zero consistency on item naming. Any skin artist who puts the type of item in the skin name, thank you.
I really wanted to have the original Store price of each skin to see how well or poor and skin’s price performed. However, this information is not really available. I reached out to FacePunch everywhere I could but was just left on “read”. Oof. I turned to Reddit for advice. Shout out to u/Regular_Merc who suggested the way back machine which keeps track of webpages over time, the Store being one of them. This worked okay but the records were very spotty. u/avantus1 suggested the youtuber “ThatGermanGuy” who has been reviewing all the Rust Store skins since just about the time skins were introduced. This was quite the gold mine however in the early days he wasn’t the best at saying the skin’s prices. More recent videos (the past year or so) have the price overlaid on every
skin which has helped tremendously. Thus, for skins I did not have the original price for I filled it in with the average of all known prices for that skin. This works great on average, however, without rhyme or reason Facepunch would sometimes double or triple the price of one or two skins on the store. So keep in mind, on average, my original prices are pretty good but may be low for about 5% of skins.
Finally, I’m currently in New Zealand. Despite my many attempts the Market would only give me back data in New Zealand Dollars (NZD) so everything here is in NZD. Store prices that were in US dollars I converted with a rough exchange rate that comes close to what the Store uses. If you must convert things to USD, roughly multiply by 2/3, eg $1.00 NZD is (currently) $0.66 NZD. Since the majority of my audience is in the US, I’ll do my best to convert in text whenever I remember.
Also, the Market (10%) and Facepunch (5%) take a 15% cut of all sale prices. I did not factor this into the expected returns so they are all 15% less than what you see here.
|Metric||avg ± sd|
|Store Price ($)||$2.81 ± 1.25|
|Market Price ($)||$5.11 ± 10.10|
|Initial Change ($)||$-1.33 ± 16.47|
|Max price (day)||206.56 ± 209.31|
|Appreciation ($)||$2.58 ± 22.66|
|Swing ($)||$14.53 ± 40.53|
Here’s a general overview of the metrics I collected or calculated. As you can see, the standard deviation of nearly all the metrics is large compared to the average. This means our data is very spread out. Let’s dive into this data further to get a better feel for what’s going on because just averaging across all skins isn’t going to tell us much.
Let’s start simple before diving into more complex analysis. Can you just buy skins on the Store then sell them on the Market for a profit?Yes, this strategy would work but don’t expect to get rich off it. Below you can see every skins’s price change from its original store price (Original) to its average market price (grand average; average of its median day’s price from every day it was on the market). You’ll see there are set price points for skins on the Store, hence many skins cluster up. However, data is much more spread out on the Market. The Rust red line is the median of all the original prices and all the Market prices. As you can see, generally, prices increase from the Store to the Market (Wilcoxon, P < 0.001). You’ll see some Store skins drastically shoot up in price (steep, positive slopes), so much so that I had to limit the y-axis to see anything. However, there are plenty of skins that also lose value (negative slopes).
We can zoom out and instead of plotting every skin, just look at the price distributions of the two markets. Below you can see the Store distribution (KDE) in black and the Market in red. It’s clear the Market prices are shifted rightward relative to the store, meaning their prices tend to be more than the Store. On average, you can make about $2.31 NZD or $1.53 USD. However, due to the massive long tail (skew) of the Market data, the median is a better metric than the average to use. Unfortunately, you’re looking at returns of about $0.89 NZD or $0.59 USD.
You may be wondering why I have a metric for the initial price change, or how much an skin’s price changes from the first day it is on the Market to the second day it is on the Market. When I was exploring the data I found so many skins that had their maximum price on day 1 of trading then drastically dropped on day 2 and would rarely recover (64 skins dropped by more than $10, 8 skins by more than $50). The Racing Stripes Chest Plate had the most drastic initial drop of $567.67 NZD or $374.66 USD. However, for all these items, day 1 trading volume tended to be very low and usually only from a single sale.
You can see from the top table that the average skin lost $1.33 NZD from day 1 to day 2. However, the massive standard deviation of $16.47 NZD warrants further investigation. Below, you can see that most skins will lose value from day 1 to day 2 despite gaining value over their Market lifetime.
One way to do this is see when skins have their maximum Market price. Furthermore, this metric will be quite informative as to when skin’s have their maximum price which would tell Market participants when the best time to sell is (or worst time to buy). Below you can see a histogram showing the distribution of the max price days. Strikingly, there is the pile up on the first few days of trading. This is further confirmed by looking at the mode which is zero (day 1). 275 of the 1642 skins (17%) have their maximum price on trading day 1. Why so many skins lose value from day 1 to 2, I have no real theories. The volume on day 1 tends to be very low so perhaps people hold out selling them on the Market, which makes buyers desperate and they place ridiculous bids. If you have any ideas, leave a comment. Otherwise, there doesn’t seem to be a best time to sell a skin you’re holding onto. Generally it looks like skins have their max price earlier, < 200 days, rather than later.
Just while we’re looking at it, we can also see when a skin has its lowest price indicating the best time to buy/worst to sell. These data are a little less perplexing but still not obvious. A lot of skins have their minimum price on day 1 and day 2. However, there’s a much smoother distribution compared to the max price days. Generally, the minimum price is earlier, < 169 days, than the maximum.
These data give us opposing advice. Many skins have their max price early on while many others have their minimum price early on. Moral of the story? “Market timing is fool’s errand” – some smart guy.
With these massive swings early on in mind I came up with a few different metrics to try to capture how a skin’s price changes over time. One is just called appreciation where I average the price of a skin on the first five days and the most recent five days and take the difference. This is similar to the Store vs Market analysis though only relies on Market trading data. Can you make money just buying early in the Market and selling later? Yes. You will tend to make a little more money if, instead of buying skins on the store (avg, $2.31), you buy them on the first few days of Market trading and sell them later on (avg $2.58 NZD or $1.70 USD). However, results may vary with the median price change being better for Store to Market flip ($0.89) compared to Market appreciation ($0.54 NZD or $0.36 USD). However, this difference was not significant (Wilcoxon, p = 0.064).
The last overall metric I looked at was Market price swings. This is simply the difference between the lowest price a skin had on the Market and the highest price a skin had on the Market. This would translate to the maximum possible profit you could make trading skins on the Market assuming you had perfect timing (which is impossible). As you can see, for most skins you can actually make a decent bit of money with an average swing in price of $14.53 NZD or $9.59 USD and a median of $6.96 or $4.59 USD. While no crystal ball, this data can help to inform your decision of when to sell a skin. If its price has risen about $5.00 USD from its low, consider selling. Or if you’re a gambler, wait for a $10.00 USD swing before selling.
While timing the market is impossible, we can at least look at overall trends and make more informed decision of when to sell. To do this I looked at how many days elapsed from the time a skin had its maximum price to the time it had its minimum price. If this value is positive a skin had its minimum price first then its maximum price at a later date. If this value is negative a skin had its maximum price early on then fell to its minimum price later on. As you can see below we get a very weird, non-normal distribution (D’Agostino and Pearson’s, p < 0.001). At the surface we can see it’s skewed to the right (skewness, -0.69; p < 0.001) meaning skins will likely have their lowest price before their highest price. On average, a skin will reach it’s maximum price 37 days after hitting its lowest price. However, keep in mind the median is 71 days and is a more reliable metric for this distribution of data. Thus, wait 71 days after a skin has hit its all time low to sell for maximum profit. However, what if a skin you want is riding high and you don’t want to pay a ton of money? The first peak to the left of zero is at around -35 days with the second, wider peak around -250 days. If you see your favorite skin dropping in price, wait about 35 days for it to reach its low. If it’s still not low enough, check back in eight months.
Before getting into very granular analyses I have one more idea for capturing the overall Rust skin Market as well as answering one of the more sought after Reddit questions. I posted in r/playRust asking what people wanted to know about the Rust skin market. u/Stilnox1012 and u/Vativ posited that time of year may influence skin prices. u/Vativ, rather confidently, said summer would be the worst time to buy skins as everyone is spending their money on other video games when Steam has the summer sale (where most games get a massive discount). Conversely, Christmas is the best time as people [kids] have higher disposable income [from Christmas gifts]. u/Stilnox1012 thought both summer and Christmas would elevate the price of skins. This was a very fun question to try and answer. I needed a way to capture price fluctuations over time using dates rather than just time on the Market as I did above. To do this I made a Rust skin index fund, similar to how an index fund works for stocks.
Basically, an index fund tracks the price of a group of stocks. For example, there is an index fund that tracks the biggest American companies like Apple and Google called the S&P 500. This gives investors a good idea of how a whole market is performing based on many companies rather than the more granular (and noisy) performance of a single company. Rather than company’s share prices underlying the index I used skins as companies and skin prices as stock values. Thus, my COBALT index fund tracks the performance of all Rust skins. For example, if, on average, skins are going up in price, the index will go up too. If skin prices are stable, the index will be flat.
I’ll spare your the details of what didn’t work in creating this index and tell you what I ended up using. For every day there was skins trading I got the average price of all traded skins for that day. I also go the total volume of trades on that day. For the actual index I just used the average price of all traded skins for a give day. This will average out the number of available skins on any given day (rather than a sum, like the DOW). For volume, however, I didn’t want to just use the total volume each day as more skins being added over time would likely mean more volume as result. So the volume is the total volume of the day divided by the total number of skins that had a sale on that day. There’s a lot to unpack here so lets dive in.
To orient you, the top plot is the COBALT index value while the bottom plot is the trading volume. The solid line for the COBALT is the five day rolling average while the lighter line underneath is the daily values. In addition, the vertical red lines indicate Christmases while the grey lines indicate when the summer sale occurred (roughly June 20th). The first thing you probably notice is the huge jump in volume at the end of 2016. This is when artist skins from the Workshop were first added into the game. Before that there was only some color-scheme-altered skins made by Facepunch available. For analysis sake we might as well ignore everything before 2017 though I kept it in because it’s interesting. After the first meaningful summer sale in 2017 we can see a pretty massive rise in prices peaking at the end of July and falling back by August/September. A similar but smaller series also occurred in 2018. Next we can look at Christmases. Indeed, it seems like right around Christmases in 2018 and 2019 skin prices started to rise had a sharp rise before returning back in February. Thus u/Stilnox1012 was right on! The best time to sell skins is right after Christmas or in July.
The annualized return of COBALT is 66.75% whereas the S&P 500 over the same time was only 8.29%. Invest all your money into skins.
Those with keen eyes may have noticed the volume bar chart is jagged. If we zoom in we can see an oscillating signal. As I’m sure many of you can guess the peaks are likely due to days of the week, with more people spending time on video games and trading skins on weekends rather than weekdays.
To test this I looked at the volume of trades for every day of the week. I took out data pre-2017 before the skin market really started up (without this bi-modal distributions result). 0 is Monday, 1 is Tuesday, and so on until Sunday which is 6. We can see that volume does indeed tick up Saturday and Sunday compared to all other days of the week (one way Anova with Tukey’s post-hoc, p’s < 0.001). However, just because volume is going up, is price going up as well?
I did the same as above but this time with the COBALT index. However, prices of skins are immune to day of the week effect and it doesn’t matter what day of the week you buy/sell on (p = 0.89). Quick note, you can see the weird distribution bulge on Thursdays (3). This is the day of the week new skins can be traded on the Market. I imagine this is also influenced by those skins who have their maximum price on trading day 1 which almost always is a Thursday.
All our analysis so far has been very big picture and looking at data from all skins. However, this is likely not the best strategy for making money. There are skins for guns and skins for armor but also skins for refrigerators which don’t get much use. Or there are skins for doors which are not only used constantly but give your base a unique look. Can we find out what factors influence a skin’s price by looking at more detailed information? Let’s find out!
As you progress through Rust you need better workbenches to craft better items. For example, if you want to make a hatchet made of stone you do not need a workbench (“tier 0”). If you want to make a metal hatchet you need a “tier 1” workbench. If you want to make a chainsaw you need a tier 2 workbench. The final workbench is tier 3 for crafting things like assault rifles and rockets. Lastly, some items exist that are so good and rare that players cannot craft them. These include military grade weapons like sniper rifles. I’ll call these items “tier 4”. I wanted to know, if a skin is for a high tier item, will it cost more than compared to low tier item skins? Yes. Well sort of. One thing to note that at the time of data collection, there was only one tier 4 item that had skins (lr-300). More have been added in since, but it does mean our tier 4 category is limited to one item. However it does have a modest 21 skins. Tier has a main effect on average Market price (p = 0.004). However, this is driven by tier 3 skins going for more than tier 0, 1, and 2 skins (p’s < 0.05). No other pairwise differences were significant. Buy tier 3 skins over other tiers to maximize profit.
I next wanted to know if certain skins in a class of items were more profitable than others. To check this I broke items into groups and tested if these group’s average prices were different from one another. I started with armor first. RSK and RSJ is road sign kilt and jacket. MFM and MCP are metal face mask and metal chest plate, respectively. Bone helmets and ponchos are the obvious outliers here. However, they have very few skins (2, 4; respectively) compared to others like road sign jackets (34) or metal chest plates (32). This likely drives up the price due to so little supply. For stats I’m going to focus only on more popular and populated items (RS, Metal, coffee). There is no difference in average price for these gear sets (p = 0.19). I also looked at price swing (p = 0.13) and Store to Market average (p = 0.23), no dice. Armor set does not matter. Unless you were a lucky one to buy an early bone helmet or poncho. On average armor skins give the best returns from Store to Market with a mark up of $3.84 NZD or $2.53 USD.
Everyone’s favorite items in the game, guns. Can you buy skins for some guns and come out better off than others?Yes, but barely (p = 0.016). Swing (p = 0.009) and Store vs Market (p = 0.008) were more significant. Unlike with armor I included every gun in the stats. Aside from the Thompson (tomy) they all have a decent number of skins. Trying to do any sort of pairwise testing here is more than I care to do with this many groups. For the average gun expect a $2.23 NZD or $1.47 USD profit flipping from Store to Market. Generally, eokas, crossies, semi-auto rifles, SMG’s, and MP5’s, do better than other guns.
I’ll quickly go through some other item groups that are interesting to look at but have too many groups to get a hold on statistically. If there is an item you don’t see I likely took it out because it had too few skins or I hate it. Clothing item skins are all over place and generally not a very good investment in terms of the average prices they achieve on the Market and generally return $1.28 NZD or $0.85 USD. Your best bet for a profit is headwraps or a lucky jacket. Never get baseball hat skins.
Last but not least, for all you role players who would rather have a decadent base than badass armor kit, here’s the role playing items. “g” is garage door. Your safest bet to make some cash is with refrigerator, guitar, or rug skins. However, you can get lucky with some garage doors, chairs, or festive/holiday items. Overall returns are pretty good at $2.31 NZD or $1.43 USD from Store to Market mark up.
To finish off I want to see if we can study the best and worst performing skins to see if we can spot any patterns. To do this I took the top 50 skins by average market price and the bottom 100 “skins”. I took the bottom 100 because so many of there were items that players randomly gained in the game, like picture frames or sign posts. I filtered out these decorations as well as crafting materials (cloth, wood, metal) and loot crates to get the bottom 82 skins. Better performing skins tended to be a higher tier (avg 1.7 vs 0.84, Mann-Whitney U p < 0.001) and were comparatively newer but overall older skins (avg 752 days vs 1004 days, p < 0.001). These top 50 skins would turn you an average Store to Market profit of $35.33 NZD or $23.32 USD while the bottom skins resulting in a $2.09 NZD loss (p < 0.001).
What else can we tell about these skins qualitatively? A large proportion 61/82 (74%) of poor performing skins are for clothing items and for clothing items skinned by Facepunch and not by artists. The next largest was armor 15/82 (18%) which were mainly road sign jackets and metal face masks. The remaining six skins were various RP items. In contrast, the top performing skins tended to be high tier guns (34%; ak’s, MP5’s) or armor (24%; all being metal face masks or metal chest plates). There were a few random pieces of clothing (16%) and role player items (22%). Overall, if you want to make some money off skins, stick to higher tier guns and armor and avoid clothing.
I know of a few other factors that likely have a big impact on skins prices but I wasn’t able to capture here.
One is color of the skins, or really, how many colors and how flamboyant the colors are. Very colorful, stand out skins tend to do quite well. I have some ideas for this but it’ll require a lot more work and this post is already long enough. Shout out u/Dertarion who suggested rainbow/golden skin’s performing better than others.
I did look into all skins (I found 15) that had “gold” in their names. They had a high average Market price ($7.23 NZD; p = 0.002) however, they didn’t have the greatest Store-Market return with quite a wide spread averaging at $2.17 NZD (p = 0.30 compared to non-gold skins). Their swings were quite massive though at $17.71 NZD (p = 0.018) if you can time the market right.
There are a handful of YouTubers who make Rust videos and have big enough audiences to influence skin prices. In fact, some Rust YouTuber have their very own skins in the game that advertise their channel. If a popular YouTuber starts using a skin it may make the price spike. Without a ton of manual work I’m not sure how I could capture this.
Some Rust skin creators make amazing works of art and have garnered large followings. These artists get their skins picked for Rust often due to their popularity and high quality. However, I need to come up with a method to associate skin artists with their skins in the data set. If I can figure that out I may do another post looking at the popularity of artists and their skins’ prices.
All the analysis code is here on Github. However, I’m warning you it’s a complete mess and has NO comments/documentation to it. Venture at your own risk.
This work by Blake Porter is licensed under a Creative Commons Attribution-Non Commercial-ShareAlike 4.0 International License
There are several ways to obtain blueprints. By default, low tier blueprints can be found in barrels, however most other blueprints must be learnt by first having the actual item. Once you have an item you want to be able to craft, you must use a research bench and place the item, along with the corresponding amount of scrap to learn it. This will remove the item you have placed in the research bench to learn, but you will be able to use components to now craft it at a suitable level workbench.
An alternative to obtain a blueprint is to use the workbench experimentation feature. For each tier workbench (1, 2 or 3), an amount of scrap can be traded for a random same-tier blueprint. For example, placing 75 scrap in a workbench tier 1 will give you a random blueprint from the tier 1 items, such as a metal hatchet, or revolver. Tier 2 will cost 300 scrap for a tier 2 random blueprint etc.
If you played Rust back during the old blueprint system, you’d use BP Fragments along with an item to gain a certain percent chance of generating a blueprint. This new scrap system, although similar, is not a game of chance like the researching of old - a specific amount of scrap will definitely yield you a blueprint as well as indicate the required level of workbench to craft it.
I love finding old, decaying items to use in my artwork, and have a particular Table salt; Spray bottle – I used an old plant mister bottle.
A few key objectives in the early game:
There’s a lot more to it than this and I’ll go into detail below.
You can build just about anywhere on the map (besides around monuments and caves) but some areas are generally quieter than others. Most large monuments draw a crowd and you’ll get picked off while trying to make your start.
A good strategy is to check the map immediately after spawning (press ) and run away from the beach so you don’t get harassed by other nakeds. Head inland away from monuments.
Part of the fun of Rust is learning the different characteristics of each area so don’t be too fussed where you end up. However, initially the snow is challenging because you will die of cold overnight unless you have clothes and a fire.
Some things to note:
There’s a lot more to finding a good place to build but that’s a start.
Once you’ve found an area you’ll want to get a sleeping bag down. Find some tall grass or bushes and plonk it down. This allows you to respawn at your bag rather than on a random beach after you inevitably die.
Next up craft a small stash and pop it down somewhere distinct that you will be able to remember. It’s worth taking a screenshot of where you’ve dumped it for future reference. You’ll need to be able to find it to add and remove loot.
The reason this is important is that you’re going to bury your stash underground where no one can see it (including yourself). To access your stash again you need to look down at the ground where it’s buried for a couple of seconds.
Because of the difficulties of locating a stash some people prefer a wooden storage box. While this is easier for you to find, it’s also much easier for other players to find. Note that it’s possible to find a box-sized rock to hide your box in, which works a treat if you can pull it off.
Periodically dump your main resources in your stash (wood, stone, metal ore, sulfur ore and high quality metal ore) and continue foraging. You’ll want to accumulate a decent amount of loot along the lines of:
The more the better, but this is enough for your starter base.
Don’t get too carried away building your first base. Your main priority here is safely securing your loot so you can build up your gear and start causing mayhem for other players.
The cheapest and most common starter base is a single square foundation with an adjoining triangle foundation (commonly referred to as a 1x1). The square foundation has one door leading to the triangle foundation, which in turn has one door leading to the outside world. The triangle creates an airlock allowing you to “safely” leave your base without other players jumping in and grabbing your main loot.
To get your head around base building YouTube is your friend. There are a ton of great base builders out there but for your basic “shit shack” I found Tiny Pirate’s starter base showdown to be the most useful. Check it out.
For some additional 1x1 layout ideas I also recommend checking out Tiny Pirate’s vid comparing 1x1 layouts.
You should already have a bag outside your base, but it’s essential that you also have one on the inside. You’ll want to be able to safely respawn in your base if you die.
To start building you will need building plan and a hammer. The building plan allows you to place twig foundations, walls, floors and more. The hammer allows you to upgrade your twig to wood, stone, metal and armoured.
When placing twig walls, notice that on one side there are criss-crossed logs forming an X. This is the wall’s soft side and you don’t want it external facing. When placing twig structures, press R to rotate them. If you place something incorrectly you can rotate it using the hammer, but only for a limited time after placing it.
Twig is very easy to destroy so at the very least you should upgrade your twig to wood. But wood is vulnerable too. It can be burnt using a flame thrower and hatchets will eventually break through it. Stone is what you should be aiming for. Just make sure you place your walls strong-side out or they can be pick-axed.
Placing a tool cupboard (TC) in your base means only you - or anyone else who has access to your TC - can build within a certain radius of where your TC is placed. Other players can build some types of twig near your base but they can’t upgrade it. This prevents players from griefing your base by building walls around your door. Place your TC in one of the back corners of your 1x1 and authorise yourself. Having TC auth allows you to destroy structures, but only for 10 minutes after they are placed.
Until Devblog 193, key locks were a terrible option for securing your base. But that has all changed. You no longer need to carry your key on your person, and there’s no longer a 1⁄100 chance that someone else’s key will open your base.
The catch is that if you want to share your base with other players you’ll need to give them a lock and at that point they become a lot less secure. This is because if anyone is killed while carrying a key to your base, anyone else can use it to get into base. It also means that if you’re not around to craft a key other players can get stuck inside or outside your base.
For these reasons, if you plan on playing with a group you should aim to get a codelock as soon as possible. To do this you’ll need at least 100 metal frags.
Crafting a furnace is one of the most important early game milestones. Once you have a furnace you can begin to smelt metal and sulphur. This is particularly vital if you don’t yet have a code lock on your base.
The biggest obstacle to crafting a furnace is the 50 low grade fuel. This can be difficult to find early on. Here’s how you can find it:
TIP: Craft a bone knife as soon as you can for max efficiency when harvesting animals.
Formica, a type of plastic laminate, provides a durable and stain-resistant veneer for counters and tabletops. Rust stains from table hardware or pots and utensils can leave light orange streaks or speckles on the Formica surface, but these usually are only on the surface and aren't a permanent stain. Improper rust removal causes more damage than the initial stain. Most abrasive cleaners scratch Formica, and highly acidic cleaners can etch the plastic laminate. Careful cleaning with the right solution removes stains without damage.
Combine 2 teaspoons of mild detergent with 1 quart of warm water. Scrub the rust stain with a rough cloth or a soft-bristled brush. Avoid abrasive pads and stiff brushes because they can scratch the Formica surface.
Rinse the counter with clear water. Wipe dry immediately because standing water can cause the laminate to separate or warp.
Combine 2 parts borax with 1 part lemon juice to form a paste. Smear the paste over the stain, but do not rub it in. Allow it to sit for three minutes. The citric acid combined with borax can lighten rust stains, but it isn't acidic enough to damage the Formica.
Wipe the paste off the Formica with a wet rag. Rinse the area and inspect it for remaining rust stains.
Pour rubbing alcohol on the remaining stain so it is completely covered. Wait one minute, then add enough bleach to cover the stain. Allow it to stand for an additional minute and then rinse with clear water and clean rag.
Jenny Harrington has been a freelance writer since 2006. Her published articles have appeared in various print and online publications. Previously, she owned her own business, selling handmade items online, wholesale and at crafts fairs. Harrington's specialties include small business information, crafting, decorating and gardening.
Genre: Survival Crafting Multiplayer Open World PvP The only aim in Rust is to survive. make sure Steam is running (Download: Steam) IIa. V.) paste “ cnmcblog.comt cnmcblog.com ” into the console for.
This is a follow-up post to Lock-freedom without garbage collection from 2015, which introduced Crossbeam, a Rust library that implements efficient lock-free data structures without relying on a tracing garbage collector.
Crossbeam has gone through a long list of improvements since then, and it’s time to showcase where it’s at today. We’re aiming to provide a rich set of tools for concurrency akin to and outdo Go channels in features and performance.
To see what is currently offered by the library, jump to the documentation for an overview.
The following tour through the history of Crossbeam contains something like 150 links. I encourage interested readers to click on them - you may find hidden treasures of useful resources buried in here! 💎
Before we get started, it’s helpful to clarify a little bit what exactly Crossbeam is and how it relates to other libraries for concurrency and parallelism.
A common question I get is how Crossbeam differs from Rayon and Tokio. My answer is:
Rayon splits your data into distinct pieces, gives each piece to a thread to do some kind of computation on it, and finally aggregates results. Its goal is to distribute CPU-intensive tasks onto a thread pool.
Tokio runs tasks which sometimes need to be paused in order to wait for asynchronous events. Handling tons of such tasks is no problem. Its goal is to distribute IO-intensive tasks onto a thread pool.
Crossbeam is all about low-level concurrency: atomics, concurrent data structures, synchronization primitives. Same idea as the module, but bigger. Its goal is to provide tools on top of which libraries like Rayon and Tokio can be built.
The same year Rust 1.0 was released, aturon published the blog post titled Lock-freedom without garbage collection, which demonstrates that one doesn’t need a language with a tracing garbage collector to write fast lock-free programs. The secret sauce is a technique called epoch-based garbage collection, which is much different from traditional garbage collectors and is easily implemented as a library.
In those early days, Crossbeam had:
Then, in 2017, aturon declared he didn’t have time to continue working on the project and asked the Rust community to take it over. Many people showed interest in contributing and that is how I got involved, too.
At that time, we discovered some pieces of Crossbeam like , epoch-based GC, and scoped threads had soundness holes. All of them were easy to fix but difficult to spot. Low-level concurrency is notoriously tricky and scary, so we first made sure those bugs are ironed out before growing the library.
Organizational changes were put in place. We split the library into multiple subcrates:
The main crate re-exported those subcrates. We didn’t want to split crates any further so MPMC queues and the lock-free stack were kept in the main crate for the time being.
Next, we created the RFCs repository and begun discussing the overall direction of the project and new features we should implement. A wiki page with learning resources was set up.
The first RFC we accepted was a roadmap for the next 3-6 months. In hindsight, that was overly optimistic and should’ve been a plan for the year…
The epoch-based GC went through a complete rewrite. The atomics API was first revamped - soundness holes got fixed, pointer tagging was added, more efficient atomic operations were introduced.
Next, we redesigned the core epoch mechanism. Pinning got more than two times faster, garbage collection became incremental in order to reduce pauses, manual garbage flushing was added, and we made it possible to run arbitrary destructors before freeing memory.
Correctness of memory orderings in the garbage collector was proven in an incredible RFC written by jeehoonkang. The proof got us more confidence in the implementation and was a big step forward in the maturity of the project.
He followed up with another RFC that made it possible to create independent GC instances and use in environments. Memory allocator elfmalloc was then able to use it as a dependency.
Another milestone was when we realized guards can be safely used for pinning, so the ugly pin scopes got removed and pinning became more ergonomic.
In the spring of 2017, I got interested in channels because they’ve become the bread-and-butter synchronization tool in Go, while our channel implementations were lacking in many regards. My observations were:
has a number of flaws. is not and cannot even be cloned. Bounded channels are just deques inside mutexes and therefore slow. The macro isn’t moving towards stabilization due to insurmountable obstacles in its design. And there are known long-standing bugs.
Like our bounded , Go channels are protected by big mutexes. A promising design proposal for lock-free channels was published in 2014 and there’s even a pull request for it, but it has been open for years with little progress.
The only way to use channels with select on stable Rust was using BurntSushi’s chan crate. It was great but never designed for high performance and was even slower than Go channels, which is unfortunate.
My goal was to build channels that have cloneable and shareable senders and receivers, are faster than both Go channels and , have , support dynamic selection, and fix ergonomic warts in . I had serious doubts that such a thing is even possible, but it was worth giving a shot to see how far we can go.
After seven months of research and experimentation, I published version 0.1 of , which delivered on most of the envisioned goals. A blog post, an RFC, and benchmarks accompanied the release. But it wasn’t a wild success as much as I hoped. For example, some of the responses were:
I still can’t get over the select design. I know it’s performant, and every other part of the library is great, but the conditions seems way too easy to mess up
I also don’t like the global state with the threadlocal variable, why can’t it be a struct with methods?
Not only it is brittle, but I must admit that even after reading the description of the mechanism several times, I am not quite clear on exactly how it works.
Back to the drawing board. After seven more months crafting a complicated macro, version 0.2 was released, which also simplified the API surface, basing decisions on the received feedback and conclusions of a lengthy discussion in the issue tracker.
The release was a significant improvement, but still not perfect. It was not possible to do dynamic selection and users complained about dropped receivers not closing channels, which sparked another interesting discussion, where we dug deeper into the design space of channels and reverted some decisions.
It took five months to figure out what to do next and rewrite the selection mechanism from scratch. Finally, all pieces somehow fell into the right places and, with version 0.3, my dream came true - Crossbeam channels now offer everything one could ask for, and I haven’t received any major complaints or feature requests so far. They are fast, MPMC, and have a powerful select. This year might be a good time to publish version 1.0 since I don’t expect API changes anymore.
Servo switched from to , which removed a bunch of unsafe code and the dependence on the unstable feature. That was an important milestone because it proved is mature and reliable enough for such a big project.
I’m also intending to write an RFC with a proposal to give a desperately needed refresh: improve performance, enrich the interface, and fix bugs. There’s still a known bug in it, which is even documented in the standard library! This is not a good look and we should do something about it as soon as possible. One option is to take a subset of to replace the whole guts of , just like we’re replacing mutexes and hash tables with and . But more on that another time…
Fortunately, has had very few bugs during its life, and none have ever been reported by users, thanks to the extensive suite of 400 tests, some of which are borrowed from and Go’s channels.
This is just a summary of how these channels came to be. Given how much interesting research went into producing this crate, it deserves a blog post of its own so I hope to write more sometime!
Scoped threads are such a simple convenience, yet may be the feature Crossbeam is best known for. In the past year, we fixed soundness issues and bugs, removed somecruft, polished up threadjoining, and added support for nested spawns. It’s really just a bunch of small incremental changes.
Some notable breaking changes were:
In , closure now accepts a single argument of type , which can be used for spawning nested scoped threads. Rayon’s scopes use a similar pattern.
Function now returns a that contains an error if any automatically joined child thread has panicked. Before the change, such panics would get silently ignored.
The set of atomics provided by the module is not particularly easy to use. In many ways, it feels very low-level:
There’s only a restricted set of primitive atomic types like and . But what if we want arbitrary types to be atomic?
can load and store raw pointers only, forcing us to use . It would be nice to have atomic and instead.
Every atomic operation needs a memory ordering. Reckless use of resulting in UB is worryingly common. Even experienced programmers sometimes fall into this trap.
Enter , which works just like , except it can also be shared among threads. Arbitrary types may be used with , although some operations only work with types. Sane defaults are used for memory orderings so one doesn’t have to worry about them.
Of course, there must be some magic enabling to work with arbitrary types:
When type can be transmuted into , we internally pretend that is actually and perform atomic operations that way.
When cannot be transmuted into , a hidden global array of spinlocks is used. To perform an atomic operation, we take the pointer address of the and pick one of the spinlocks based on it. Then we lock it, pretend the is just a , and perform the desired operation.
Most implementations of in C++ use the same trick, so this is nothing new. However, there is one thing that sets apart: optimistic reads.
Our spinlocks are implemented as sequential locks, which means every lock has a stamp, an atomic integer that gets incremented on every write operation. Read operations load the stamp, optimistically read data, and then check whether the stamp has changed. If not, we’re done, and if it has, we need to retry.
This way read operations don’t contend with each other, meaning they are very fast. Neat!
As of recently, Crossbeam also features synchronization primitives. They are tools in the same category as mutexes and conditional variables, except a little bit more exotic.
Currently, we have the following primitives:
, the same mechanism behind function , but extracted for implementing custom thread notification. Tokio uses it for parking and unparking threads in its thread pool. Rayon might adopt it in the future, too.
is like , except it has an array of small s called shards. Each thread performing a read operation locks a distinct shard, thus reducing contention and making reads faster. However, writes are slower because they need to lock all shards.
, which allows threads to synchronize the beginning or end of some computation. It is inspired by Go’s from its standard library.
Almost every work-stealing task scheduler has the same setup:
There is a shared global queue of tasks, usually called injector and is the entry point for new tasks. For example, if you call outside Rayon’s thread pool, the task will be pushed into the global queue. Any worker thread is then allowed to take tasks from it.
Each thread in the thread pool has its own worker queue. Only the thread that owns it is allowed to push and pop tasks, but other threads may steal tasks, which is a particular operation optimized for task scheduling.
The job of each worker thread is to wait for tasks to appear and run them. To find the next task to run, a thread will first look into its worker queue. If empty, it looks into the global queue or attempts to steal tasks from other threads.
This setup is used in Rayon, Tokio, Go, Thread Building Blocks, you name it. The advantage of work stealing is automatic work balancing among all threads even in presence of skewed workloads.
Crossbeam’s deque originally started with a basic Chase-Lev for work stealing, but it got beefed up since then:
We added support for FIFO worker queues in addition to the classic LIFO queue. LIFO order makes sense for Rayon because it prioritizes tasks for cache utilization, while FIFO makes more sense for Tokio because it prioritizes tasks for fairness.
Batched steal operations were added, which significantly reduce the total cost of queue operations. They got us nice speedups in Tokio.
A special queue was introduced, which integrates nicely with queues and supports similar operations.
Every such improvement in has a ripple effect on the library ecosystem. By bumping dependency versions and leveraging new features, Tokio’s thread pool gets faster, and therefore every application using Tokio gets faster, too!
Until very recently, there were just two unbounded MPMC queues in Crossbeam:
, the classic lock-free Michael-Scott queue. It allocates on every push operation, putting high pressure on the global allocator. An interesting extra feature it has is blocking pop operation.
, which is like , except it allocates segments of nodes. Even though it’s not strictly speaking lock-free, fewer allocations and better cache locality make it quite a bit faster than in practically every case.
Since offers almost nothing over , we’ve decided to remove it. And if one needs blocking pop operations, channels can be used as an alternative.
Then we added , which is based on dvyukov’s bounded MPMC queue. The original implementation in C++ has two rough edges: it is not linearizable and forces the capacity to always be a power of two. We’ve smoothened both.
A month ago, I had an epiphany and realized the bounded MPMC queue can be generalized to an unbounded queue. By combining segments from and turning the sequence field into something functionally akin to hazard pointers, we can replace epoch-based GC with a different garbage collection scheme, bringing two benefits:
Garbage collection is entirely eager rather than lazy. Under high concurrency, it can be measured that this new queue uses less memory than the old .
Epoch-based GC incurs a certain overhead on every operation due to thread pinning and occasional garbage collection. By removing it we get performance wins.
was then rewritten from scratch and got us much better benchmark numbers.
Both and the new implementations sprung from the effort of optimizing channels, which were the first to use those queues internally, and then we just ripped them out and moved into .
There’s an experimental crate featuring maps and sets based on lock-free skiplists. They are very similar to and in functionality and interface, but can be mutated concurrently from multiple threads.
These maps and sets scale very well, much better than mutex-protected B-tree based equivalents. However, in single-threaded workloads, skiplists offer rather underwhelming performance, generally three to four times slower in comparison to B-trees.
The main inspiration here has been Java’s . I’ve always been jealous of Java having concurrent skiplists that are straightforward to use, so this is an effort to bring them to Rust as well. The fact that Rust doesn’t use the JVM to rely on a mature and well-engineered concurrent GC made implementation much more difficult for us. Surely one of my most complex pieces of code!
The crate has been in a coming soon state for a long time now. We’ve just been slacking off on writing additional tests and documentation to push it over the finish line and publish the first version. But really, it’s been completed - you can clone the repository and play with it.
There are two utilities that are difficult to put into any category. While very simple, they are indispensable in low-level concurrency.
performs exponential backoff in spin loops by executing the PAUSE or YIELD instruction and yielding the current thread to the OS scheduler. Here’s how one might use it to wait for an to become :
wraps a value of type , padding it and aligning to the length of a cache line. Useful when we want to make sure an atomic variable doesn’t get falsely shared. For example, in a MPMC queue, it’s a good idea to put the head and tail indices into their own cache lines:
Crossbeam has come a long way already, but there is still work to do. In summary, there are two sorely missing features we need as soon as possible, and a list of less critical nice-to-haves.
In an interesting blog post comparing concurrency in Java and Rust, hniksic points out we don’t really have a satisfactory equivalent of . The closest one we have today is probably , but there is still room for improvement - for example, in some cases, it is an order of magnitude slower than .
Another common request is a concurrent hash table. While we do have wonderful crates like , they tend to be designed for niche uses and often have non-standard interfaces. I’d love to also see a generic concurrent hash table that works well enough across a broad set of applications, with few surprises, and is ideally as similar to as possible.
So these are the things I’d like to see in Crossbeam this year: and written in Rust. There is a lot of design and implementation work to do here, but we generally know how to move forward. If you’re interested, take a peek at discussions on atomic references and hash tables in the issue tracker.
Finally, there is a neverending list of lower-priority features we might want to explore, but are not essential to the project:
Looking back at the past two years, I’m thrilled with what we’ve accomplished so far! These days Crossbeam is even gettingreferenced in researchpapers.
My work on Crossbeam was sponsored by Mozilla and patrons on Patreon, to which I owe a big thank you! Without your support, it just wouldn’t be able to happen. Sustainability of open source work is a complicated topic and, instead of going into a ramble, I’ll just say everything withoutboats wrote about it at the end of their blog post is spot on and rings true for me personally.
I’d also like to thank all contributors who write code, participate in discussions, and share their feedback. This experience made me realize firsthand just how much every single contribution means, even if it’s as simple as, say, a comment in the issue tracker.
On a final note, I feel humbled by technical and leadership knowledge of so many people involved with Rust. And Crossbeam has contributors whose grasp of wide ranges of topics is sometimes way over my head. I’ve learned a lot from you and still am!
Genre: Survival Crafting Multiplayer Open World PvP The only aim in Rust is to survive. make sure Steam is running (Download: Steam) IIa. V.) paste “ cnmcblog.comt cnmcblog.com ” into the console for.
MooguhnNovember 22, 2018 3:17 AM
I apologise, but it not absolutely that is necessary for me.
KikinosNovember 18, 2018 6:34 PM
And you so tried to do?