Archive for the ‘Coding’ Category
Screencast: The Conversation Editor
A bunch of people showed interest in the conversation editor I showed off in the screenshots of our admin section.
I decided to record a screencast that shows off how the tool works. It’s un-rehearsed and rough around the edges but it’ll give you an idea of how we write the conversations that you play out in sTalk.
The Forumwarz Admin Tab
While it’s not as obvious when you’re going through quests and having fun playing, underneath the scenes, Forumwarz is just a database.
Every forum, attack, conversation and cinematic is represented in the database somewhere so we can keep track of it and modify it as we need. In order to maintain all this data, we have quite a large admin section. Only a handful of people have access to it, so it’s not the prettiest thing in the world, but it’s functional and allows us to build new content and episodes.
This blog post is a small tour of the admin section. There’s way too much to show everything in detail, but here are a bunch of screenshots with descriptions so you can get a bird’s eye view of how we do things. Click any of the screenshots to zoom in.
(Note: These screenshots contain minor game spoilers. I’ve tried to keep later content out of them but you should view at your own risk.)
1. The Admin Root
As an Admin, you get a tab you can click on. When you do, you come to a menu that scrolls vertically. It’s basically just a set of links to the various sections of the site that are editable:
2. Abilities List
Most of the links take you to overview pages for their sections. Here’s what the ability page looks like. Note we have filtering tools at the top to search for abilities to edit them easier. The ability post links allows us to edit the madlibs that appear when you perform an attack.
3. Event View
On the previous screenshot, did you see the little gear icons? Those mean that the particular item in the database has an event associated with it. Events are links to Ruby scripts that execute code when things happen.
This is the event for the Drool on Keyboard and Bash Head against Keyboard attacks (note they are exactly the same from an event standpoint.) The code says that this attack damages the enemy’s pwnage by a value between 3 and 6. It also tells the toolbar to inactivate the action after executing it.
In my earlier attempts at building Forumwarz, these attributes were represented by entries in a database table. But I quickly found it was less flexible. The beauty of Ruby code for events is that I can make an attack do almost anything I want. I could make an event contact Yahoo! weather, get the temperature and make an attack hurt for that much, for example. Not that I’d advocate doing that for performance reasons 🙂
4. The Conversation Editor
One of the most dynamic things you can do in Forumwarz is have a conversation with an NPC. We started by thinking of conversations as threaded, but then we realized that we often want to jump back to previous states (for example: “Let’s talk about something else…”). The current conversation system is a finite state machine with a visual editor:
The interface is designed so that our writer (Jalapeno Bootyhole) can jump in and create conversations. He can create new states, and link them together with choices and arrows.
After a few edits, I step in and embed small amounts of ruby code in the conversations. This allows us to change the flow depending on things you’ve done in the game. For example, in the above screenshot, right off the bat it checks if you’ve talked to Bruce Bear already. If you have, it sends you down a different path than if you haven’t.
When the conversation is done, we compile it into Ruby code. This is what the output looks like. This ruby code is then interpreted as you step through the conversation. You’ll notice that images and special characters are encoded as html in the strings.
Any programmers reading this might have noticed that the compiled conversation is actually an Event. Our event framework is the same everywhere, whether it’s using an item or having a conversation (they have access to the same game-changing features.)
5. Cinematics List
Cinematics are interesting. Any time the game interrupts you and takes over fullscreen you are probably watching a cinematic. Cinematics can vary from a simple NPC wanting your attention to extended gameplay sequences like the beginning of Episode 2 (the cinematic there is larger than all the cinematics in Episode 1 combined.)
6. Editing a Cinematic
Cinematics are divided up into shots. Each shot can be different, showing an image or text crawl or even an NPC saying stuff.
7. Shot Properties
Furthermore, shots can be given extended properties. We can tell shots how to transition in and out (shake, fade, scroll, etc.). Additionally they can call external Javascript if we want something else interesting to happen.
8. Forums
All our Forums are stored in the database. On this list you can see a “Special” forum which is where we store generic content that can be inserted into any other forum a certain percentage of the time.
I have no idea why the generic forum has an XSS and FBO quota though, looking at it! The little icon of three little colored dots means that database entry has a style associated with it. Clicking it allows us to customize the way that forum looks using CSS.
9. Reports
We have an extensive Reports section that gives us back summaries of the data in the database. A lot of them are business oriented such as how well the various brownie point packages are selling. But there’s also gameplay stats in there, such as which classes are the most popular, what abilities people prefer using, etc. It’s a goldmine of corporate secrets!
(For the paranoid: Big Brother is the name I gave to our conversion tracking. It tracks our various ad campaigns and how many users each one brings in so we can evaluate how effective they are.)
10. Nicknames
Ever wondered how the name generator works? We have two columns of data. It randomly joins together a value from both columns, then does post-processing on it to add typos, converts random characters to numbers, etc.
11. Word Types
Anyone who’s used ForumBuildr is aware of how we generate all the text in the forums. It uses certain word types, identified with a @ before their name. They are replaced, madlib-style, into arranged paragraphs. Here’s a page that lists them:
12. Editing a Word Type
Finally, the last thing I took a screenshot of, is how we add words. It’s pretty simple, we just give a huge blob of text into a field, separated by commas.
That’s it!
I hope you enjoyed this little tour of the admin section. Forumwarz is definitely the biggest site I’ve built from scratch, and it’s fun to show the sections that only 3-4 people ever access. If you have any questions, be sure to ask!
Do you learn more working for yourself?
I just got this tubmail:
Hey Trout,
On building forumwarz the past 1-2 years, would you say that you’ve learned more about web technologies and web business than you would have if you were to work in a typical 9-5 IT company?
thanks
– udo
I liked the question a lot so I decided to post my answer here on the blog.
Yes. Absolutely yes.
First a bit of history for those unfamiliar, which I think qualifies me to answer this question. Feel free to skip to the next section if you just want to know my answer and don’t care about how we got to where we are.
From 2 years ago until now:
I have been working on Forumwarz for just over two years. I don’t have an exact date for when work begun, but I do remember telling Jalapeno Bootyhole and BINGEBOT 2015 that I had decided to quit my job at a Halloween party in October 2006. At that point I had already been working on it in the evenings after my day job.
I gave my job a lot of notice, and started working full time on Forumwarz in January of 2007. We decided in early October 2007 that we had a pretty good first Episode done, so we launched that to the Something Awful goons on October 31st. About 1,000 people took part in that beta, so if you got in then, consider yourself an old timer!
When we launched Episode 1 there was really no end-game to play. Once you’d finished the storyline there was nothing at all to do! So we took a few ideas we had and built them out. First we built support for Klans. Then we created the Brownie Point upgrades system. Then we created ForumBuildr v2.0 BETA. Then we created Forumwarz Domination. Once we had that set of features, we decided to take off the beta label, and launch properly. This was in February 2008.
We were picked up by some major news sites online (the difficulty of sparking that could honestly be a whole blog post in itself), and we jumped from about 2,000 to 45,000 users in a month! To say we had some growing pains is a huge understatement. We focused on improving our architecture. There were many software upgrades and even a few hardware upgrades.
After that we built some more community features: Internet Delay Chat, INCIT, more brownie point options. We began season 2 of Domination and introduced a bunch of themes. At some point, I think around Spring, we started working on Episode 2 hard core. We’d had the storyline written for a long time already, but there was a lot that needed to be fleshed out in terms of the specifics of how it would play out.
We shifted gears and that’s what we’ve been doing since then, with the odd contest and small upgrade in between. We launched a closed Beta of Episode 2 on Monday. We will be launching Episode 2 in early to mid-October. It’s really interesting to me that I decided to quit my job in October, we launched Episode 1 in October and we’ll be launching Episode 2 in October!
Okay, now I’ll actually answer your question:
In my experience, most of the web developer positions in the Toronto area are either Java, PHP or .NET. There are definitely jobs for those who do Ruby or Python or Perl, but they are much harder to come by.
If you’re lucky enough to have a good employer, they might allow you to experiment a little with a technology that branches off your own. Generally though, in my experience most companies want to stick with the technologies they’ve already figured out (and more importantly, invested in) how to support. If your company has a huge Java application that runs on Weblogic and Oracle, then you come up and say “Hey, for the reporting component, why don’t we try Ruby on Rails or Django”, you’re probably about to face an uphill battle.
When you work on your own, there is no such resistance. You are free to do whatever you want. You can try out new things like I did and gain a lot of experience in the process. That’s not to say there isn’t a lot to learn in the Java or .NET world, but by virtue of doing it all yourself you’re going to have to learn it.
Even if you don’t take a chance on a new programming language or framework like I did, you will still end up learning things.
A good example is the IT side of a project. I was just remarking the other day that I hated setting up servers in the past. I would do it out of necessity but I would complain incessantly while doing it. When deploying Forumwarz, I had no choice but to configure them myself. I had help from friends, but I did the majority of it myself. And then, at some point recently while configuring an EC2 image for Amazon, I realized that I was actually enjoying it.
I now know how to set up a cluster of ruby instances, how to set up Apache and Nginx, mail servers, software firewalls, databases, dynamic DNS and I love it! I was forced to learn those aspects of deploying out of necessity, but I am much stronger for it. I never would have done that if I was working for another organization. I would have just thrown my code over to the IT guys.
Finally, there is the long amount of hours you inevitably end up working when you run a start up. Even if you promise yourself you’re only going to work 9 to 5 (And nobody I know with a startup does that), things will pop up when you least want them to. Your site will go down in the middle of the night. You will get calls while sick and in bed and have to crawl out to fix them. These are amongst the worst aspects of working for yourself on a startup, but also the ones that teach you to work under pressure. They also teach you how to work when you desperately don’t want to.
In the end, even if Forumwarz fails and I have to go back to the corporate world, I’m sure the skills I’ve learned have will only push me forward career-wise. And that’s saying nothing of the thousands of awesome people I have met, and the industry contacts I have made in the process.
I have made huge sacrifices in my personal life for this project, but you’ll rarely hear me complaining about it: I love what I do, I’ve learned a hell of a lot, and I’m sure no matter what happens with the project from this point on, my career will be better for it.
Flood Detection, Rails and Memcached.
Note: The article is about the code behind Forumwarz for those who are interested in such things. If people like it I might write more like it!
Flooding is what happens when a user submits data repeatedly to your server. A good example would be a user who repeatedly posts comments on your message board.
Sometimes it’s done innocently: a user hits the submit button 5 times impatiently during a bit of server lag, only to find their post went through 5 times.
Other times it’s nefarious and deliberate: a user creates a wget script to just post garbage over and over.
Not only can flooding be taxing on your server, but it can fill up your site with so much garbage that it will put other users off. Obviously it’s something you want to get rid of!
A simple and effective strategy: the cool down period
You can assign a “cool down” period, where a user is barred from submitting data until the period ends. The length of the cool down is largely up to you, based on what you consider normal submission processes. I’ve found 30 seconds seems to work in most cases.
An obvious way to do this would be to add a timestamp column to the row you insert (which is always a good idea anyway!). Then, when posting, query the table you’re about to insert into for anything from that user within that cooldown period. If any rows are found, don’t allow the insert.
For most sites, implementing something like this will work great. However, and I cannot stress this enough, make sure you have a good database index on your timestamp column. If you do not, every insert will result in a table scan and your site performance will be terrible.
A memcached implementation
I got this idea from the Memcached FAQ and it works splendidly!
Memcached allows you to set an expiry for any key you set. So, instead of using the timestamp column in the database, you can simply set a key in memcached with an expiry of your cool down period. Then, when you are about to insert, check to see if the key exists in memcached. If it does, don’t insert. If it doesn’t, insert your row and then add the key there.
It is incredibly fast, and it doesn’t matter if your tables are indexed on the date column or not. In fact, since it’s not tied to your database at all, you can do flood prevention on anything you want (sending emails, real time chat, etc)!
Adding it to ActiveRecord
I have created a custom validation method in ActiveRecord for flood protection. It can be attached to any model using the following simple syntax:
prevent_flood 30.seconds, :user_id
The first parameter is the length of the cooldown period. The second parameter is the column in the model that uniquely identifies the user. In this case, it’s a user_id column.
The implementation looks like this:
def prevent_flood(cooldown, field) validates_each(field, :on => :create) do |record, attr_name, value| cache_key = "flood:#{record.class.name}_#{record[field]}" unless CACHE_ME.get(cache_key).nil? record.errors.add_to_base("You're posting too often. Slow down!") end end after_create do |record| CACHE_ME.put("flood:#{record.class.name}_#{record[field]}", "F", cooldown.to_i) end end
CACHE_ME is an abstraction I wrote to use Memcached from ruby. It it initialized to connect to memcached when rails starts up, and can be replaced with however you personally connect to Memcached fairly easily. The get method returns nil if the key isn’t there, and the put method sets a key to be the value “F” with an expiry of cooldown seconds. It doesn’t really matter what value you put in the cache, I just chose F for flood, and because it’s one character long.
I know personally flood protection is something that I never really implement until it becomes a problem, and one of the reasons was that it was a pain in the butt to code for every model. However, with this interface, I am now using it on all new code I’m writing from the beginning. The overhead is minimal, and it can really save your butt down the line!