Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Leaf

Pages: 1 [2] 3 4
16
Programming / Re: Importance of persistence
« on: August 31, 2012, 08:02:13 AM »
I respectfully submit that I think it can be useful and fun to see how much you can push out of a very limited system, as an exercise if nothing else.

I rather enjoy fiddling with 6502 systems now and then.  I am not hardcore enough for the Atari 2600, though. :P

There are still plenty of 6502s and 8051s with like 8k or less still being used in cheap embedded systems.  It's a whole different programming paradigm, and very interesting if you are into that sort of thing.

17
Programming / Re: Good Settings for Free-Roam RogueLikes
« on: August 31, 2012, 07:14:27 AM »
  Oh good one Legend. Strange western. I dig it.

  I've heard Fantasy/Western ideas tossed around, but have never seen one done.

A Deadlands RL would kick so much butt that there would be no butt left on planet earth.

18
Programming / Re: Resizing Terminal Screen
« on: August 31, 2012, 06:41:01 AM »
And if you are using Java and decide that you want to go ahead and use a full-blown widget toolkit for the UI outside of the map viewport, but don't like the look of the stock Swing look and feels, I have been (slowly) pecking at some (unfinished) extensions to some Swing widgets that let you just provide a bunch of PNG images to skin them, rather than having to putz around with the whole LAF subsystem.

It's....  somewhere in my mess of a svn repository.  I could probably dig it up if you want it, as long as you send me back any improvements so I can use them later. XD

(scaled down from 1024x768, UI skin components from something off of opengameart.org)

19
Programming / Re: Importance of persistence
« on: August 22, 2012, 02:24:16 PM »
That's a tough one!

You might use a procedural algorithm to generate a list of loot locations.  Then you could use a couple of your precious variables as bitfields to indicate which entries in the list are still there.  You'd not be able to let the player drop things, but I don't think that would be a problem.  They pick up a +1 sword, it only replaces what is in their inventory if it's better than what they have?

Yea, I'd do that, and not let them return to previously visited levels as was already discussed.

20
Programming / Re: clean code
« on: August 22, 2012, 06:24:04 AM »
How long did it take to get initial version up and running?

I don't remember exactly.  It took some experimentation.

I started out trying to avoid partitioning the world into areas, and used a global lock on the whole object tree.  Events fired out of a thread pool.  This was of course only marginally better than using a single thread.

Then I tried putting a lock on each object and doing some spin-release-wait locking to acquire locks on objects before the guts of the events ran.  This worked ok-ish, as long as you knew what objects you needed to access beforehand.  If you found that you needed to access another object half-way through processing, you had to add it to the lock list, roll everything back, unlock, spin through all the locks again, and reprocess (rather like backing out of a database transaction).  But it was hideously complicated to write properties for.

That led to the investigation of software transactional memories, but it turned out that none of them that I examined at the time were really of production quality.  The fast ones were really buggy, and the robust ones were really slow.

Somewhere in there I tried to write it on JPA/tomcat, and then GAE, but both of those were too slow.  GAE would have been good enough it they supported persistent TCP connections or http streaming or something at the time, but they didn't (the slowness there was due to HTTP polling; their memcache backed by bigtable makes for lightning fast persistence as long as you're not too write-heavy).  JPA was /horridly/ slow with many-many relationships being mapped to POJOs.  It works pretty good if you avoid many-many relations, but otherwise it brings even the fastest DB to an ignoble grinding halt. :P

After that, I settled on having to partition the world into areas with the realm model, which I didn't want to do, because it doesn't scale as well, but....  Oh well.  I think it took 3 or 4 days to get that model relatively bug-free with the compositional object model and some basic dungeon geography properties and a stat-less @ wandering through halls of #s, but there was a lot of bits of code to be reused from before so it wasn't /all/ from scratch.  It helped that I was using all the maps from Daniel Lawrence's DND for test areas, so that made for a quick relatively large dataset to test with without having to mess with writing random generation stuff.

I think it also helped that I was unemployed and very very bored at the time. :P

