Here we have Zynga recruiting for a CTO in SF:
(see also the last post, on UK online games jobs)
Here we have Zynga recruiting for a CTO in SF:
(see also the last post, on UK online games jobs)
UPDATE: I’ll add other roles in as/when I get the OK from the relevant people; not all of these are public yet.
—
Despite the heavy rash of studio closures (well documented by Nicholas Lovell), it seems there’s a lot of exciting online/social games hiring going on right now – I’m getting lots of requests from friends, ex-colleagues etc.
There’s also some interesting roles in publishing, and some in startup online games companies.
If you’re UK-based, you’ve worked in MMO / social games / etc, and you’re looking for a job, feel free to email me. No CV’s, just tell me who you are and what you’re looking for in 4 sentences or less.
Or, if you see an entry-level job here you want to apply for, ditto and I’ll put you in touch (or just follow the links to the company directly).
Dear Eric … here’s my problem:
Somehow that didn’t feel right for Google. We wanted something much more transparent and open” (Eric Schmidt, Chairman/CEO Google, writing in the Harvard Business Review this month)
How, exactly, would you reconcile that with the fact that I had to serve Google with a legal document (here’s how to use the Data Protection Act of 1998) to get the feedback that your employees admitted they already had?
How do you explain the difference between the 10 seconds of feedback I received from your in-house recruiter, and the 12 pages (yes, *pages*) of feedback I received after I served the legal notice. Is this transparent? Is it open?
I’ve met many Googlers, and a lot of them are excellent salespeople for the company. If I truly believed that Google lived up to it’s claimed principles, I would go to great lengths to work there. Why devote my time, the precious days of finite life, to any other kind of organization? Before the interview process, I *did* believe, thanks to the testimonies of friends and ex-colleagues.
But the reality is clearly – “transparently”, to use your terminology – different. And it’s only thanks to the insightful and diligent work of UK lawmakers that I got anything out of Google at all. Amusingly, you didn’t even pay my travel expenses! Ironically, at the same office, when I went to the London Open House party, many years ago, I provided less … and received more … than when *you* asked *me* to come to an interview for a job. Surely that’s the wrong way around?
(I’ve still got my Google Open House t-shirt, and I got free food and drinks at the event. Thanks! That’s more than I got from the interviews…)
Personally, I have huge respect for people. I believe it’s the most important thing *for me* in any business. There are industries where individuals don’t really matter – I actively choose not to work in those industries. As experienced first-hand, my values wouldn’t allow me to work at a corporation like Google.
Why is it acceptable to those of you who work there now?
So, Google doesn’t live up to the hype? Shrug. My reaction: I’m building my own company that does. I’m not going to sit around waiting for someone else to make it for me.
Also … about that 12 pages of feedback… I checked with Google whether they’re OK with me publishing it (me, I really do strive for transparency ;)) – they said something very close to “it’s your data – do you what you want”. I’ve already sent it to a wide array of friends and ex-colleagues – people who know me well enough, both good and bad, to read between the lines and second-guess the context.
But it’s hugely context-dependent (i.e. you had to have been there to understand what’s going on), and there’s some extreme statements in there. It would be very easy to misinterpret, sometimes making Google look much worse than it should, sometimes making me look much worse than it should. I’m conflicted on how to publish it; transparency is great, but … you have to consider your personal responsibility too.
Please note: the design described in this blog post has been much improved and updated and put up – with full source – on http://entity-systems.wikidot.com/.
On the wiki linked above, there is now a wholly new Entity System design, based on this one, but much improved. There’s a brief post about it here, recommended instead of this ES design: http://t-machine.org/index.php/2011/08/22/entity-system-rdbms-beta-a-new-example-with-source/.
I’ve been writing about Entity Systems sporadically for the last few years. Recently, I finally had the time and the excuse to build one of my own (i.e. not owned by an employer). If you haven’t read the main series of ES posts, you should do that first. There are many things in the world masquerading under the Entity/Component banner – and even more that just coincidentally share the name, but describe something else completely. It’s worth understanding which variant I’m talking about before you read about what I’ve done :).
At a generic level, this is covered in the other posts. But it’s taken years for me to have the time/inclination to write a new one from scratch outside of my day-job. What happened?
I’ve been making games for a long time. I know how much effort will go into it, how much time, and how much slog there is before it becomes worth it. Writing a game on your own often means putting in 90% of the effort to get 10% of the reward.
Enter … the Entity System. If I were to pick a game-design that mostly used data-driven game features, I could implement it around an ES, and massively reduce the amount of planning needed to get the game running. I could maybe have a working game after a mere 20% of the effort. Hmm…
Android runs something that’s *almost* Java (although more on that later – Android’s version of Java is very slow at some of the core libraries, and it really shouldn’t be). Technically, Android supports all the core data structures from Java (Collections), and the templating system (Generics).
If I were writing an ES in C++, I’d do it using templates without pausing to think; I wondered how well the same might work with Generics, given that Generics is *not* a complete templating system, although it provides quite a lot.
How to design/implement this thing? Well, we know one thing for sure:
Entities have a single name/label/global-ID. Entities MUST NOT contain ANY DATA: these are NOT objects, this is NOT OOP!
There you go, the Entity class wrote itself:
[java]
public class Entity
{
public int id;
}
[/java]
This immediately raised some concerns for me, being the seasoned coder I am (ha!). How the heck was I going to write any code that dealt with these things if I didn’t have references to them? Obviously, sometimes you do have references, but other times you expect to follow refs from within the objects you have, to get to the objects you need. That wouldn’t be happening here, since there are no inter-object refs.
[java]
public class BaseEntitySystem implements EntitySystem
{
/** I’m too lazy to write a "safe" method to get a globally-unique ID; for now,
I just return 1 the first time I’m called, 2 the second time, etc… */
protected int getNextAvailableID();
/** Whenever you create an entity, you’d better invoke this method too!*/
public void registerEntity( Entity e );
/** The method to solve my fears above */
public Entity getEntity( int id )
/**
* Merely removes the entity from the store. It becomes a GC candidate
* almost immediately (since all other refs are transient)
*/
public void killEntity( Entity e )
}
[/java]
…but, again, being a Veteran coder, the survivor of many painful battles on the field of programming … I didn’t trust myself in the slightest to “always remember” to invoke registerEntity. Quick trick: give the Entity class a static reference to a default EntitySystem, and have each EntitySystem check if that reference is null when starting; if so, set itself as the “default”.
[java]
public class Entity
{
…
public static EntitySystem defaultEntitySystem;
…
public Entity( int i )
{
id = i;
if( defaultEntitySystem == null )
throw new IllegalArgumentException( "There is no global EntitySystem; create a new EntitySystem before creating Entity’s" );
defaultEntitySystem.registerEntity( this );
}
…
}
public class BaseEntitySystem implements EntitySystem
{
…
public BaseEntitySystem()
{
if( Entity.defaultEntitySystem == null )
{
slog( "Setting myself as default entity system (Entity.default… is currently null) self = " + this );
Entity.defaultEntitySystem = this;
}
}
…
}
[/java]
W00t! I can create Entity’s, and I can find them later on. Awesome. What about those Components, then?
I’ve done ES in C++ before, with real templates, so I wasn’t really thinking at this point … I just ran with what seemed natural based on prior experience. The thought process (had there been one) would have been something like this:
I made a Component class, and had all Components extend it; there is a particular reason for this, but it doesn’t matter right now – essentially, it lets you define shared behaviour for all Component subclasses, and just saves you time on typing.
My first real Component:
(NB: I defined this *inside* another class, because I couldn’t be bothered having N source files for the (large number of) N Components I was bound to create. Hence the “static”):
[java]
public class MyEntitySystemExperiment
{
…
static class Position extends Component
{
float x, y;
int width, height;
float rotationDegrees;
@Override public String toString()
{
return "("+super.toString()+" @ ("+x+","+y+") * rot."+rotationDegrees+")";
}
}
…
}
[/java]
Great. I have a component. Now comes the largest single piece of work in the entire implementation of the ES: writing the methods to:
This is the win/lose point: if this works well, our ES will be nice and easy to use. The other two methods (add and remove) are simply twiddling bits of data. This one is the challenge: can you make it *easy* to write code that uses the ES, and for that code to be clearly *understandable*?
[java]
public class EntitySystemSimple extends BaseEntitySystem
{
HashMap<Class, HashMap<Entity, ? extends Component>> componentStores;
public <T> T getComponent( Entity e, Class<T> exampleClass )
{
HashMap<Entity, ? extends Component> store = componentStores.get( exampleClass );
T result = (T) store.get( e );
if( result == null )
throw new IllegalArgumentException( "GET FAIL: "+e+" does not
possess Component of class\n missing: "+exampleClass );
return result;
}
…
}
[/java]
Boom! It works.
Let’s just stop briefly and I’ll explain why. Reading Java generics code from cold (just like reading C++ templates) often takes a lot of hard thinking.
Looking at the “result” of this method, we want it to be (enforced by the compiler):
[java]
/** based on comments at end of blog post, think this is correct,
but not checked */
public <T extends Component> T getComponent( Entity e, Class<T> exampleClass )
[/java]
It causes you to write application code that looks something like this:
[java]
public void doSomethingWithAnEntity( int globalId )
{
// remember, we NEVER hold refs to Entity objects for long
Entity e = entitySystem.get( globalId );
Position position = entitySystem.getComponent( e, Position.class );
position.x = 5;
}
[/java]
…and what’s important is that the “type” of the “Position position = …” line is already hard-typed to “Position”. So, the content-assist will *auto-complete* anything put after a dot on the end of that line, e.g.:
[java]
entitySystem.getComponent( e, Position.class ).AUTO_COMPLETE
[/java]
…so you can instead write your method much quicker, and yet very clearly, as:
[java]
public void doSomethingWithAnEntity( int globalId )
{
// remember, we NEVER hold refs to Entity objects for long
Entity e = entitySystem.get( globalId );
entitySystem.getComponent( e, Position.class ).x = 5;
entitySystem.getComponent( e, Damage.class ).hitpoints = 145;
entitySystem.getComponent( e, Renderable.class ).foregroundColour = Color.red;
}
[/java]
HashMap is the “correct” class to use in Java for this setup: it’s the exact equivalent of Hashtable / Dictionary / etc in other languages. We need to map (somewhere, somehow) from one thing (an entity) to another thing (a component).
NB: this does not mean that you have to use HashMap as your data-store for the ES; I positively encourage you to consider other options. I used it here as the most obvious, simplest possible structure that would do the job. If you think back to my posts on Entity Systems for MMO development, I’ve often suggested that the data store could *and should* be any of many different things. In particular, SQL databases make for an excellent data-store (and remember you can get in-memory SQL implementations that do away with all the expensive write-to-disk stuff).
Unfortunately … Android seems to only partially support HashMap. You can use the class, but it runs an order of magnitude slower than you expect for a normal JVM (compared to the speed with which it runs other methods). It seems to have problems with the hashcode methods, but also even with basic iteration over the Map contents. Odd. Later on, I had to do some tricks to speed up the ES, just because of this problem.
The examples I gave above for accessing components were lean and clear on the right hand side (thanks to autocomplete and strong typing), but terrible on the left-hand-side. By the magic of OOP, I’m going to clean up the LHS. BUT (and this is a big “but”) … make sure you fully understand what I’m doing here. With what I’m about to do, it would be very easy to fall into one of the traps of ES development: slipping back into OOP techniques.
Looking at the example:
[java]
entitySystem.getComponent( e, Position.class ).x = 5;
entitySystem.getComponent( e, Damage.class ).hitpoints = 145;
entitySystem.getComponent( e, Renderable.class ).foregroundColour = Color.red;
[/java]
… applying OOP mindset, we see that the first argument is redundant; the Entity already knows about the EntitySystem to which it’s registered.
Also, we know that the Entity class will never have any methods or data other than the ID. If that’s the case, the only thing we’d ever “get” from an Entity is a Component. So, we can add this to Entity:
[java]
public class Entity
{
…
/** Gets a filtered view of the entity’s data, only returning the subset that
* corresponds to a particular one of its components */
public <T extends Component> T getAs( Class<T> type )
{
return source.getComponent( this, type );
}
…
}
[/java]
…which converts our usage example to:
[java]
e.getAs( Position.class ).x = 5;
e.getAs( Damage.class ).hitpoints = 145;
e.getAs( Renderable.class ).foregroundColour = Color.red;
[/java]
Recap: right now, we can:
One last thing is needed before the ES can work: we need a way to fetch Entities “by component”.
e.g.:
[java]
public class MyEntitySystemExperiment
{
…
public void runLoop()
{
while( true )
{
// move all the entities
positionSystem.move( MOVEABLE_ENTITIES );
// check for collisions
collisionDetectionSystem.process( MOVEABLE_ENTITIES );
// render all the visible entities
renderingSystem.render( RENDERABLE_ENTITIES );
}
}
…
}
[/java]
We need a way to provide the arguments that are capitalized above. We know that these should be plain-old lists of entities. We know they have to come from the EntitySystem. Finally, we know that the only defining characteristic of these lists is that everything in the list has *at least* a particular Component.
(respectively, in the example above, the lists contain: “all entities that are moveable”, “all entities that are moveable AND all entities that are barriers to movement (e.g. solid walls)”, and “all entities that should be displayed on-screen”)
So, one more method for the EntitySystem interface:
[java]
public interface EntitySystem
{
…
public List<Entity> getAllEntitiesPossessing( Class… requiredComponents );
…
}
[/java]
“Class…” is just a convenience; in many cases, you’ll be insisting on a single Component. In many other cases, you’ll be insisting on a set of components. Java varargs provide the minor convenience of doing both of those in one method, while retaining type-safety.
The implementation of this method is obvious: it iterates over every entity that’s been registered, and checks it against ALL the required components. If it possesses all of them, it goes into the output list.
That’s it. So easy! Obviously, there’s more to it – the other methods you need to create should be mostly self-evident – but this should be enough to get you started.
Now, I’m not sure where to go from here. I’ve got a working Java ES. I’ve got some performance improvements and feature improvements. But … in practice, hardly anyone writes games in Java (except Android programmers, and there aren’t many of those), so … is it worth it?
Alternatively, I might just run through some of the practical pros and cons I encountered when actually using the ES in writing the game-logic. There’s some interesting things that came up which most people encounter sooner or later when doing their first ES, and which might be worth looking at in more detail.
Did it work? Did this ES allow me to write a decent Android game?
Yep. I wrote a space-invaders / bullet-hell game with it. It worked fine on Android phones for a hundred-odd enemies and bullets on screen. On Android, thanks to the crappy JVM, it started to chug after that (dropped below 30 FPS), so I had to make some substantial performance improvements, and now it’s happily rendering 300 things all flying around at 20-30 FPS. The game is far from finished, but it’s playable and fun for a minute or so – a definite achievement considering how little of it I’ve written so far.
NB: it’s got some way to go before I’ll be happy releasing it. But, given a few more spare evenings, I hope to get this up on the Android Market as a free download in the near future.
I’m pleasantly surprised that the Android phones can handle something as high-level as an ES, in a pure, unoptimized “simplest possible” implementation.
You can say “thank you” by giving me your email address, and letting me contact you next time I make a game of my own:
Two more firms have proven their mettle this week (i.e.: think twice before engaging them).
As always, it’s nothing personal. If a recruitment agent is doing a job that I and others think weak, but managed to persuade an employer to pay them to do it – and to accept that standard – then all power to them; they’re just making a living. No employer is ever “required” to use an agent, and they have many to choose from; they have no excuse for not insisting on high standards. Shame on the employers for screwing-up their own employee-base like this.
See here for more examples:
First up, we have Ravello/Enigma, with two big faux pas.
I double-checked. Yep, it’s still there, in black and white (and caps lock):
“NOTE CAREFULLY! * I only add people I actually know.”
Recruitment Agent:
“I have the following role for you:
Job type/s: 2 roles. 6 months fixed term contract or 1 year perm.
Salary: Wide open but depends on experience really.”
Me:
“What’s the actual role? I’m guessing this is iPhone related? My standard rate for iPhone development and consulting is X”
Recruitment Agent:
“The project details are top secret as my client advised me but it is an Iphone/Ipad related role. As stated before, salary depends on experience.”
WTF? What is this – MI6? The CIA?
I tried to think of an intelligent response. I failed. Maybe I’m supposed to be “intrigued” and start begging the Agent to give me some information? “Artificial scarcity” and all that?
Frankly, I have no idea. Just too weird.
Second, we have Unique Selection, asking me to do their job for them gratis (the job that they get paid to do…).
On the third attempt I relented and pointed the agent at my profile. The one they should have read to start with.
Agent:
“I wasn’t sure if you could help me but my client is looking for an perm iPhone/iPad App developer to join their team”
Me:
“I run an iPhone development team”
(basically: I couldn’t believe they were really that cheeky / dumb as to ask me to do their recruiting for them.)
Agent:
“If you were to go perm, what would roughly be the base you would look for?”
Me:
“I’m probably not interested.”
Agent:
“please feel free to pass my details to anyone you think could be interested.
Ah, yes, because those of us who actually “make things” have nothing better to do than offer ourselves up to be exploited so that recruitment agents can get a pay-cheque. Social security, eat your heart out…
I’m getting messages every day from John Say (of Say Design) pushing me to join his network on Ushi.
Ignoring them had no effect; I’m now routinely forwarding these to google’s spam box.
I’m amazed that in 2010 people would send out an “invitiation” to their business contacts without bothering to provide the recipient with ANY REASON why they’d want to say yes. Cluetrain, guys?
(Also: John, and everyone else, please stop. This shitty service doesn’t even include an email address for me to ask you directly)
Interesting search result for “O2 Facebook”:
…IIRC that started off as a service for MySpace, then MySpace/Facebook. Apparently now it’s all about Facebook (only). The only mention of MySpace is a navbar link to something about broadband – seemingly unrelated.
PS: Entity System post coming soon, for those that look for such things. I’ve just written one for Android. I just have to take the time to write the damn thing up!
One great achievement of the web is the huge reduction in barriers to publishing. But the flipside is that we now see extremely low incentives for publishers to keep content “live”. Back when it cost money to publish info, you had good reasons to *keep* your content live once it had been published; you had a revenue stream to protect.
Nowadays, with publishing costing nothing, it’s often un-monetized. All it takes is the slightest increase in hassle for the publisher, and they’re better off killing the content entirely.
That’s the case with a site I just shut down. A small, incomplete – yet moderately valuable – resource for iPhone Developers, with a few thousand unique visitors a month. Too small to be worth monetizing, so I hadn’t. I was eating the (very small) hosting and support costs, until someone abused the site, and those “support costs” became non-trivial.
I created this site at the start of 2009, because there was no good FAQ for iPhone Development (AFAIAA there still isn’t; even today, the nearest you can get is StackOverflow. SO is great, but … a lot of subjects are “forbidden” under the site terms, and the site-search is very weak).
I set it up to be low maintenance, and to allow multiple people to moderate it (very similar lines to SO, but slightly less open, and a lot more “niche”).
In the past two weeks, after more than a year of “no active moderation”, we saw forged posting credentials and then pointless offensive questions. First rule of running a passive website: leave it configured to report (surreptitiously) on all unusual activity, so you can see if it gets out of hand / abused / attacked / etc.
Deleting offensive content requires only a couple of minutes (to remember the password, login, and hit delete).
Checking what happened with the forged credential (probably unrelated) is more like half a day to a couple of days. I could audit the code, audit whatever 3rd-party PHP libraries were being referenced, and almost certainly plug the hole (or holes).
Or … I could do what I actually did: two lines of typing, and Apache kills the site. In a way, it’s a bit sad – it had background traffic of a few thousand uniques a month – and the whole thing is now gone.
At the end of the day, I get *zero benefit* from this site. I pay a tiny amount for the web-hosting and the domain-hosting, so it’s almost free, and I’m happy to leave it running for the benefit of the thousands of visitors each month.
But if it’s going to start costing me hundreds (or thousands) of dollars in lost time when I would otherwise have been doing paid contract work (every hour not working is an hour’s salary lost) … then the balance switches and (as in this case) I’m obviously going to kill the site.
I expect that the people who abused the site were just being thoughtless, and probably wouldn’t have ever gone back anyway. But I can’t afford the time to make sure.
Ultimately: Who has the time for this? A handful of callous acts just killed a repository of info.
So neatly done, I find it hard to believe it’s not real…
“3D gaming? What if the game characters saw the GAMER in 3D instead of the other way around? Is it really only myself that thinks like this?”
(along with other greats such as: “A good game can be played on a 12″ black and white TV. Fact.” … and “What do all games require? The answer is someone to play them. What if a game went against that very concept?”)
Why couldn’t I stop thunderbird from downloading 2GB of files that it is absolutely not supposed to download in the first place?
Ah, well, it turns out … there’s a bug in the basic “include folder for offline” GUI, whereby it is COMPLETELY IGNORED for certain folders. One of those folders being … the magic “All Mail” folder in Gmail.
Instead, you must use a *different* part of the TB GUI to kick the damn stupid software into doing things properly in the first place:
“Right click on the account in the Folder pane, and click on Subscribe..
You can then tick or un-tick the subscribe box for each folder.
Click on OK when you’ve finished.”This seems to work, having just tried it. Although … I also went into my profile and manually deleted the monstrous All Mail file – but before I did the above hack, TB (dis)loyally would immediately start redownloading that folder each time you started it up. Now it merely downloads the headers (at least that’s counted in MB, not GB). Now you can have your email client, with about 4 pages of manual hacks, work as … a basic IMAP email client.
…I’m really.Sorry, just very very busy right.now!
Welcoming some new people to Red.Glasses (my iPhone development / agency company), and lots of projects all happening at once.
Given how many people I see out of work, I’m delighted to.be bucking the trend, but until.our new people get.up.to.speed, its a bit too much, and I’m looking forward to things calming down a bit.
PS: typos due to the nexus one and its broken touch sensor and badly designed keyboard. Don’t have time to get to a PC to.post: )
I’m doing some pretty cool stuff at the moment – I’m not looking for a job – but a few months ago I got emailed by three different Google recruiters, inviting me to apply for three (different) specific jobs, in different parts of the company, almost all at the same time. 2 were in London, 1 was in Zurich.
(I guess that all departments were given the go-ahead to increase head-count at the same time – hence hearing from 3 people at once. They didn’t co-ordinate, either – at least one of the recruiters failed to notice that I was already being interviewed, and asked in her opening email: “would you consider working at Google?”, the day before my scheduled interview with a different department :))
I took advice from current and ex Googlers – I wasn’t looking for a job at the time – and they gave estimates of up to 3-6 months to complete the process, so I might as well go ahead and see what happened.
I had an excellent first-round interview. I have the written feedback here (which Google point-blank refused to let me have, until I served a legal notice on them, and forced them to pony-up … more on that later):
Adam is probably the most interesting, experienced, and “Googley” candidate I have ever interviewed. I am excited about the mere possibility of him joining Google: he would bring entrepreneurship, product design acumen, incredible passion for technology, experience at a big influence, and other critical skills.
Yeah, it’s way over the top, and hard to believe. I quote it here for reference against what happened next…
I had four second-round interviews, back to back, starting at mid-morning, and carrying on through the afternoon with no break for lunch. By the end, I’d not eaten in 8 hours (why no lunch? Ask the Google recruiters; they never gave me an apology or explanation). I’d caught an infection the day before, and felt like crap throughout. The interviews went from poor to terrible, and overall it was a disaster; a rejection soon followed. (NB: with hindsight, even had I been compos mentis, there’s a good chance I’d have been rejected – I’m not blaming the rejection on this)
It was immediately followed by another request from one of the other recruiters re-inviting me to their job instead – including attached paperwork to progress the application.
There were a handful of small but mildly offensive – mostly passive-aggressive – actions by Google staff along the way. Overall I was shocked, I felt that the way I was treated was poor. I was also concerned about the process itself. They admitted they had detailed feedback on my rejection, but refused to divulge any of it (legally, they had to; I forced them to pony-up). I decided to wait for the legal process to complete, and for me to get the feedback, before making any further decisions.
Once I’d read through the 12 pages of feedback, I wrote back with this:
Thanks, [name removed].
After my experience in the 2nd-round interviews, and reading the detailed feedback from the Google interviewers, I’ve realised I’m not suitable to work at Google right now.
The role-specific things were fine, but I did terribly in the 2nd-round. Reading the feedback, it seems you want a certain type of person, and that doesn’t seem like me.
Also, it’s not the company I thought it was. I want to work at companies which live and breathe an open culture, and that was part of my attraction to Google, but the reality is very different from the public image.
Thanks,
Adam
(UPDATE: it’s been more than a month now. Just so you know – I never got a response. Maybe there’s now a great big “DO NOT HIRE” mark on my file ;))
I forwarded the detailed Google feedback to a handful friends and ex-colleagues, and it’s been food for some interesting discussions so far. One ex-Googler confirmed that I was rejected by not just one, but all four, of the second-round interviewers. A dubious honour, perhaps? :)
A couple of people have remarked that Google USA differs substantially on at least some of the key issues that came up.
One thing’s for sure, though: I would certainly not want to work for Google Europe right now. They and I seem to disagree on some fairly basic issues of work and people. Neither is right, nor wrong. But I – personally – just don’t want to work at a company that works like that. So … mutual rejection! :) I’m certainly not following-up with any other Google jobs.
Unfortunately, the whole experience left me quite shaken: I’d been using Google as a shining example of various recruitment and employment practices, at least some of which I now know to be untrue. Maybe Google used to act better, but they certainly don’t today – I know this first-hand. I’m hugely disappointed.
I apologise to anyone I’ve accidentally deceived over the years. I’m also wondering what other examples of humane and open companies I can use instead – I’m no longer confident in citing Google. I’ve seen a few come up on VentureHacks twitter feed/blog-posts (awesome resource that VH is).
(if you haven’t read the main post explaining this, read this first)
“If you have trouble grabbing the beam, just keep trying—we promise it works, but lots of readers have told us it’s not always easy.”
I’m a pretty good AC player, but after 10 minutes of trying to do that one standing jump, I gave up and stopped playing for a long time in frustration.
When game developers talk about “games should be so easy that all players can complete them; no-one should ever have to give up / fail to complete a game because something is too hard”, I usually disagree.
But in this instance, where the game is extremely, excessively difficult on something that the designer obviously intended to be extremely simple – and where the player has spent hours being taught that this will be easy – you have something different going on. It’s a failure of the control scheme; in fact, it’s a bug.
It’s a side-effect of the heuristics that AC uses to decide “what the player is trying to do” – heuristics that are far from perfect, while being very good.
In the first game, it took me a long time to get past the intro – no, really – because if you *try* to jump over gaps, then you fail. The heuristics were so heavily weighted towards “allowing” you to jump off buildings that running over a small gap became very difficult – until you learnt that the character “automatically” jumps small distances.
On the whole, I’m very impressed by the AC2 heuristics – compare it to Mirror’s Edge (a beautiful game, but feels a lot less fluid). I find them a bit too simplistic – I would love another 25% or so of user-control, and another 50% of precision on directional control – but (as ME shows) they got closer to perfect than any other game so far.
BUT … what do you do about a bug like this, one severe enough to make me stop playing the game entirely?
They had a huge QA team already (this is Ubisoft, after all), and such a vast amount of content in this game (multiple entire cities, modelled in fine detail), that there’s no way they could be sure to catch this bug.
Or is there?
This is the raison d’etre for a whole segment of in-game analytics / metrics: data-mining to discover undiscovered bugs.
Good metrics for game designers are VERY hard to describe, and IME the vast majority of the industry doesn’t know how to carefully hand-pick the few numbers they really need out of the millions of stats availalbe. Here’s a good example of how to pick well.
If the game reported
“the quest-point at which people stopped playing”
…then you *might* discover this bug. But it’s too coarse-grained.
If the game reported either/both:
“the segment on the map where people stopped playing”
“the segment on the map where people spent most-time during a mission”
…then you’d quickly and easily discover this bug. By “segment” I mean, essentially, a small patch of polygons approximately 6’x6′. This is relatively easy to measure algorithmically using greedy-polygon grabbing and hashing – although it would take a little care to make sure the measurement of the value didn’t take much CPU time (it could easily be pre-compiled for any given map, of course).
I’m not 100% of the “stopped playing” part – this is a console game, and while that info would be useful, it would mostly stop evenly distributed over quest-end points. Where it was more / less likely, it would be obvious just from knowledge of the story. ALTHOUGH: still well worth doing *in case* there were anomalies there – that should set off alarm bells.
However, the “spent most time during a mission” is more cut-and-dried.
This probe gives you a set of local maxima. It’s categoriesed by mission, making it one level finer than doing it over the entire world-map (which is too much, too uncategorised info), and it’s also coarse enough to correlate closely with user-behaviour (it merges results mission-by-mission; recurring bugs are very likely to show up by people doing the same mission and getting stuck at the same point).
The mission-based merge of results also has a nice side-effect: it tends to iron-out any anomalous results due to people wandering around the open-world game.
So. With a little bit of probing, using probes that you could/should have invented at the start of development (i.e. without knowledge of exact bugs that would occur) this bug could be ironed out. The three remaining questions are:
The font-settings in ThunderBird are terrible: the default settings are ugly, and the GUI is too broken to let you change them. Fortunately, if you hand-edit the config files, you *can* change the fonts, as much as you like.
Sadly, the options etc are undocumented, and easy to get wrong. Incidentally, not only does TB have *no* help or documentation (why not?), if you go to the online “knowledge base” and search for something fairly obvious like “fonts”, you get nothing – not even the usual 3-years-out-of-date result that’s normal for this app:
For future reference, here’s how I made my fonts go from “ugly” to “delightful”.
For reference, in case TB fixes their fonts in future, here’s what you’re supposed to do, but doesn’t quite work yet:
In reality, here’s what happens
There are a couple of things that DO work, and which you need to set anyway, so:
NB: TB is … confused … when it comes to font choice. The obvious way to make a desktop app is to ask the user “which font do you want to use in place X?” (where X = “email body”, “compose window”, “menu items”, “dialogs”, etc). Windows has been doing this for more than 15 years. TB instead arbitrarily declares different parts of the app as “Proportional” or “Monospaced” or some combination of the two. Even if you tell it not to use monospaced in certain places, it mostly ignores you. It doesn’t tell you which is which, and it’s not documented, you have to work it out by trial and error.
Edit the master config file:
(I’m still using TB 3; I need a desktop client at the moment, and I find myself still using it. TB 3.0.4 seems to run well on OS X. It’s fast enough and with little enough RAM that it’s easy to leave running and forget about it; earlier versions of TB on OS X would kill your Mac, and had to be force-quit frequently. With this version, I keep forgetting I’ve got it running, until it sends out a Growl…)
(Third and final Thunderbird post (promise!))
I had an idea; maybe if I deleted TB, then restarted it, and forced it to go offline first, THEN configured folders, THEN allowed it to connect, it might. just. work.
Of course, doing so discovered some obvious bugs in TB. Sigh. I got it mostly working in the end, after some false starts. So, if this is something you want to try, here’s how to do it:
1. Start TB, and give it your account details.
You want it to look like this (see note above; if you don’t do this correctly, you’ll get different settings, with no SSL/TLS)
2. QUICKLY go to offline mode (next step) or … unplug your internet cable – this may be easier
Why on *earth* do you have to do this?
Well … there’s a major bug in TB. If you do NOT create an account *the first time the app launches*, then you can never create a Gmail account. The code that configures email accounts *will not run* after that first launch.
Really, I’m serious: try it. It will – if you’re lucky – try to configure your gmail account with all encryption turned off, thereby sharing your emails with everyone on the network. Nothing you can do will make it accept SSL/TLS. Even typing the port number manually, it fails to work.
BUT … if you allow it to configure the account on first-run, it will correctly setup everything as SSL/TLS
3. Go to offline mode
4. Go to Synchronization settings (from the same menu – File menu)
Click the ADVANCED button (it’s not advanced, it’s basic, but this is a hangover from the Mozilla Mail days, when the GUI for configuring the app was poorly arranged)
5. De-select the folders that Thunderbird should never have pre-selected in the first place
NB: this is language dependent! Google “kindly” names some of these folders depending on your regional language. Great idea, unfortunately it makes config / instructions a bit more tricky.
In the UK (this is different in USA!), the folders you must untick are:
and this:
6. Are you finished ? No, you aren’t; There’s some “magic” folders left…
On the left, under Inbox, there are a couple of magic folders that don’t appear in the synch list – but will synch automatically, and kill your disk space. One of them in particular: “All Mail” (that is: every single email that Gmail has for you, ever, anywhere. All unsorted)
You have to right-click, and go to the 3rd tab, and deselect the checkbox, to make this safe:
You MUST also do a difficult one – the Trash folder.
Thunderbird will *not allow you* to deselect this one, but you have to expand it, and inside find the “Bin” and “Trash” folders, and manually deselect them as with “All Mail”.
NB: in the attached screenshot, I hadn’t realised this was needed, so you can see it’s downloaded my whole Trash folder from the server. Ugh.
NB: yes, in theory you have *already* deselected those folders. But with Gmail you can easily end up with two copies of the folders, both with the same name, but in different subfolders. Depends which clients you’ve used with Gmail in the past (ironically, in my case, they exist because I ran an earlier version of Thunderbird, where the Gmail integration wasn’t so good).
(you may also want to do the same for Sent Mail and Spam – depending on personal preference / need)
7. Finally (finally!) you can de-enable Work Offline, from the File menu
Now what?
Well, nothing happens. Because Thunderbird is “magic!”.
If you wait long enough, and you’re lucky, it will magically start to synch.
If you don’t like waiting, staring at a computer, you can force it to download – go to the File menu, and from the Offline submenu, select Download now.
HOWEVER once again there are issues: this will work outside the normal Activity Window system, so your download will be in the foreground, and doesn’t even appear as an “Activity”. Sigh. So, you might want to try waiting for the “magic” to happen instead, and hope you get lucky.
(in theory, it should be a 10 minute wait at most, but earlier versions of TB 3 used to have severe problems with this, sometimes never synching at all)
This is still imperfect. Thunderbird insists on “indexing” all 5,000 of my Sent Mail contents – although at least it’s no longer trying to download them all locally. It also insists on “indexing” the contents of things like “All Mail”, which is completely incorrect.
But I can find no way to remove the annoying “All Mail” folder, and if you ever click on it accidentally, then BAM! the expensive processing starts. The best you can do is use the little buttons at the top left hand side to switch mode to ”
Oh – and you aren’t allowed to stop it. The Activity Manager is missing a feature (which is already in bug reports from last year – I remember seeing people ask for it) : a “click to cancel” button for each activity.
Hopefully, though, so long as you remember to avoid clicking on those magic folders, you can at least make TB work normally…
Ditto for all the folders we chose not to download locally … and, ditto, I can’t find a way to remove them from the list, so there’s no way to stop myself from accidentally clicking one.
There are a few other things you should do too, to make up for the incorrect GMail setup. For instance, you should go to Thunderbird settings, and DISABLE the “save copy of messages in Sent folder”, because GMail will automatically do that on the server side anyway.
I was just about to press the post button, when I noticed something horrific.
TB just started downloading the 5,000 emails in a “Bin” folder … somewhere … despite my very clear instructions not to.
This is a buggy load of crap. Try the workarounds listed in this post – see if they work for you. But, ultimately “Don’t use it” is my advice.
When you run Thunderbird, and connect to gmail, it downloads your inbox.
Then it does nothing, for an hour or so.
When you run Thunderbird a second or third time, OR when you first synch a single IMAP folder (I don’t know which of these two events triggered it) it waits a minute, and then spontaneously downloads your entire GMAIL account. Since Google makes it very hard to delete emails, this probably amounts to a huge amount of data.
It also means downloading two (or more) copies of everything that has a label.
If you tell it NOT to download folders, it ignores you and downloads them anyway.
On my laptop, I don’t have several gigabytes of spare storage to carry every piece of random crap ever sent to me on Gmail. I cannot run Thunderbird. I hereby giveup! I wish they’d dump the “clever” stuff they don’t seem to be able to make work, and just provide a simple, straightforward, “email that does what you tell it to”, and *then* add the rest by plugins…
If you *don’t* use Gmail, TB is looking like a viable option right now (although still lacking features that its predecessor – Mozilla Mail – already had).
(I tried every option on the folder – they all said the folder would NOT be synchronized, even as the activity manager was saying “now downloading 1 of 15,000” (ish))
Thunderbird just released an update a couple of weeks ago, so I thought I’d try it out.
Day 1: Thunderbird refuses to check email. See this screenshot, note the timestamps, while I’m sitting here waiting for an email that should ahve arrived a few minutes ago, desperately pressing the “get mail” button, and thunderbird refuses to do anything at all:
I’m not sure I’ll bother with a day 2; an email client that cannot fetch email is no email client at all.
(although, that said, I’ve noticed a fascinating bug with Google’s iPhone contender (the Nexus One) – it cannot send email, even though it displays a message saying “Sending email…”, unless you switch the phone into a special “synching” mode, or manually tell it to Refresh the inbox; until you do that, the “Sending email…” message displays forever, not actually doing anything. Basic testing that Google apparently forgot to do, sigh)