Monday, April 30, 2012

Guild Wars 2


Unlike most of my friends, I watched Guild Wars 2 from afar. Every once in a while I'd drop by the website and read a bit, but most of the development I wanted to keep a surprise. I knew almost nothing about playing the game, with the exception of some minor details like the dye system (which, astonishingly, does not easily lead to fashion disasters; you have to really work for those). I had read a bit about the sidekick system, events, the aggressive removal of the Tank-Healer-Damage party structure, and seen some combat skill overviews; but deliberately avoided thinking about them until I could play the game. I rather enjoyed the result, though it meant that many implications only set in with about 3 hours left in the beta event.

Wednesday, June 08, 2011

I Paid For It


I knew I liked this game the moment the menu music loaded up.

It's fairly simple, once you grasp the mechanics: there are two meters, reminiscent of the hit point and special meters in fighting games. The first meter is Patience, and the stage ends when this runs out. Your normal attacks--as well as bonuses--are cast out of this bar, though it will also drain fairly rapidly just doing nothing. The second meter is Stress, and normal attacks add points to it. When you have enough points, you can cast a Special attack.

The damage you inflict is added to your score (besting a previous score earns a money bonus), and each hit has a chance to grant you money, which is used for upgrades. You can upgrade the maximums of both meters to 1000 Patience and 100 Stress, as well as critical and money chance to 50%, as well as buy bigger attacks, specials, and temporary bonuses.

The real draw, however, are the ability animations. Much care was put into the animation of all abilities, as they are very smooth and visually appealing. My favorites are the Lightsaber and Chain attacks, as well as Fire 2, Water 3.

Thursday, May 19, 2011

The End of the World


So, apparently, the Bible says no man can know when the world ends. It's kinda funny, I mean, now people are saying the world can't end on Saturday at 6pm local time, because some random Joe thinks it will.

Think about that for a second. It's highly exploitable.

So here's the plan:
1) A computer assigns (future) dates and times to every person on Earth, and to new arrivals as they appear.
2) These people are told that this is the date, hour, minute, and second that the world ends and tell no one else the date they were given.
3) As people die, their dates are placed back into the computer and doled out again if they are still in the future.

There are approximately 6.77 billion people on Earth, which comes out to about 214 years during which the world cannot end without a single one person knowing the date or hour (or minute or second; the last thing we need is God slipping the end of the world in between two hours). Unfortunately, we can't go to milliseconds; that only gets us one fifth of a year.

It is absolutely critical that each person knows only the date and time they were assigned: the moment a person knows two, there is an uncertainty with his own, and the world ends.

It may be necessary for the people operating the computer to not participate in this project.

For safety, some people should be held in reserve, in case it becomes necessary to quickly have someone know when the world ends (i.e.: someone died and the world ends tomorrow). That may not be strictly necessary, since this knowledge should apply from the afterlife as well, but we should not take any chances.

Friday, April 08, 2011

Python switch statement

Having a bit of fun at 3 in the morning.

The Python version of the C-style switch statement is either a bunch of if/else's or a dictionary. Going the dictionary route can be ugly looking (not to mention confusing, pulling a value out of a mapping and calling it). This just encapsulates the whole thing and adds some support for a pseudo-default case.

The structure is a bit weird, nowhere near as nice as regular syntax, but if you absolutely have to do this, it makes it easier to read.

Wednesday, April 06, 2011

And now I can't remember what got me on this line of thought...

So here I am, hacking away in Python for oh, a few years now, and I just remembered what got me into Python in the first place.

See, I was using Python's implicit line wrapping with a tuple, and remembered back to when I read an article explaining that Python did that. Before that, all my longer lists weren't wrapped, or wrapped with '\'.

That made me recall the impetus that had me looking for such information in the first place: a Wiimote driver for Linux called CWiid.

Which, coincidently, was the first time I had seriously taken a look at Python.

I was using it to add motion control to Descent 3, which I had just purchased from GoG.com, which was... September 23rd, 2008.

I got something rudimentary going, noted that it would take a lot of hacking to make it better, and I needed to be better at the extension language it used to do it myself. So I waded into that language, which turned out to be Python.



Huh. I never did get back to motion control in Descent 3...

