Because I can't leave well enough alone, every few months I seem to always start tinkering with Scry and begin considering a rewrite. The cycle continues and I'd like to give some details on where I want to go with Scry in the near future.
I had an idea some time ago about modularizing Scry. What I mean by that is splitting Scry up into a series of extensions called modules that have defined roles:
A main module as the conductor. It handles receiving requests from site modules, requesting Oracle text and pricing info from the appropriate modules, and building the card popup and injecting it into the page.
Site modules would be responsible for finding card links and requesting a popup be injected from the main module.
Oracle modules would fetch the Oracle text for a card from some source. magiccards.info is what Scry currently uses but I have some thoughts about changing this.
Pricing modules lookup prices from a source.
The advantage to separating Scry into these pieces means there can be multiple modules for each task and the end user decides which one they want. For example, TCGPlayer is great for players who want to lookup prices for physical cards, but for MODO prices blacklotusproject.com is better.
Additionally, I can write a base set of modules and hopefully the community can more easily contribute new modules. Know of a site that you want Scry to work on? Write a site module. The aim is to provide skeleton modules for budding contributes to get started.
The best news is that this isn't just pie-in-the-sky hopes and dreams now. I finally sat down and hammered out the basics of cross-extension communication. I have a site module that asks the main module to inject some content into certain pages. There's still a bit more to flesh out but that was definitely the biggest challenge.
Unfortunately a completely rewritten Scry is still a long ways off. There are a couple more framework challenges to solve and my free time has become more limited lately. But since I succeeded today in getting a big part working I don't think this is a goal I'll drop.
Technical Info
One of the biggest hurdles is Chrome permissions. For an extension to inject content into a site the extension must have the user's permission to do so. Since the main module is doing the actual content injection it needs these permissions. What I don't want to do, though, is have the main module on install look for permission to inject content on any site anywhere; that's just sloppy and makes end users justifiably wary.
The solution is Chrome's optional permissions. Instead of having the main module request permission for every site, it let's Chrome know that it might need permissions to inject content into any site. The main module can't actually do that unless the user explicitly approves it sometime after install. To get the needed permission, when a site module is installed it informs the main module of what site permissions it has. The main module opens its options page and the user can then explicitly grant the main module the same site permissions that the newly installed site module has. The user is informed of exactly what new permissions is the main module is requesting. The cool part about this is that the main module doesn't need to know what site modules are out there and what sites they are dealing with; the site modules themselves tell the main module of the new Scry playground.
Is Unlimited really the first Un- set? When it was released in all its white-bordered glory some players considered it inferior to real (read Alpha and Beta) Magic. It also drafts about as well as the other two Un- sets.
For a few months now I've been working on rollover popups for autocard links here on Sally. I went back through the thread earlier today and it's interesting to see just how far the project has come. My original intention was just to throw together something and leave it for anyone else who wanted to hack at it. However, the response I got and the enjoyment I had just coding the thing kept me working at it. It's come a long way since being just a simple Google Chrome extension to a cross-browser compatible userscript.
Chrome and ajax
I adore Chrome. It's fast, cutting-edge, and has the support of one of the industry giants behind it. After doing some extension work on Firefox I am more than pleased with how Chrome handles extension development. Firefox requires lots of obscure boilerplate code and compiling just to get a simple extension going. Chrome, in contrast, leverages the web technologies a lot of us have been using for awhile. If you know javascript you can create a Chrome extension in five minutes. There's no compiling, no odd packaging, no big frustrations. I originally wrote the project as a Chrome extension precisely because it was quick and easy to get up and running.
I am frustrated by Chrome's lack of complete support for userscripts, though. Userscripts allow a lot of cross-browser support for things just like adding rollover popups to a site. They're a very appealing option when users start asking for your stuff to work on multiple browsers. Chrome, though, makes it tough. It's default support for userscripts lacks a way to do cross domain requests. These ajax requests as they're commonly known allow a lot of cool integrations of technologies. "Mashups" of Google Maps with all sorts of content began with userscripts and ajax. For security reasons Chrome disallows userscripts to grab content from other domains. This meant for me that having just a userscript that would work on many browsers was out of the question.
NinjaKit to the rescue
Thank goodness for NinjaKit. This little Chrome extension is basically Greasemonkey for Chrome. Greasemonkey is a Firefox extension that provides userscript support to that browser. NinjaKit goes about extending Chrome's built-in userscript support by adding all the features that Greasemonkey provides and, most importantly, allowing ajax requests. Once I discovered NinjaKit I was able to consolidate the two trees I had, one for Chrome and one for Firefox, into one nice, neat userscript.
I admit that switching to NinjaKit did pull the project back a little. Chrome has cool support for context menu entries that Greasemonkey and NinjaKit try to emulate but not in as nice a fashion. The NinjaKit entries are pretty much hidden; you have to know to click on the icon in the URL bar to find them. I also miss Chrome's ability to push extension updates out to users. I've had to basically announce updates on the thread and hope users update to the latest version of the script. Hopefully the auto-updating support in the new version of the script will alleviate that; one more manual update by users and the auto-checker should handle the rest.
jQuery is God
I wouldn't have started this project at all if it weren't for jQuery. I know there are some developers out there who think jQuery is bloated and overkill. They're right on the first but wrong on the last. In today's internet having a solution that solves lots of common but complex tasks is essential. Who cares if it's a few kilobytes in size when most people are on connections that can pull down gigabytes in an hour? Who cares if it might not be optimized to run as fast as home grown stuff (which often is slower than you think) when modern browsers run javascript insanely fast on insanely fast processors?
Since the point of the project is to grab data with ajax and then alter the DOM1 of a webpage it seemed natural to go with a utility that has such support baked in. As the scope of the project has grown I've leaned more heavily on jQuery; there's basically so much DOM manipulation going on that if I were to try and replace jQuery I suspect the userscript would be twice as large and three times as fragile. There are one or two pieces of jQuery 1.4.x that I would like to use, but Greasemonkey and jQuery don't place nicely together past version 1.3.2. C'est la vie.
Where to now?
The current userscript has the following features:
Large image popups from magiccards.info
Auto-expanding Oracle text and edition listings from magiccards.info
Theme integration
Prices from tcgplayer.com
Integration with mtgsalvation.com, wizards.com, starcitygames.com, and mananation.com
User configurable animation and expansion toggles (Firefox only for now)
When I look at that list I'm actually quite pleased with how things have gone. The project originally had just one feature: showing a card image with Oracle text. Where it's going to go now and what will be added next is anyone's guess. I'm open to suggestions and adding support for sites other than those listed above shouldn't be too difficult. There's obviously some bugs that I'll need to fix eventually; one particularly annoying one about an occasionally jumping popup is on the list. I'm just really happy at where it is now.
1DOM stands for Document Object Model. A webpage is stored behind the scenes as groups of objects arranged in a defined way. Tables have entries underneath them and markup to text surrounds the text it alters. When you add something to a webpage after it's loaded you are altering the DOM to insert these new elements where you need them.
Something just occurred to me about Assassin's Creed. There are absolutely no children in the game. The environments contain lots of random people but not one under the age of 20. It's like the animus has no concept of non-adult people.
I'm sure the game engine can support child actors just fine so I don't believe that's the reason. I suspect it is just easier to avoid any fallout from having children killed in a game. Fallout takes the approach of just making children invincible which certainly detracts from the immersiveness of the game. Given that's what AC strives for, and has largely achieved with the last two entries, I guess including invincible children was a no go.
I'm not complaining about this in any way. It's just something that struck me as odd this morning. I'm surprised I hadn't noticed it some time ago.
Awhile ago in Magic General there was a thread about removing Instant as a card type; proposals were to make Instant a supertype or remove it entirely and instead use flash. I come firmly down on the camp of Instant as a supertype (which would remove flash as a keyword ability).
I began to think about the type line some more and came to the conclusion that it does need some more tweaking. Some things need to be moved and some new things need to be added. The type line as it exists isn't broken per say (other than Tribal) but it could be better.
I must note that in this discussion I'm avoiding Planechase and Archenemy specific additions to the type line.
Supertypes
The current supertypes are Basic, Legendary, Snow, and World. All carry some inherent rules which is fine. Supertypes are meant to convey some difference about the object: a legendary creature is obviously different from a non-legendary creature. Technically supertypes could all be converted into keyword abilities but that would just muddy up text boxes. Proposed Changes:
Make Instant a supertype - Instants are currently pretty much Sorceries with flash. The arguments for this are detailed in the linked thread above.
Allow Basic to be applied to objects other than Lands - we've pretty much already got a Basic Creature. The rules currently allow the supertype to apply to just Lands but for no discernible reason. I'm not saying we need Basic Enchantments or Basic Sorceries, but the game can support things other than Lands being Basic.
Add a Token supertype - currently the game distinguishes between cards and tokens based simply on cards being a physical Magic card and tokens not. While this works within the rules the tokens that are now in booster packs can distort what exactly is a card for some players. Having a Token supertype can clearly mark what exactly is a token and what isn't.
Add a Unique supertype - this is mainly for a change I'll propose later when it comes to the Planeswalker type. Currently the "planeswalker uniqueness rule" is invoked by the Planeswalker type. The rule is very similar to both the "legendary rule" and the "world rule", and since both of those rules are invoked by a supertype it makes since to move the "uniqueness rule" out of the Planeswalker type and into a supertype of its own. I can't see this being applied to anything but planeswalkers at the moment, but maybe in the future there could be some other type that would require the same uniqueness quality that planeswalkers have.
Types
The current types are Artifact, Creature, Enchantment, Instant, Land, Planeswalker, Sorcery, and Tribal. Each of the types has its own rules (except Instant which redefines some of Sorcery's timing rules) and most have their own set of subtypes (Instant and Sorcery share a set as do Creature and Tribal). Types are important because they separate distinct concepts and allow some level of composition (Artifact Creature, Creature Enchantment, etc.). Most of this is fine. Proposed Changes:
Remove the Instant type - this goes along with the proposed change to supertypes above.
Remove the uniqueness rule of the Planeswalker type - as detailed above.
Subtypes
The current subtypes are too numerous to mention here. There's a few separate sets of subtypes that can be attached only to certain sets. For example, only enchantments can have either the Aura or Shrine subtype. For the most part subtypes care no rules baggage, except in a few cases:
Some of the Land subtypes carry a mana generating activated ability
Aura has some very detailed rules and is tied directly to the enchant keyword ability.
Equipment and Fortification also have detailed rules and are tied directly to the equip and fortify keyword abilities.
It would be nice to be able to remove the rules baggage from the subtypes, and I suspect it would be doable with the Aura, Equipment, and Fortification subtypes (key the rules off the existence of the appropriate keyword ability). However, the basic land subtypes are actually pretty important. The game went through a time where these subtypes didn't have rules baggage nor exist. It made type changing cards a little wonky with respect to lands. Without the rules baggage for these subtypes cards like Blood Moon would have to specify that the lands get the Mountain ability. It's ugly and inelegant and the current way is very much worth the slight disconnect in having subtypes carry rules weight. Something might be able to be done with adding another supertype, but that gets into some weird rule hacks since currently adding supertypes doesn't change anything else about the card. Type changing is a sticky business.
Tribal
Earlier I stated that the Tribal type is broken. It functions like a supertype but is actually a type. Unlike every other type it can't exist on its own and it serves as nothing more than a holder for creature subtypes. I understand the rules reasons why it needs to be a type and not a supertype, but it's just ugly as it is. The name is even bad: other types are all nouns whereas tribal is an adjective.
So what to do about Tribal? I'm not sure. I've pondered ways to turn it into a supertype but I keep running into the issue of having subtypes attached to a supertype which muddies the distinction between supertypes and types. I've thought about turning Tribal into a keyword ability, something like "Tribal -- Goblin" but I'm not sure if that would work cleanly. Tribal is the ugly hack that has turned into the enigma. I'm going to keep working on it because I believe there is some solution.
I've been playing Nethack on alt.org for awhile now and recently I've been trying out pacifist conduct. So today I start a gnomish healer and begin with the standard strategies for pacifists. I attempt to drink my blessed potion of extra healing, for the HP boost, and out pops a genie! The RNG has kindly made potions of extra healing smoky potions (each of which might contain a genie) and started me off with three uncursed ones and one blessed potion.
So the genie grants me a wish. I use that wish to get a pet archon. An archon is a very powerful pet that can take out pretty much anything that will be thrown at me in the early game; this is important for a pacifist since I can't kill monsters myself. The archon proceeds to kick butt and destroy everything in my path, usually in one or two hits. I come across two shopkeepers who give me all their stuff, including all precious gold, after my pet archon destroys them in a couple hits. Even better I stumble across a Hawaiian shirt randomly in a level; Hawaiian shirts are actually pretty rare in the game and are very helpful come the late game. A shirt now means I don't have to use a precious wish later to get one.
So far the RNG has been very generous what with the pet archon, a couple shopkeepers and their gold, and a rare piece of armor. I'm thinking everything is great so I'll head down to the guaranteed priest in Minetown and buy some divine protection. This is important to a pacifist since you need to be as hard as possible to hit. I travel through the mines without much hassle (thank you pet archon) when suddenly I fall through a trapdoor. Luckily my pet archon was next to me so he came down with me. Even better was that the trapdoor put me directly into Minetown. A quick trip to the priest for some simony and I should be sitting even prettier than I am now. After the initial protection buy I can lead my archon around to the guaranteed shopkeepers in Minetown for more gold to buy more protection.
Unfortunately here's where things start to go very wrong. While I was dropped into the center of Minetown so was my archon. He found the priest before me since he's faster than me and proceeded to destroy my source of divine favor long before I was able to reach the priest. So the large amount of gold I have amassed for the sole purpose of buying tons of protection is now useless. Furthermore, the orc that was down the hall has shown up and the RNG decides to cancel out my good fortune by giving the orc a wand of magic missile. A few zaps from that and I'm quickly dead.
Over the past two years I've developed a reputation at work for being the guy that is good at fixing software bugs. I'm not sure why but developing a fix for most bugs just comes naturally to me. During this time I've worked out a set of procedures I use when fixing a bug. I've begun to call these procedures the RED method. Now I'm sure what I've worked out isn't new; in fact I think it's probably been standard practice in software development for ages. But for some reason I keep seeing developers trying to fix bugs using only part of the RED method and these developers don't understand why the fix for a bug keeps eluding them.
So what is the RED method? It's basically three steps: Reproduce, Evaluate, and Debug. These three steps have allowed me to quickly identify the source of a bug and develop a fix for that bug in relatively quickly. Let me detail the steps.
Reproduce
Reproducing a bug not only allows you to verify that the bug actually exists but also gives you a test case to develop against. Being able to confidently test your fix is crucial to ensuring that you've actually fixed the bug.
Evaluate
This is where I see most developers spend their time when fixing a bug. I'll say straight out that that's the wrong way to approach things. Evaluation should be spent looking for obvious problems (bad string values, wrong constants, etc.) and putting in debugging statements to help isolate a bug down to its base parts. Fixing a bug takes more than just staring at code so evaluation can go only so far. It's important but shouldn't be what you rely on to fix a bug.
Debug
This is the most important step. Once you've identified where in the code a bug is occurring it's time to step into the running code and investigate. Bugs are more often about the state of the program instead of a place in the program's code. If a bug is cropping up because a variable is turning out to be null when the assumption is it's never supposed to be null then the bug isn't that the variable is null. The bug in the code is before the line where the code dies in this case. The state of the running code is more important the code itself. Debugging allows you to step into the program while it's running and investigate its current state. Being able to step through the program line by line will allow you to conclusively discover where your code is wrong and what state is causing your code to blow up. The best fixes for bugs come from understanding why the bug is occurring more so than where the bug is happening.
For example, the scenario described above can be solved in two ways:
Add a check to make sure the variable isn't null.
Eliminate all situations that are causing the variable to be null.
The first might be correct sometimes but in case where the assumption that the variable should never be null is correct then you can't just add a conditional. If you do then you're pushing the bug further down in the execution or creating more work for yourself to handle the situation of this variable being null.
If you instead identify the situations that are causing the variable to be null, and consequently invalidating your assumption the variable should never be null, and you eliminate all of these situations from ever happening then you've conclusively fixed that bug. A different bug related to the first bug is not going to show up latter in the execution because you've removed the first bug completely. Fixing bugs is supposed to be about removing defects in code if possible instead of just hiding the situations that expose these defects.