Last night I got a craving for mindless FPS. Not a type of game I have many of I'm afraid. I usually just play Mercenaries when I want thing to go 'splode but I had just finished running through it twice the other day, so that wasn't going to cut it.
So I poke through my collection of games and find 'Duke Nukem, Time to Kill', a PS1 game from years back. I had forgotten just how horrible it was. But horrible for what I think are interesting reasons.
Time to Kill was made in the shadow of Doom. Doom was a fairly revolutionary (in the iterative sense) game,.. it had a highly efficient engine, lots of variety, a good pseudo-3d environment, good controls with lots of capabilities without overwhelming the person, and smooth movement.
Time to Kill seemed to try to take this formula expand upon it and bring in things that, I am guessing at the time, conventional wisdom felt would be 'better'. So it added an inventory (which was clunky access and use), step based movement instead of phantom-slidy (which result in coarse grained control that made maneuvering difficult), the ability to put away your gun and do other things like climb (which was more realistic perhaps but added unnecessary and confusing state-fulness), and a more 3D engine that followed Doom's pseudo 'everything on one plane' 3D but added multiple slices (or something) so you could climb around. It felt like a bad comprise.
The end result? An utterly unplayable game that felt like it was designed by a few modders who looked at doom and asked 'wouldn't it be cool if?' but failed to integrate everything into a balanced game.
At least that is my take ^_^
Ultimate solution? I finally bought an xbox360 and a copy of Halo3,.. which I have a different set of complaints about having come from Mercenaries ^_^ but that is a different rant.
Showing posts with label WTF. Show all posts
Showing posts with label WTF. Show all posts
Saturday, April 19, 2008
Friday, April 18, 2008
Moving windows, forks, shares, and ARG!
I just spent the last 3 days working on a problem that SHOULD have been incredibly simple. Take a few windows, slide them to their new positions. The devil was, however, in the details.
Each of these windows is owned by a loadable module. Each module has a binding that translates requests form the loader into commands that the API used by the binding can use. This allows multiple graphics engines to run side by side and opens up all sorts of cross platform capabilities in the future.
Now, each module is loaded by the, well, loader, then forked off with a block of anonymous shared memory between them for message passing. This means that the loader has no way to execute commands directly in the process space of the module. Now comes the tricky part.
Moving a window around. xlib has a simple call for this, move window. The bindings do not expose the Window reference directly though, instead they take a 'move' signal. Each module is runing in it's own space and is thus subject to the scheduling whims of the kernel. So if you do something like:
for(modules module)
module->move(x,y);
you will get a whole mess of modules all moving themselves in the order and timing of the kernel's liking. Unfortunately these are low level graphics modules, which means they do not get the nice automatic refresh that you would get on a desktop. End result? Artifacts as they slide over eachother.
Now, a way to deal with this would be to have each one call the other modules and say 'hey, I drew over you, refresh yourslf' with the problem that they are all running at different speeds and thus no messages can get across quickly enough. The loader can in theory coordinate them but at best it can only suggest when to draw, it can't control the execution dirrectly.
So what I ended up trying to do was using a set of mutexes to let the loader control when each one drew. Essentially:
for(modules module)
module->lock();
for(frames)
{
for(modules module)
module->unlock()
sched_yield()
module->lock()
for(modules module)
module->unlock()
sched_yeild()
module->lock()
}
for(modules module)
module->unlock()
And on the module side:
while(!done)
Lock()
Move(one_frame_x,one_frame_y)
Unlock()
sched_yeild()
Lock()
Redraw()
Unlock()
So each module has it's mutex released in turn, gets to move it'self, then each module in turn gets to draw it'self.
Oh, that should have worked, but didn't. It turns out (and this took forever to track down) under the 2.4 kernel semaphores can't be shared between processes (threads are ok, but forked processes fail) and mutexs just quietly don't work.
Well, you learn something new every day.
Ugh. The things that should be simple.
Each of these windows is owned by a loadable module. Each module has a binding that translates requests form the loader into commands that the API used by the binding can use. This allows multiple graphics engines to run side by side and opens up all sorts of cross platform capabilities in the future.
Now, each module is loaded by the, well, loader, then forked off with a block of anonymous shared memory between them for message passing. This means that the loader has no way to execute commands directly in the process space of the module. Now comes the tricky part.
Moving a window around. xlib has a simple call for this, move window. The bindings do not expose the Window reference directly though, instead they take a 'move' signal. Each module is runing in it's own space and is thus subject to the scheduling whims of the kernel. So if you do something like:
for(modules module)
module->move(x,y);
you will get a whole mess of modules all moving themselves in the order and timing of the kernel's liking. Unfortunately these are low level graphics modules, which means they do not get the nice automatic refresh that you would get on a desktop. End result? Artifacts as they slide over eachother.
Now, a way to deal with this would be to have each one call the other modules and say 'hey, I drew over you, refresh yourslf' with the problem that they are all running at different speeds and thus no messages can get across quickly enough. The loader can in theory coordinate them but at best it can only suggest when to draw, it can't control the execution dirrectly.
So what I ended up trying to do was using a set of mutexes to let the loader control when each one drew. Essentially:
for(modules module)
module->lock();
for(frames)
{
for(modules module)
module->unlock()
sched_yield()
module->lock()
for(modules module)
module->unlock()
sched_yeild()
module->lock()
}
for(modules module)
module->unlock()
And on the module side:
while(!done)
Lock()
Move(one_frame_x,one_frame_y)
Unlock()
sched_yeild()
Lock()
Redraw()
Unlock()
So each module has it's mutex released in turn, gets to move it'self, then each module in turn gets to draw it'self.
Oh, that should have worked, but didn't. It turns out (and this took forever to track down) under the 2.4 kernel semaphores can't be shared between processes (threads are ok, but forked processes fail) and mutexs just quietly don't work.
Well, you learn something new every day.
Ugh. The things that should be simple.
Monday, April 14, 2008
delete(this)
For any given language there are always language features that are considered so 'wrong' that no programmer 'should' use them ever. I've always found this a rather interesting bit of behavior and group think among programmers and it is actually a topic I've used as an interview question in the past.
You can guage someone's perspective on what they think about the 'unholy' language features. If one claims that a methos is always the right solution, or that a feature is always the wrong solution, that tells me something not so great about how flex able in understanding problems someone is... i.e. when presented with a screw do they still try to use a hammer?
Goto is the classic example,.. a dangerous command that comletely breaks OOP if OOP is your hammer. But that isn't the one I wanted to babble about.
Today I realized I was using "delete(this)". It isn't even the first time I've used it in this project. Deleting 'this' is considered one of those no-nos that many say 'well, it just means you structured your program wrong!'... unfortunately people tend to say this without actually knowing the structure, they can simply 'tell' because you came up with the 'wrong ' solution.
In this case, yes, I used it with one particular class. The case? Threaded classes that want to be able to terminate themselves. Nothing else 'owns' the object. Adding a manager (the 'correct' solution) is significant extra complexity, indirection, a bottleneck, and breaks from an event-driven design. The thread class ends up needing to know about the manager, the manager needs to know about the threaded class, you have statefullness to keep in sync, a deeper stack to do anything, and have the potential to introduce all sorts of race conditions. On top of that in order to do it right you need a whole mess of mutexs to make sure that different threaded classes don't screw with the manager's data.
All that to get around 'oh no! delete this is bad!'. And of course, the real problem is going to be the code review...
This also highlights the problem (and advantage) of multi-paradigm languages like c++. You can use any paradigm you like but start mixing them and you run into arguments. On top of that, in order to support so many ways of programming, it implements each of them poorly.
For instance, the delete(this) problem is handled elegantly in ObjC via Retain/Release. I would be surprised if Java didn't have a solution to the situations too. C++? You have to do something 'evil'. Oh well, evil needs luffs too.
You can guage someone's perspective on what they think about the 'unholy' language features. If one claims that a methos is always the right solution, or that a feature is always the wrong solution, that tells me something not so great about how flex able in understanding problems someone is... i.e. when presented with a screw do they still try to use a hammer?
Goto is the classic example,.. a dangerous command that comletely breaks OOP if OOP is your hammer. But that isn't the one I wanted to babble about.
Today I realized I was using "delete(this)". It isn't even the first time I've used it in this project. Deleting 'this' is considered one of those no-nos that many say 'well, it just means you structured your program wrong!'... unfortunately people tend to say this without actually knowing the structure, they can simply 'tell' because you came up with the 'wrong ' solution.
In this case, yes, I used it with one particular class. The case? Threaded classes that want to be able to terminate themselves. Nothing else 'owns' the object. Adding a manager (the 'correct' solution) is significant extra complexity, indirection, a bottleneck, and breaks from an event-driven design. The thread class ends up needing to know about the manager, the manager needs to know about the threaded class, you have statefullness to keep in sync, a deeper stack to do anything, and have the potential to introduce all sorts of race conditions. On top of that in order to do it right you need a whole mess of mutexs to make sure that different threaded classes don't screw with the manager's data.
All that to get around 'oh no! delete this is bad!'. And of course, the real problem is going to be the code review...
This also highlights the problem (and advantage) of multi-paradigm languages like c++. You can use any paradigm you like but start mixing them and you run into arguments. On top of that, in order to support so many ways of programming, it implements each of them poorly.
For instance, the delete(this) problem is handled elegantly in ObjC via Retain/Release. I would be surprised if Java didn't have a solution to the situations too. C++? You have to do something 'evil'. Oh well, evil needs luffs too.
Subscribe to:
Posts (Atom)