Wednesday, June 09, 2010

Quantum Electrodynamic Threshold

Wikipedia: Quantum Electrodynamic Threshold

Whether this is real or not is immaterial to my purposes. What I really want to know is, if one were to be fixed in place about a meter from the edge of one of these things--unaffected by the massive magnetic field--then 1) what would the region look like, and 2) what would happen to matter entering the region assuming the magnetics don't immediately pull it to the center. Also, what does it look like from the inside--looking deeper in, to the side, and out.

Tuesday, November 24, 2009

Ninja Planet

16:55:24

You're standing in the wasteland of the planet Ninja.

The desolate land that stretches out before you is scattered with knobbly trees, swaying lightly in the breeze. Twisted mountain peaks occlude the horizon, silhouetted by the setting sun behind them, and casting shadows that claw their way towards you. The whole landscape radiates a level malovence that should be impossible for mere nature.

You knew the moment you entered the atmosphere in you exploration lander than even coming near this planet was a horrible mistake, but here you are, 3 feet from the ladder back into the craft, standing, frozen in place, and attempting to plan out your escape.

The breeze picks up slightly, pushing a light cloud of dirt off the vacant landscape and into the air. Your eyes catch on the swirls in the ominously blood-red dust. Are they simple vortexes, or perhaps, something else..? A small twig or rock bounces lightly off your right thigh. You don't dare move to see which.

Timing drifts through your head. Approximately five seconds to get to the airlock, two to key it open and another to step through and hit the 30 second cycle sequence. One long minute to run through the abbreviated emergency checklist. 8 minutes to orbit.

Nine minutes and thirty eight seconds. Five hundred and seventy eight ways to die.

This is planet Ninja. In front of you? Well over a thousand ninjas... probably. You imagine them watching; Waiting for weakness. You hope it's your imagination.

16:55:25

You discover your legs won't move. One second of thought, one more way to die, though this one more unanticipated than the rest. Your hand brushes over a small pin stuck to your right thigh.

Inaction has consequences too.

Tuesday, November 03, 2009

Mapping Shipment Tracking

You know when you buy something online and the retailer gives you a number that lets you check on it's progress as it makes it's way to the destination? They generally give you a list events--checked in, checked out, in transit, out for delivery, etc.--along with times.

Sometimes, when I have nothing better to do, I take the locations its been and string them together in Google Maps (guessing because the locations don't usually have addresses, but I can guess pretty well with satellite view.)

But the shipper knows exactly where those locations are, and moreover, the route the truck is actually scheduled to take, though this matters more for delivery than shipment. So here's what you do:

  1. Plot the anticipated route on Google Maps. I know you can, you don't run a shipment company for years without some way to predict the likely path any given package will take.
  2. Plot the actual route as updates come in. Revise the anticipated route if needed.
  3. Plot events as pins. Stack multiple events in one pin with timestamps, no need for pin forests at an overnight stay at a warehouse.
  4. If you have a way to determine the location of the truck, consider plotting that on the map. Perhaps in real time.

Now your customers can not only read where it is, but can look on a map and see how far away it is.

Better, Google already detects tracking numbers people search for and forwards them to the appropriate page. Leave the mapping data open and get Google to add to the "Forward to Tracking" page a "View tracking in Google Maps" link.

Tuesday, September 08, 2009

Duck Typing

So, in Python there's this concept of Duck Typing. First time I read about it (shortly after getting into Python,) I passed it off as a curiosity. No idea how to take advantage of the concept.

So now I'm taking a Java class (mostly because the Uni doesn't offer Python classes.)
import university

class Python:
    pass

university.CompSci.add(Python)
No not that kind of class!