21
Programming / Re: clean code
« on: August 18, 2012, 04:48:00 PM »
I also briefly explored using software transactional memories and a threadpool without the distinction between Dungeons, which would have allowed a really huge seamless world to run on multiple cores without having to deal with locking issues.  But they all still seem to be academic things that aren't quite ready to go into production code, yet....

I also tried implementing it all on J2EE with the whole game state living in google bigtable and memcache, but it ended up being just a little too slow to be nice.

22
Programming / Re: clean code
« on: August 18, 2012, 04:38:28 PM »
Alrighty....  I violated the implementation lines of your poem, but (weakly) in my defense, most of the fun was trying to make the game engine highly concurrent and distributed, so that always adds some complexity and indirection, to ensure that threads don't mess with each other's data directly and cause nasty race conditions and concurrent modification exceptions. :P

At the top of the object graph was something I called the "Realm".  The realm contained a bunch of areas, or "Dungeons".  The realm provided methods to look up dungeons by name, returning a reference to a dungeon.  It also provided methods to start, stop, create, and delete dungeons.  When the realm booted, it read a directory hierarchy off disk that contained the game state and used that data to initialize the dungeons back into whatever state they were left in when the realm was last shut down.

Each Dungeon ran in its own thread, and Dungeons could not access each other's object graphs.  The only globally accessable methods provided by the dungeons were methods that they could use to pass Tasks (glorified Runnables) to each other through concurrent queues.  The idea here was to eventually be able to replace all of the shared memory operations above this level with stuff being serialized over TCP sockets, so that a realm could run on a cluster/cloud, or you and your buddies could link your realms together over the internet into one big game world.

Internally, the dungeons maintained a 3d grid of GameObjects in a threadlocal hashmap keyed with 3d integer coordinates.  These toplevel GameObjects represented "cells" on the map.  The dungeon also internally maintained a table of GameObjectData (private), keyed by UUIDs, and all internal GameObject operations looked up data on this table by that key, instead of holding direct references to other GameObjects.  This bit of indirection was to enforce that dungeons could /only/ access game objects contained within themselves.  This made higher level property programming simpler, because the world builder could write their properties without having to worry about narsty concurrent programming issues.

GameObjects provided methods to look up their Parent (the GameObject that contained them) and their Children (the GameObjects that they contained).  They also provided methods to move themselves to a new parent.  A GameObject's parent and children were not directly modifiable; you could only move them to a new parent.  This was to ensure that the object graph never became circular (example: a chest containing a bag, while the bag also contained the chest, which would have caused code that walked up and down the object tree to get stuck in an endless loop).

GameObjects also provided methods to add and remove Properties from them.  So if I wanted to make a wall at coordinate (0,0,0), I'd create a new GameObject, add an Obstacle property, a Graphic property, and maybe a Name("Wall") property to it, and then move the GameObject to (0,0,0).

Early implementations used a global Heartbeat event, but I later removed that as it proved to be too CPU intensive with large worlds.  So Properties could register a Timer event, either repeating or one-shot, and an OnTimer event would be raised to that property at the given interval.  This is how monsters wandered aimlessly around the dungeon waiting for players to run into them.

Some event handlers could return a boolean value that could signal whether the event chain was to be interrupted or not.

For example, when an object was about to move, it would raise an OnMoving event to the set of all objects near it and all objects near its destination.  Any of these OnMoving event handlers could return true, in which case the event chain was canceled and the object did not move.

This was handy for this sort of use-case:  A player is standing next to a wall.  The player pushed a direction key, trying to move into the space occupied by the wall.  The wall gets the player's OnMoving event.  Seeing that the player is trying to occupy the space that the wall is in, the wall prints a message to the area saying, "So-and-so runs into the wall and falls down!".  Then the wall returns true, canceling the event chain and preventing the player from moving.

