Friday, October 28, 2011

Three Funny Links. (And Our Sale Is Ending Soon.)

So I need to write a blog post to remind people that Spiderweb Software's Glorious October Carnage 10% Off Sale ends in just a few days. But I don't have one of my standard 1000 word infodumps ready. You see I've been busy lately trying to finish our next game and playing Catherine on my XBox. (Working title ... "I Have Serious Issues With Women: The Game.")

So I'm going to post links to three thinks I really liked. Because that's what blogs are best at.

1. An Old Short Story

There used to be a magazine called Dragon. It was ostensibly about tabletop role-playing games, but who are we kidding? It was about Dungeons and Dragons. This was back in the good old days when you could play D&D and relax and unwind and keep up with what was going on without a computer, four whiteboards, and a specially trained idiot savant. (If you ever wanted me to write a review of Fourth Edition Dungeons & Dragons, there it was.)

Every month, Dragon magazine also had a short story, for some reason. One of these stories has stuck with me because it was so amazingly prescient. It was about MUDs, but in the story they were weird free for alls and if you did the right thing you could earn game cash that converted to real money. It predicted the existence of Eve Online twenty years before the fact, and other SF writers are just now catching up with the crazy possibilities.

Someone just sent me a link to the story, so here it is:

Catacomb, by Henry Melton

Henry, if you ever end up here and see this, your work had a big, big influence on one particular misfit boy.

2. A New Video

So far, I've managed to avoid writing any blog articles on the subject of The Game Industry Might Maybe Have Woman Issues, Just a Little Bit. To anyone looking in from outside, this statement is about as controversial as saying that water is wet, but it still gets gamers really angry.

Anyway. IGN made some sort of online reality show thingy called The Next Game Boss. It's about indie game developers. That someone made a reality show (even a web one with a micro-budget) about people who want to do what I do is pretty fascinating. Not flattering, but fascinating. Because, um ... watch this cruelly edited selection of highlights from the first episode ...

Hey, Ladies! They're Still Single!

(It gets awesome one minute in.)

There are things I could say about this, but there's really nothing that can be added to perfection.

3. I Like Music

Everyone who knows me in real life is sick of hearing about Garfunkel & Oates. But you don't know me! So I can just drop a link or two.

They really broke big over the last year or so and are now making actual money (plus an HBO development deal). If you poke around on YouTube, you will find that these two have been working their asses off for years trying this and that before they broke big. They are a great example of a universal truth: It takes ten years to make an overnight success.

OK, that seems like enough content to justify a blog post. So. We're having a sale. It's over in a few days. Order one of our series on CD and you can totally get your Hanukkah shopping done early.

Wednesday, October 19, 2011

The 10000 Hour Rule.


This image appeared on my Facebook the other day, and I really liked it. (The quote is killer, and it is available at a much more readable size here.) It reminded me of a bit of wisdom I value greatly, and I'll pass that on in this blog post. Lucky you.

I am a huge fan of the many Rules, Laws, and related discrete wisdom chunks you can find on the Internet. Sturgeon's Law. Poe's Law. Rule 34. (NSFW) The Iron Law of Oligarchy. But one of my favorite, as an important lesson about How the World Works, is the 10000 Hour Rule.

I would describe this law thusly:

To master any non-trivial field requires 10000 hours of dedicated practice and study.

Sometimes, this law is stated with "10 years" in the place of "10000 hours." Or, as an old saying elegantly puts it, "You have to write a million bad words before you can write a good one."

Drawing. Writing. Chess. Singing. Tennis. Designing Games. Acting. Golf. Playing the violin. Leatherworking. Poker. In each case, mastery requires work. A lot of it.

What does this law mean? It means that fantasy you have, about picking up a guitar and finding that you have a deep, innate talent for playing and that you're the next Hendrix? It's just a fantasy. Better get practicing, pal. You got 10 long years of work ahead of you. Free lunch? No such thing.