Well, apparently in Java when you have a function that could just as easily work on int arrays as String arrays as float arrays, you need one copy of that function for each type. Which means in certain situations you have two otherwise identical functions (and I mean character-for-character identical, as in Ctrl-C, Ctrl-V identical, aside from the function signature.

So I'm just staring at this pair of functions now, and I really ought to be wrapping up the assignment, but I can't shake the feeling that this is wrong.

Sunday, September 06, 2009

Open Letter to all webcomic artists

And those with any other periodically updating website, and especially if it doesn't get updated on a regular basis.

Use RSS or Atom feeds on your website. If you have a webcomic, make a new entry for each new comic. Link directly to the comic. If possible and practical, inline the image directly into the entry.

Now, you don't have to roll a custom RSS feed. You could simply open a blog somewhere (Blogspot, Wordpress whatever floats your boat,) and drop a new post in when you update the comic. Hell, even Twitter would work

Now, why do I emphasize those sites that update infrequently? Nothing kills the mood like hovering over a bookmark and wondering to yourself "Is this even updated? Am I just wasting my time?" There are comics I read that update like clockwork and have no RSS feed. Glance at the clock and at a certain time chances of the site having been updated increases greatly. But there are others that post new pages seemingly at random. These comics actually have a special tag in my bookmarks: Sporadic. It exists especially for comics that are floating in the purgatory between irregular updates (which I check on Sundays) and comics that have been placed on Hiatus (which I check about every 6 months, and leave a note in the bookmark the last date/comic number I read.) I check this grouping of sporadic comics sporadically. A lot of them probably have more updates by now, but I can't remember where I left off. Shame really, since many are good.

Remember, with RSS feeds you subscribe and leave them alone, because they tell you when there is new content. You never have to look at an RSS feed and wonder if there's anything new (unless your feed is broken. Yeah, I'm looking at you Altermeta, your RSS updates, but something is wrong, new posts get inserted halfway down with bad publish dates.)

So there. Keep your readers. Bring them back regularly to bask in your stunning artistic prowess (and possibly some adverts,) build an RSS or Atom feed for your comic today.

Tuesday, August 25, 2009

Getting sources, source keys, and package lists out of an installed Ubuntu from a Live CD

When reinstalling Ubuntu, you may often find a large quantity of time becomes taken up afterwards as you look for applications that you had installed but failed to install after you finished reinstalling the OS.

There are ways to export and save this data before running reinstalling, backing up /etc/apt/sources.list is most commonly referenced, which gets you all the repositories you had, but you still have to reinstall the individual packages, and obtain the verification keys for them.

From a working system, you'd just

# If you want to preserve deinstalled packages, just omit the grep command 
$ cp /etc/apt/sources.list ~/sources.list
$ dpkg --get-selections | grep -v deinstall > ~/packages.list
$ sudo apt-key exportall > ~/sources.keys

In a Live CD, a situation I recently found myself in (I borked GRUB and decided that halfway through the potential fix was a good time to prepare for a possible reinstall,) it's a little harder. Those commands will get you the sources, packages, and keys from the Live CD rather than your install. Obviously, /etc/apt/sources.list is a simple copy-to-backup-partition away, but the other two are behind applications.

The solution (hijacked from ubuntuforums user 5-HT and pieced together with this Upgrade or Reinstall post by Daniel Bo) is to chroot into the old install, and then run those commands. Here's how to do that:

# /dev/sdb2 and ext3 was my install partition and type, replace those values with your own.
ubuntu@ubuntu:~$ sudo mkdir /mnt/root
ubuntu@ubuntu:~$ sudo mount -t ext3 /dev/sdb2 /mnt/root
ubuntu@ubuntu:~$ sudo mount -t proc none /mnt/root/proc
ubuntu@ubuntu:~$ sudo mount -o bind /dev /mnt/root/dev
ubuntu@ubuntu:~$ sudo chroot /mnt/root /bin/bash

Now you are root in your install partition. Now you need to mount the partition that you will store these on. If your home directory is on a different partition, you can use that/ If it's not, get another drive and back up your home partition to that drive as well as these files. You can't use nautilus for this, so here's what I did as a hint:

# Again, /dev/sda1 is for me, replace that value with the one applicable to your system.
root@ubuntu:~$ sudo mkdir /media/disk
root@ubuntu:~$ sudo mount /dev/sda1 /media/disk