(Edit: I think the key thing here regarding cleanliness, that event driven programming is really good at is....  Notice that the wall's behavior (blocking movement) is implemented /by the wall/, not by the movement code on the player.  If I go to change how walls work, it's all there in one place.  There is no spaghetti of stuff between the wall and the player movement code and your toaster and the neighbor's dog.)

Once an object had successfully moved, another event, OnMoved was raised to the area.  Monsters used this to notice when other things wandered around the dungeon, so that they could see players coming and decide to either flee or become aggressive.  Players also used this to send screen update packets to their clients.

There were other low-level events that were raised by the engine, things like properties being added or removed and such.  I never used them for much, but the idea was that critters would be able to notice when spells wore off and re-cast them and stuff like that.

Higher level events (such as combat and damage events, speech, etc) were raised by other object properties rather than the low-level engine.

Since dungeons were only allowed to know about objects that they contained, the issue of moving objects between dungeons was handled by the dungeon Tasks mentioned above.  An object (and its children) to be moved to another dungeon was converted into an "object blueprint" and removed from the dungeon's object table.  The blueprint was then packaged up into a Task, which was enqueued to the other dungeon.  The new dungeon would dequeue the Task in its own thread and run it.  The run() method of the task would then rebuild the object (and its children) in the new dungeon from the blueprint.  Since the actual "GameObject" was just a wrapper around a UUID, with some internal GameObjectData methods attached to it, the gameobjects that the properties of newly moved objects referred to would just resolve to nothing in the new dungeon, instead of creating a concurrency nightmare.

There was no broadcast to all dungeons with that paradigm, and objects in different dungeons had to communicate with each other asynchronously with a rather obtuse task passing system.  That worked ok with a convenience layer on top of it, until the objects lost track of which dungeon the other object was in and sent the tasks to the wrong place.  I never did fix this, but thought about implementing some kind of Object ID registry in the realm so that things could look up what dungeon an object with some ID was in.  This would have put a crimp in the massively concurrent idea (though I suppose I could have stored it in some kind of big distributed hashtable), so I kept thinking about it and never got around to fixing it.

So, there it is.  Rather impractical for a game of roguelike scope, but it sure was fun figuring out how to make it work.  Lol.  Not designing it to be highly concurrent would have made it vastly cleaner.  A single-threaded game could have just kept direct references to other objects instead of internally referencing a data object by UUID, and could have done away with the task-passing system.

23
Programming / Re: clean code
« on: August 18, 2012, 03:26:31 PM »
At the risk of being a Java fanboy, I think java can give you the benefits of both.  (Though honestly I prefer C# as a language, but it doesn't run on near as many platforms.)

Mine was designed to be a multiplayer engine with online content creation.

- You can force untrusted Java classes to run in a sandbox, great for user-authored code running server-side.
- You can load those classes from anywhere you want (ie database or "user home directories" in a virtual filesystem, or even over the network) by writing custom classloaders.
- There's a compiler built into the runtime libraries that is easy to use.  No shelling out to the OS and doing other sorts of nonportable stuff to compile properties written through an OLC.  My plan was to have a dedicated compiler thread that all compilation jobs got serialized through, do avoid bogging down the game if everyone was compiling at the same time.  But I didn't get that far.
- Since the OLC stuff is compiled and runs through the hotspot JIT, it's as fast as native code (except for a bit of securitymanager overhead if you are running them in a sandbox).
- Since it is compiled, you get the syntax errors out of the way ahead of time.


I'll see if I can dig up some of my old documentation on the object model.  I am fairly sure it's in svn or backups somewhere. :P

In the meantime, if you're familiar with Inform 6, I kind of stole part of the object model from the Infocom Z-Machine, with some special behavior at the head of the tree to arrange the world into a grid instead of an arbitrary graph of spaces.  So I guess it's more like a 3d grid of trees.

I am feeling incredibly lazy today, so I probably won't be able to find the gumption to dig for docs until I have more coffee in me....  Maybe tonight.

24
Programming / Re: Good Settings for Free-Roam RogueLikes
« on: August 18, 2012, 12:49:59 AM »
Even if I don't explicitly mention it, how can you make a setting where half the world was destroyed by nuclear weapons and not have it come up somewhere, somehow?  Or if the setting is a world where the oceans rose due to all the ice on Earth melting, how do you not bring up global warming?

Jack Vance, Gene Wolfe, and Sterling E. Lanier did it without becoming political.  But their books were far far far post apocalypse.  The world was destroyed.  Nobody remembered why.  It just was.

25
Programming / Re: clean code
« on: August 17, 2012, 05:44:42 PM »
I have it all in a subversion repository going back a few years.  The oldest stuff is on tapes somewhere and I no longer have a tape drive that will read them, but that stuff was all in C/Unix and fugly anyway. :P

I've never publicly released any of it though.  Let me think about it.  What parts in particular are you interested in? :P

Something that I kept going round and round and back and forth about is how to "stack" properties on the object, how/what order to route events to them (first property of type?  All properties of type?), and how to let the higher level game developer (let's call him the "world scripter") easily call a method defined on some property on an object without having to do a bunch of instanceof stuff or check for null values (making the assumption that the "world scripter" may be at a neophyte level of programming experience and needs something that can still be reasonably robust even when they write bad code).  Seems to be a toss-up between doing it "right" and making it "easy" there...

26
Off-topic (Locked) / Re: Good Tileset Size
« on: August 17, 2012, 05:36:19 PM »
I prefer 32x32 myself.

Though you lose so much at 16x16 anyway that it might look just fine if you scaled them down (with a good scaling algorithm) at load time, and let the player scale the tile size any way they wanted.

When I was working on a Up/down/left/right/A/B controls style semi-roguelike for mobile devices, I had it set up with a native 32x32 tile size, but let the player pinch the screen to zoom in and out and see a larger area.  It looked fine.

27
Programming / Re: clean code
« on: August 17, 2012, 01:42:41 AM »
But but but but....

Designing and writing overly-complicated game engines is so fun!  ;D

When it comes to making actual playable games with the engine, I start getting bored and chase down some other shiny thing, lol.....

28
Programming / Re: clean code
« on: August 16, 2012, 10:22:10 PM »
The way I did the event-driven thing for my stuff, the core of the game engine just became a relatively small multithreaded (it was multiplayer, with one "engine" running for each area, with some RPC-type operations to move objects between different areas) timer/turn event dispatching loop and object graph management thing.  Things like the combat system and magic and stuff got implemented in various object properties.  I don't know if one could call it perfectly "clean" or not, but it was all fairly modular and not all tangled together.

The player wasn't any different than a monster, except that the monster AI property was replaced with a property that handled translating player input packets into events generated by the player object, while listening for events and sending screen update packets to the player's client based on the events it heard.  Actions/Skills/Powers available to the players/monsters were implemented as separate properties, either properties on the "creature" itself or on objects in its inventory (ie, if you're wielding a sword, the sword provides the melee attack action), so that the AI could be kind of generic and pick actions/powers out of broad categories (melee attack, ranged attack, AOE, escape, etc) instead of being hardwired with a bunch of different specific actions/powers.

Unfortunately I've never completed the game, because I keep thinking that there is a better way to implement everything and starting over from scratch, after taking breaks in-between.  I started the first one 8 or 10 years ago.  I think the last time was the 12th partial-rewrite from scratch. >_>

So I guess what I am getting at with all that is, maybe there is a trade-off to be made between making it "perfect" and "clean" and actually completing something. <_<

29
Programming / Re: clean code
« on: August 15, 2012, 04:08:18 PM »
I think the cleanest way to implement a game like this would be to use an OO language to build an engine with an event-driven paradigm around various interfaces.  Perhaps with some sort of compositional object model to get rid of the boilerplating problem.
That is quite high level, could you elaborate it a bit?

Define interfaces to "listen" for particular events and "extract" particular data.  Write your objects that inhabit the game world so that they implement those interfaces.  For example, "public class Torch implements Item, Light, HoldableOneHanded".

The Light interface might define method prototypes for getting brightness, radius, etc.

The HoldableOneHanded interface might define no method prototypes, but the game code could check what interfaces an object implements to see where it can be worn/wielded.

The Item interface might just extend a set of other interfaces, such as Gettable, Droppable, Description, Name, Weight, etc.  All of those interfaces would define appropriate method prototypes.

All those would be interfaces for extracting data.  When a player moves, all of the objects that implement Light within the scope of the location that the player has moved to could be queried to calculate what the player can see.  When the player tries to pick up an item, the game engine could look and see if the object implements the Gettable interface before letting the player pick it up.

A monster might implement an event handler interface, such as "OnObjectMoved".  That OnObjectMoved interface might define an "onObjectMoved( Object movingObject )" method prototype, to be called whenever something moves around in the dungeon, be that a player or whatnot.

Then your game engine has an event mechanism, where when, say, an object moves, the game engine collects a set of objects within the scope of the location that the object is moving to.  It loops through them and finds the ones that implement the OnObjectMoved interface and calls the event handler method defined by that interface.  Within each object's event handler method body would be the code to respond to the event.  A monster could "see" the player and move towards him, or attack if close enough.

You could define many different sorts of higher-level event interfaces, like for when an attack happens, damage is dealt, the player gets hungry, etc, etc, etc.  Objects in the game world can define handlers for these events, so that they can respond to them appropriately.

The event-driven paradigm is very powerful, allowing you to cleanly define object behaviors without junking up the core of the game engine with a bunch of stuff that you have to sort through when modifying your code.  It makes maintenance much easier and allows for a great deal of extendability without having to touch the core game engine code.

This all seems fine and good, but about the time you write the same boilerplate code for the 50th monster or the 20th weapon, you're going to be gnashing your teeth!  That's where the compositional object model comes in......

Instead of directly defining a class for every sort of object in the game, we define a "GameObject" that is nothing more than a set of "Properties" (a slightly inaccurate term, given its use in newer languages like C#, but I borrow it from CoffeeMUD).  Then you write a bunch of generic properties that implement the various interfaces and take various initialization values.  "public class StdLight extends Property implements Light".  The constructor for StdLight could take brightness, radius, and duration values, or something.  There could also be various different properties that implement various event handlers to define monster behaviors.  A goblin archer could have standard properties like "RangedCombatant, Cowardly" etc to define their behavior, while a goblin axeman could have properties like "MeleeCombatant, Aggressive" etc.

Then your actual items, monsters, etc in the game become a set of XML files that list a bunch of different properties and their initialization values.  You can make new objects quickly without a bunch of boilerplate code, maybe even in-game with a simple built-in object editor, by just assigning a bunch of properties with initialization values to an empty GameObject.

And since all of these properties are backed by data extraction and event handler interfaces, you can still define special properties to provide special behaviors to very unusual objects, and they just magically work with the game engine.

Since the properties are so reusable and can be extended to create those special behavior properties for special items and monsters, they can also /define their own behaviors/ internally, instead of junking up innards of the game engine with movement code, combat code, etc.

Thus the whole clean abstraction thing that is sometimes so very difficult to preserve in game programming is preserved, and you have a very modular game engine that can be easily customized just by modifying some of the low-level properties that everything else is built on.

The compositional object model is great for generating random monsters and magical items, too.  Your generator just picks some properties off a table and assigns them.  Done.  No weird stuff to mess with deep in the game innards.

I think it may be overkill for a "small" game, but for a "large" game, I think such a design can really help keep the implementation from collapsing under it's own weight, especially if you are doing it all for fun and just designing as you go instead of making up a big ol' design document and sticking to it.

30
Programming / Re: Randomized Object Stats, Randomized Quests?
« on: August 15, 2012, 02:02:21 PM »
There is a tabletop RPG plot generation system called "Mythic GME" that I really, really like.  It tracks rising tension and throws in plot twists and other interesting things.  It's pretty awesome to use with random solo-play stuff like the 1st Edition DMG, Dungeon Bash, etc.  :3

The problem is, it requires a great deal of human intuition to interpret the results.  I haven't been able to figure out how to adapt it to a computer program without the results just turning into a weird mess.  D:

I think the human intuition is the key thing.  The computer just can't provide it.  So with sandbox-style games that offer no random quest system but /do/ offer a high degree of simulation, the player kind of makes up their own plot/quests, using their own intuition, and the game is Fun.  But if you take the same game and add in some kind of technically (in the programming sense) cool but still obviously machine-generated quest/plot to it, the player looks at the quest/plot that isn't really very different from the last 20 quest/plots, finds that it is Lame, and gets bored.

If there was some sneaky way to get the game to underhandedly "read" things from the player's human intuition as play progresses and recycle it back into a plot generator, that would be cool, but I see too much possibility of the results going weird to make it robust. :s

Pages: 1 [2] 3 4