I Will Now Deal With Your Objections

People don't like this law. People hate to be told that they can't have Free Stuff, and gaining mastery without sacrifice is the epitomy of Free Stuff. But there are several obvious objections people come up with to rebut this law. I will dispose of them now.

It Doesn't Take That Long To Master Something! I Can Master Tic-Tac-Toe in a Minute!

That's why I said it takes 10000 hours to master something non-trivial. Obviously, some things are easy, but nobody cares about whether you can do them. Learning to tie your shoes is much simpler than learning to play the violin, but nobody will pay you to watch you tie your shoes.

What About Child Prodigies? Mozart Was Composing Symphonies When He was Four!

Yeah, but nobody wants to hear them. They want to hear what he wrote later. Many, many hours of work later.

Child prodigies exist, and they can do amazing things. However, the main advantage of being a child prodigy is that you get to start putting in your 10000 hours at an early age. You still have to work for it.

But Some People Have Amazing Innate Abilities!

People need to believe that they can possibly have the innate ability to do amazing things. Some sort of magical penumbra that gives you the supernatural ability to write or play baseball or whatever. Not really.

Now don't get me wrong. Some people do have the innate ability to excel at a field. It is the opportunity, resources, and drive to put in the many, long tedious hours mastering a chosen field. It's the ability (the time, money, and energy) to sit down and work. That's the only innate ability that really means something.

So I Just Have To Spend 10000 Hours On Something and I Become Awesome?

No. It has to be 10000 of meaningful practice. Learning new things. Stretching your ability. Occasionally failing, learning from your mistakes, and improving. Repeating the same lame thing for one hour 10000 times will not cut it.

I've Hardly Spent Any Time At All Learning To Do [X], and I'm Amazing At It!

Are you sure? One of the main reasons people are mediocre at a profession or activity is that they lack the ability to recognize when they have done it poorly. One of the main things successful craftspeople and artists have in common is a loathing of their own work.

I'm not saying you aren't that great. Hey, I've never met you. But are you sure?

It's an Unpleasant Rule

The 10000 Hour Rule is about crushing dreams. It's about understanding that there are limits to what you can do in the all-too-short period of time we spend on this Earth. It's about giving people who have achieved mastery the respect they deserve. It's about, before taking on a new task, honestly evaluating whether we can afford to give what it takes to complete it. And it's about forgiving yourself for not being able to play the guitar like Hendrix.

I have a lot more to say on this subject and how it applies to writing computer games. Next time.

(If you're interested in reading more about this stuff, I've heard that Outliers, by Malcolm Gladwell, is a good read. I'm not so much a big Gladwell fan, but it's a very interesting topic.)

Sunday, October 16, 2011

Setup ssh user equivalence for all RAC nodes


This Example Done on Two Node SSH Setup:
Log on as user ORACLE on nodes. Do not give pass phrase to any of the methods below.

ON NODE 1

Step 1

[root@rac1pub ~]# su - oracle

Step 2

[oracle@rac1pub ~]$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/oracle/.ssh/id_dsa):
Created directory '/home/oracle/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/oracle/.ssh/id_dsa.
Your public key has been saved in /home/oracle/.ssh/id_dsa.pub.
The key fingerprint is:
b1:24:2f:8f:5a:27:4b:ce:aa:09:ce:13:bd:d8:b1:3e
oracle@rac1pub.kasb.com

Step 3