Once you have the partition mounted (I'm going to assume /media/disk as described above,) you can simply run the commands:

root@ubuntu:~$ cp /etc/apt/sources.list /media/disk/sources.list
root@ubuntu:~$ dpkg --get-selections | grep -v deinstall > /media/disk/packages.list
root@ubuntu:~$ sudo apt-key exportall > /media/disk/sources.keys

If you have any other files in the filesystem you need, such as apache2 configs or the /etc/hosts files, now is a good time to get them.

Now, when you have your fresh install, you can restore the data by replacing or adding to the new sources.list file (remember to change the lines if you changed Ubuntu versions,) adding the keys back into apt, and re-installing all your packages. Ironically, the hardest part now (possibly) is the sources file if you need to alter it. If it's the same, just drag it into the Third-Party tab in Software Sources app (System > Administration > Software Sources) and have it add them.

The keys file can be done the same way, except in the Authentication tab.

Now that the repositories and their keys have been added, reinstall all your packages with:

$ dpkg --set-selections < /path/to/packages.list && sudo apt-get dselect-upgrade

Or with Synaptic, File > Read Markings and press Apply.

Saturday, May 16, 2009

Simple Extension implemented in Shimmie core

Simple Extension functionality was imported into Shimmie core earlier this week. If you have this extension active, PHP will complain that you can't define SimpleExtension twice.

There were some changes during the import due to some problems with the Shimmie forum (broke during upgrade to phpBB3) and Shish ended up re-implementing it in core rather than performing a true import.

Unlike my version, which merely forwarded execution to functions of the same name as the event, this implementation prepends "on" and removes the trailing "Event." So an event PageRequestEvent will call a function onPageRequest.

This method, however, removes the need for the acrobatics my version was performing to prevent the forwarding from calling the receive_event and new i_am functions. The event_method_callable function is thus not needed and was not used.

I have not yet updated the source of my extensions that used this, though Shish has already made most (if not all) of the core and core-maintained extensions use it.

Thursday, February 26, 2009

KFI Internet Stream URL

Update (2010-07-22): Recently KABC also changed their streaming method. Neither station can be played in a dedicated player now.




Update (long ago): About a week ago, Clear Channel updated their internet stream again, this time to a flash based thing. Unfortunately, in this update they no longer serve the XML expected by this script to the Sonos user-agent.

If anyone has a way to grab the stream URLs from the new player, please leave a comment.




If you've been trying to play the KFI radio stream in an external media player like totem, mplayer, or VLC, you've no doubt suffered annoyance at the number of hoops you have to jump through to acquire it. Further, the url needs to be refreshed every few hours to get the new authorization code.

Luckily, there's a solution. If you set your User-Agent to "Sonos" when you go to the KFI player it will send an XML file containing the stream URL instead of redirecting to the new browser player.

You can change your User-Agent string in Firefox with User Agent Switcher, or you can fetch the url with
wget -U "Sonos" http://www.kfi640.com/cc-common/ondemand/player.html
.

Once you have the file you can just copy the URL into the player of your choice.

Or you can use a Bash script that wget's the file, parses out the url with sed, and loads the results into totem.



I can't claim complete authorship of the bash script, as I found a version on a forum first, and have only subsequently made modifications to keep it working through Clear Channels streaming changes and to add a switch for another station I listen to.

Saturday, November 22, 2008

Arrogance and Code Hosting

Arrogance: I'm going to write like I actually have an audience here.

Code hosting: I'd been unsure of how to go about posting the code to my projects, trying sites.google.com, which can host files within a wiki-type application, to Drop.IO, again with file hosting, though they both suffer from making it difficult to push large projects to them. I had to tar.gz them to keep them contained, and that's just not how you do this kind of thing.

And then Ars Technica posts a guide to setting up an account and repository on GitHub. I Already put Simple Extension there and updated the post here to reflect the new code location and changed the drop.io link to point to github. As I work on the other projects, they'll get repositories too.

Tuesday, November 11, 2008

Shimmie Extension - User Permissions

This is a three-in-one.

User Permissions manages a more complex form of user permissions than can easily be done in Board Config. Basically, in default Shimmie you have three de-facto user groups: Admin, Registered, and Anonymous. Problem is, admin has access to everything, so if you wanted to let some registered users help police images and other user content, you'd have to trust them not to screw you with their admin access, up to and including de-admining your user.

Anyway, it started as an all-in-one that established user groups, default permissions, group-level permissions, and user-specific permissions. This got unwieldy very fast, especially since the user groups part was basically an extension in itself. So I split it in four pieces: User Groups, Global Permissions, Group Permissions, and User Permissions. As posted earlier, User Groups managed groups of users.

Global Permissions was basically a store of all the registered permissions, name, description, and default value.

Group Permissions built on top of Global, if there's a setting for the group level, it uses that, or falls back to global.

User Permissions builds on top of Group. Like Group, it falls back on groups the user is in if user level is unset (multiple settings for one permission are ORed together,) or to global if there are no groups with that permission set.

These were going to be separate extensions, with group and user being agnostic of each other (though depending on global.) Unfortunately, I could not figure out how I'd go about untangling the levels from each other, so I merged the three back together, partially. It has three separate extension definitions, each handling one level, and I could drop the need to have them able to work separately from each other.

So an extension can use this by registering options by hooking the PermissionRegisteringEvent and calling $event->register(String $key, String $name, String $description, Boolean $default), then later access the permissions applied to a user with Permission::user(User $user, String $key).

It's not as clean as the Config object, I admit. I want to have access to it through the User object since it would look like User::permission(String $key), but I also didn't want to require a code patch for core files. I was able to skip requiring the Config and Database objects from being passed in to Permission::user() by cheating and importing globals into the function.

Works well regardless, though it suffers from a bit of disuse. So far there are only 2 extensions that use it.

GitHub Repository
Commit at time of post: 14735d34
Dependencies: Simple Extension, User Groups

Shimmie Extension - User Groups

Split off another extension I was writing, which needed a method to group user to allow it to act on many users at once.

User Groups does that, and later extensions can add additional features to them, such as chat, forums or news. If they do become more social contructs like that, I'll have to add a few features to facilitate that, since right now only admin can create groups and add users to them.

Probably some manner of group ownership and a few settings the owners can change regarding membership will be next.

Anyway, the initial point was accomplished: groups of users.

GitHub Repository
Commit at time of post: e81093d3
Dependencies: Simple Extension

Thursday, November 06, 2008

Shimmie Extension - Simple Extension

Got tired of defining Event::receive_event when making a new extension, and got tired of starting to code in the if statements, "Oh, it's just a little bit of code..." and then an hour later: "Crud. It's like, three pages long and 5 tabs deep now," and spend the next few days refactoring code.

Anyway, there's the inspiration. A few weeks ago I had thought that it would be easier to have an XML file control the event hooking, like WoW does, but that got kind of silly when I realized that the control would be split across the XML and extension code, and I'd have to figure out the XML DOM. Then I realized that the most basic use of the thing would essentially be exactly the same across extensions, I dropped it.

So today, I decided it wouldn't be too much of a loss if I defined the function once in such a manner that it gets the class type of the incoming event, looks for a function of the same name in the extension class, and calls that (passing the event along, of course.)

So there. Simple Extension creates another abstract of the Extension class, and does just that, as well as make the theme object, and so far it seems to work pretty well.

To use, install by uncompressing the archive into contrib/ and activate by creating a symlink in ext/ to contrib/!simple_extension or by using the Extension Manager. Until a dependency resolution system is created, removing the exclamation mark from


After that, make a new extension, but instead of "class hello_world implements Extension" use "class hello_world extends SimpleExtension" and then write methods like this to use events:

public function PageRequestEvent(Event $event) {
// PageRequestEvent code here
}


GitHub repository
Commit at time of post: 13434a2c
Dependencies: util.inc.patch (Ticket #360) to allow get_theme_object to skip non-existing base themes. Patch incorporated into core.

Shimmie

A while back I came across an open source php web application called Shimmie that stores and organizes images and other media similar to the danbooru engine.

I was looking for an organization app for photos at the time, and Google Picasa was not as flexible as I desired. Having first tried the recently opened danbooru engine, I found it difficult to even install the dependancy chain - Ruby, Rails, postgre, and some other things - I could get nothing working.

I'm not sure how I initially found Shimmie after that. Most likely it was some form of google search, and the dependancy chain was short and handled completely by the XAMPP apache package.

Anyway, Shimmie was as flexible as I wanted, but not exactly... complete. Being open source, I decided to dive into the code and change it around to my liking.

Been there ever since, contributing to the theme engine and trying to make useful extensions for the app.

Next few days, I'll start posting about some of the extensions and theme I've been working on.