[oracle@rac1pub ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/oracle/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/oracle/.ssh/id_rsa.
Your public key has been saved in /home/oracle/.ssh/id_rsa.pub.
The key fingerprint is:
68:0f:79:13:76:a7:a1:2e:ce:6e:1f:a6:2b:1a:15:f3
oracle@rac1pub.kasb.com

Step 4

[oracle@rac1pub ~]$ cd /home/oracle/.ssh

Step 5

[oracle@rac1pub .ssh]$ ls -ltr
total 16
-rw-r--r--  1 oracle oinstall 613 Oct 16 23:06 id_dsa.pub
-rw-------  1 oracle oinstall 668 Oct 16 23:06 id_dsa
-rw-r--r--  1 oracle oinstall 233 Oct 16 23:06 id_rsa.pub
-rw-------  1 oracle oinstall 883 Oct 16 23:06 id_rsa


Step 6

Note: The file name should be (authorized_keys) their is no chance for spelling mistake.
otherwise your ssh will be failed.


[oracle@rac1pub .ssh]$ cat id_dsa.pub >> authorized_keys

[oracle@rac1pub .ssh]$ cat id_rsa.pub >> authorized_keys

Step 7
Now we are sending node1 ssh keys to node 2 in temporary file because to get this keys in authorized_keys file of Node 2
 
[oracle@rac1pub .ssh]$ scp authorized_keys  rac2pub:/tmp/rac1keys.tmp


The authenticity of host 'storage (192.168.1.192)' can't be established.
RSA key fingerprint is 97:b4:a8:13:a1:76:57:44:e2:0b:60:c1:b8:13:db:27.
Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added 'rac2pub,192.168.1.192' (RSA) to the list of known hosts.oracle@rac2pub's password:
authorized_keys                                                   100% 1692     1.7KB/s   00:00
 

ON NODE 2
Now you can verfiy on Rac node 2 in tmp directory file(rac1keys.tmp) should be their

[oracle@rac2pub ~]$ cd /tmp
[oracle@rac2pub tmp]$ ls -ltr
-rw-r--r--  1 oracle oinstall  1692 Oct 17 00:06 rac1keys.tmp

STEPS ON NODE 2==================================================
Note: Follow same Steps(1 to 6) done on RAC node 1


Step 7
Now we are sending node2 ssh keys to node 1 in temporary file because to get this keys in authorized_keys file of Node 1


[oracle@rac2pub .ssh]$ scp authorized_keys  rac1pub:/tmp/rac2keys.tmp
The authenticity of host 'storage (192.168.1.191)' can't be established.
RSA key fingerprint is 97:b4:a8:13:a1:76:57:44:e2:0b:60:c1:b8:13:db:27.
Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added 'rac1pub,192.168.1.191' (RSA) to the list of known hosts.oracle@rac1pub's password:
authorized_keys                                                   100% 1692     1.7KB/s   00:00



ON NODE 1
Now you can verfiy on Rac node 1 in tmp directory file (rac2keys.tmp) should be their
[oracle@rac1pub ~]$ cd /tmp
[oracle@rac1pub tmp]$ ls -ltr
-rw-r--r--  1 oracle oinstall  1692 Oct 17 00:15 rac2keys.tmp


After you have done all above now proceed further

ON NODE 1

[oracle@rac1pub ~]$ cd /home/oracle/.ssh/
[oracle@rac1pub .ssh]$ cat /tmp/rac2keys.tmp >> authorized_keys

ON NODE 2


[oracle@rac2pub ~]$ cd /home/oracle/.ssh/
[oracle@rac2pub .ssh]$ cat /tmp/rac1keys.tmp >> authorized_keys


Gather ssh fingerprints of all RAC nodes.
Now get fingerprints of all possible interfaces / nodes of this RAC setup using ssh.

NOTE: You need to exit after each successful logon to avoid confusion.

On Node 1:
ssh rac1pub.kasb.com
ssh rac1pvt.kasb.com
ssh rac2pub.kasb.com
ssh rac2pvt.kasb.com
ssh rac1pub
ssh rac1pvt
ssh rac2pub
ssh rac2pvt


On Node 2:
ssh rac1pub.kasb.com
ssh rac2pub.kasb.com
ssh rac1pvt.kasb.com
ssh rac2pvt.kasb.com
ssh rac1pub
ssh rac1pvt
ssh rac2pub
ssh rac2pvt


I put two practical example for your more uderstanding that what I am trying to say in Gather ssh fingerprints of all RAC nodes.
============================================================================
[oracle@rac1pub .ssh]$ ssh rac1pub.kasb.com

The authenticity of host 'rac1pub.kasb.com (192.168.1.191)' can't be established.
RSA key fingerprint is 97:b4:a8:13:a1:76:57:44:e2:0b:60:c1:b8:13:db:27.
Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added 'rac1pub.kasb.com,192.168.1.191' (RSA) to the list of known hosts.
[oracle@rac1pub ~]$ exit
logout
Connection to rac1pub.kasb.com closed.

[oracle@rac1pub .ssh]$ ssh rac1pvt.kasb.com
The authenticity of host 'rac1pvt.kasb.com (10.0.0.1)' can't be established.
RSA key fingerprint is 97:b4:a8:13:a1:76:57:44:e2:0b:60:c1:b8:13:db:27.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'rac1pvt.kasb.com,10.0.0.1' (RSA) to the list of known hosts.
Last login: Sun Oct 16 23:34:57 2011 from rac1pub.kasb.com

[oracle@rac1pub ~]$ exit
logout

=================================================================================

Verfication of Passwordless SSH connection
Here your passwordless connection established between both node

ON NODE 1You can see date of both nodes will be appeared without password prompt

[oracle@rac1pub ~]$ ssh rac1pub date
Sun Oct 16 23:41:29 PKT 2011
[oracle@rac1pub ~]$ ssh rac2pub date
Sun Oct 16 23:41:03 PKT 2011
[oracle@rac1pub ~]$ ssh rac2pvt date
Sun Oct 16 23:41:20 PKT 2011

ON Node 2
[oracle@rac2pub ~]$ ssh rac1pub date
Sun Oct 16 23:42:18 PKT 2011
[oracle@rac2pub ~]$ ssh rac1pvt date
Sun Oct 16 23:42:26 PKT 2011
[oracle@rac2pub ~]$ ssh rac2pvt date
Sun Oct 16 23:41:59 PKT 2011
[oracle@rac2pub ~]$ ssh rac2pub date
Sun Oct 16 23:42:06 PKT 2011
[oracle@rac2pub ~]$ ssh 192.168.1.191 date
Sun Oct 16 23:42:52 PKT 2011


Congratualtion your SSH have now Configured for Oracle Rac 10g.




Monday, October 10, 2011

LODING DATA INTO CLOB COLUMN BY SQL LOADER


THIS EXAMPLE IS FOR LODING DATA INTO CLOB COLUMN BY SQL LOADER

STEP 1  Creating lob table;
================
SQL> conn scott/tiger
Connected.

SQL>
SQL> create table test_clob(
    id number,
    ename varchar2(10),
    resume clob);


STEP 2 first create 2 text file on OS and enter some data in it.
==============================

[oracle@ocs sqlloader]$ vi resume1.txt
name Syed faraz Ahmed
fname Ahmed
dob 01 Jan 1975

======save file =======


[oracle@ocs sqlloader]$ vi resume2.txt

name zaman ahmed tatari
fname naseem ahmed tatari
dob 29 mar 1980

======save file =======

STEP 3 NOW CREATE CSV data file
==============================

[oracle@ocs sqlloader]$ vi emp.csv

1,zaman,resume1.txt
2,faraz,/home/oracle/sqlloader/resume2.txt

===========save file ================
STEP 4 Now create Control file
===============================

[oracle@ocs sqlloader]$ vi testclob.ctl

load data
infile '/home/oracle/sqlloader/emp.csv'
into table test_clob
replace
fields terminated by ',' trailing nullcols
(
id integer external(3),
 ename char(10),
 clob_file_loc filler char(100),
 resume lobfile(clob_file_loc) terminated by EOF
)
===========save file ================

NOTE: In above control if you use Blob column the syntax is same.
clob_file_loc filler char(100) this filler column in sqlloader file which will not load into the table. This is use to get filename that use in next line.

STEP 5 Load data by  SQL Loader
==============================
sqlldr scott/tiger control=testclob.ctl


STEP 6 Verify  Load data by sqlplus
=================================
SQL> conn scott/tiger
Connected.
SQL>
SQL>
SQL> select * from test_clob;

        ID ENAME
---------- ----------
RESUME
------------------------------------------------------------------------------
         1 zaman
name Syed faraz Ahmed
fname Ahmed
dob 01 Jan 1975

         2 faraz
name zaman ahmed tatari
fname naseem ahmed tatari
dob 29 mar 1980





Saturday, October 8, 2011

Understanding on Fine Grained Auditing (FGA)

 Follow the steps for understanding on FGA.

LOGIN FORM SYS or SYSTEM USER to sqlplus

Creating Fine Grained Auditing Policy on Scott emp table
begin
dbms_fga.add_policy(
object_schema=>'SCOTT',
object_name=>'EMP',
policy_name=>'EMP_SAL_CHK'
 ,audit_condition=>'deptno=10'
 ,audit_column => 'sal'
 ,enable=>TRUE
 ,Statement_types=>'SELECT,INSERT,UPDATE,DELETE'
);
end;


RUN BELOW QUERIES ONE BY ONE TO GENERATE AUDIT
run queries from scott user

QUERY NO 1
SQL> select * from emp where deptno=10;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM
---------- ---------- --------- ---------- --------- ---------- ----------
      7782 CLARK      MANAGER         7839 09-JUN-81       2450
      7839 KING       PRESIDENT            17-NOV-81       5000
      7934 MILLER     CLERK           7782 23-JAN-82       1300

QUERY NO 2
SQL> select * from emp where sal>6000;

no rows selected

QUERY NO 3
SQL> select * from emp where sal=950;

     EMPNO ENAME      JOB              MGR HIREDATE         SAL       COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- --------------------
      7900 JAMES      CLERK           7698 03-DEC-81        950                30

QUERY NO 4
SQL> select ename , job from emp where empno=7839;

ENAME      JOB
---------- ---------
KING       PRESIDENT

QUERY NO 5
SQL>  select ename , job, sal from emp where empno=7839;

ENAME      JOB              SAL
---------- --------- ----------
KING       PRESIDENT       5000

QUERY NO 6

SQL> select ename , job, deptno from emp where empno=7839;

ENAME      JOB           DEPTNO
---------- --------- ----------
KING       PRESIDENT         10

QUERY NO 7

SQL> select ename , job, deptno from emp where deptno=10;

ENAME      JOB           DEPTNO
---------- --------- ----------
CLARK      MANAGER           10
KING       PRESIDENT         10
MILLER     CLERK             10

QUERY NO 8

SQL> select ename , job, sal from  emp where deptno=20;

ENAME      JOB              SAL
---------- --------- ----------
SMITH      CLERK            800
JONES      MANAGER         2975
SCOTT      ANALYST         3000
ADAMS      CLERK           1100
FORD       ANALYST         3000

QUERY NO 9

SQL>  select ename , job, sal from  emp where deptno=10;

ENAME      JOB              SAL
---------- --------- ----------
CLARK      MANAGER         2450
KING       PRESIDENT       5000
MILLER     CLERK           1300


FROM SYS USER CHECK AUDIT QUERIES

SQL> select sql_text from dba_fga_audit_trail;
or
SQL> select lsqltext from fga_log$;

LSQLTEXT
---------------------------------
select * from emp where deptno=10
select ename , job, sal from emp where empno=7839
select ename , job, sal from  emp where deptno=10


REASONyou can see that ONLY QUERY no  1,5,9  were Audited.
WHY
The above 1,5,9 queries only audited because we selected sal column and
result of rows belongs to deptno 10.

additionally, if you avoid sal column in select list  & select any column of emp table and records still belongs to deptno 10 than audit will not be genrated because your audit column is SAL in policy. So both condition must be matched.

One more understanding
audit_condition=>'deptno=10'
above parameter in audit policy doesn't mean that this value present in where clause. it means that audit generate when SAL column selected its records belongs to department no 10.



Dropping  Policy

from sys or system user

begin
dbms_fga.drop_policy(
object_schema=>'SCOTT',
object_name=>'EMP',
policy_name=>'EMP_SAL_CHK'
);
end;







 

Secure Application Role

Step 1: Create separate security user

SQL> create user sec identified by sec;

User created.

SQL> grant dba to sec;

Step 2: Connect with security user and developed these objects

1. package and package body
2. create role
3. add some objects privileges to above role
4. grant execute privileges on package to a user.


sql> conn sec/sec

create or replace package sec_role_pkg authid current_user is
  procedure sec_role;
end ;

 create or replace package body sec_role_pkg is
 procedure sec_role is
 begin
  if sys_context('userenv','session_user')='HR' then
   dbms_session.set_role('MYROLE');
   end if;
  end ;
 end ;


SQL> create role Myrole identified using sec_role_pkg;

Role created.

SQL> grant select on scott.emp to myrole;

Grant succeeded.

SQL> grant execute on sec.sec_role_pkg to hr;

Grant succeeded.

Step 3: Now Connect form HR user to test role

SQL> conn hr/hr
Connected.
SQL> select * from session_roles;

ROLE
------------------------------
RESOURCE
DBA
SELECT_CATALOG_ROLE
HS_ADMIN_ROLE
EXECUTE_CATALOG_ROLE
DELETE_CATALOG_ROLE
EXP_FULL_DATABASE
IMP_FULL_DATABASE
GATHER_SYSTEM_STATISTICS
SCHEDULER_ADMIN
WM_ADMIN_ROLE

ROLE
------------------------------
JAVA_ADMIN
JAVA_DEPLOY
XDBADMIN
XDBWEBSERVICES
OLAP_DBA

16 rows selected.

Execute the procedure and check what role now HR have

SQL> exec sec.sec_role_pkg.sec_role;

PL/SQL procedure successfully completed.

SQL>  select * from session_roles;

ROLE
------------------------------
MYROLE

SQL>

SOME TEST OF HR USER
SQL> create table test1 (id number);
create table test1 (id number)
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL> select count(*) from scott.emp;

  COUNT(*)
----------
        17


SQL> select count(*) from employees;

  COUNT(*)
----------
       107


SQL> select count(*) from scott.dept;
select count(*) from scott.dept
                           *
ERROR at line 1:
ORA-00942: table or view does not exist

Tuesday, October 4, 2011

Why All Our Games Are Now Cheaper Forever


Spiderweb Software just started our annual sale. It's ten percent off everything we sell for the whole month of October. That isn't really news. We do this every year, and people seem to like it.

But this year, there is much more. We permanently lowered the prices of everything we sell. At least 20% cheaper (in addition to the 10% for the sale). For some products, much more. The most expensive game we sell is now $20, and that is likely to last pretty much forever.

It's a big mental shift for us, and I thought it was worth blogging about. I write about game pricing on this blog a lot, and I'm not ashamed of it. Right now, most of the huge revolutions in the game biz are in the new crazy pricing models, and there are still a lot of questions out there about the most efficient way to make a game make money.

Why It Took So Long To Lower Our Prices

We released our first game in January, 1995. That is a long time ago, and much has changed. A few helpful comparisons.

Now: Huge distributors like Steam and iTunes sell massive numbers of copies for low prices, and Indie developers make good money on huge volume.
Then: The World Wide Web barely existed and we scraped by on a handful of sales from AOL.

Now: A quality Indie niche game sells on big portals for ten bucks at most. More than that and people think you're crazy and move on.
Then: Most good shareware games sold for $25. It took me a very long time just to realize that that price isn't normal anymore.

Now: Indie developers can make excellent livings selling lots of copies of cheap games.
Then: Indie game developers were called "shareware developers," and everyone thought they were losers and spat on them.

Now: Want to pirate a game? It just takes 3 seconds of searching on Pirate Bay.
Then: Took five minutes of searching instead of three seconds. This actually made a big difference.

Now: Many new games are given away for free and make their money on micro-transactions from a portion of their users.
Then: FREE games? With micro-WHAT? What are you? A SORCEROR?

(The shift to free games is arguably the most stunning development in the games biz in a very long time. My prediction: Within five years, there will be a successful game that pays you a small amount to play it and makes their cash selling better swords or whatever.)

I'm a dumb person in plenty of key ways, so it took me a while to observe the key fact:

A LOT of money is being made by selling games for cheap.

So now , instead of selling our games for $25 or $28 (!!!), we'll sell them for $20 or $15. I know this still seems like a lot, but I haven't backed off on the key thing I've long said ...

People Who Write Niche Games Can't Charge a Dollar

If you're making a pretty, shiny, highly casual game with cartoon squirrels and you think you can find a million fans for it, go ahead. Charge a dollar. You'll have to.

But if you write games like mine? Low budget, old school, hardcore RPGs with lots of content? If I charged a dollar for it, I'd have to sell a copy to pretty much every interested human everywhere to have a chance of making money.

So I still charge an actual price, an amount of money that still feels like money. Maybe I should have taken everything down to $15. Maybe I'm being too timid in the price drop. But, in a sense, that difference doesn't matter.

There are two sorts of prices you can pay for a game: An amount that is so small you don't care, and an amount high enough that you do. Our newest game, Avadon: The Black Fortress, is $20 on our site and $10 on Steam. That's a big difference, but, in a very real sense, they have the same price: an amount of money that actually feels like spending money. We will always charge actual money, as opposed to pocket change. All I have done is slightly tinkered with the level.

Bonus Point: Why Is Our Game Twice the Price On Our Site Than On Steam

I get asked this a lot, and it's a fair question. The answer:

In any place where your game is sold, pick the price that will maximize the profits. This ideal price changes depending on the nature of the place where it is being sold.

Steam is a big, sprawling gaming bazaar where practically all of the games are cheap. People see a game, spend a moderate amount of money on it, and try it out. People experiment there, and you need to charge a price that encourages customers to pick you as their experiment. Also, if you charge $20 for your game there, it will be on a list with ten good games at half the price, so you will get murdered.

Spiderweb Software's web site, on the other hand, only lists our games. It is generally only visited by fans of role-playing games. People on our site are generally really interested in the specific sorts of games we sell, and so the higher price doesn't scare them off.

This sort of logic isn't my weird invention. It's basic business. World of Goo is $20 on the company site, $10 on Steam, and $5 on iTunes. Each marketplace has its own norms, and you price your game to maximize your earnings there.

And that is why games are now at most $20 on our site. Because of the current standards of the game industry as a whole, I think that will most likely increase our earnings overall. It might not always have been that way, but I feel it is now.

(And, yes. I set game prices to maximize my earnings. Of course I do. Astonishingly, some people seem to take offense at this. I don't care. I'm not going to neglect to send my kids to college just so I can satisfy someone's arbitrary standards of Indie cred. I'm too old for that, and children persist in their irritating need to eat food.)

So. Anyway. A Sale.

Our games are cheaper forever, and even cheaper than that this month. We're getting a lot more sales, and I don't feel like the dumb jerk that still charges $28 for three year old games anymore. If you like old school role-playing games, you could certainly do worse.

And it will be a while before I write about pricing again. Believe it or not, I have other things to say (and make fun of). Time to get going on that ...