Jump to content

  • Log In with Google      Sign In   
  • Create Account






Epoch R10 Scheming and Plotting

Posted by ApochPiQ, 31 March 2010 · 124 views

Gather round, kiddies, it's time for more tales of Epoch!


In my last little blurb here I mentioned I'm rewriting the entire project, more or less from scratch. The vast bulk of existing logic has been scrapped, and the architecture is changing very heavily.

Before I dig too deep into the details of the architectural shift, I'd like to clarify the actual intention of the Release 10 rewrite, as it wasn't really all that clear in the last post.


From a conceptual point of view, the R10 plan is to transform Epoch into a meta-programming language. In fact, the core Epoch tools will not actually specify a language. Rather, they will specify a set of meta-programming rules which can be used to construct a language. The Epoch standard library will contain a definition of the Epoch language itself, which will permit easy reflection on Epoch code and some other nifty tricks.

It is important to note that I'm not trying to build a family of languages here. The use of Epoch's meta-programming facilities to define the language itself is mostly for the purpose of consistency: I want user-defined meta-programming entities to behave just like the first-class entities that define the Epoch language itself. In other words, meta-programming code that you write (as an Epoch programmer) will have the exact same power and flexibility as meta-programming code I write (as the language implementer).

The motivation for this shift is largely based on opinions I gathered at GDC'10. A significant number of programmers are looking for a tool that they can use both for their low-level engine/systems programming work and for higher level things like gameplay scripting and so on. Right now this is typically handled by blending multiple languages, but that can become unwieldy very quickly for complicated systems. There are many benefits to working in a single, uniform language from top to bottom; the goal of the R10 restructuring is to deliver a language that can promise those benefits with no loss in flexibility, expressibility, and power.

In a nutshell, the idea here is that software can be built by defining increasingly abstract "layers" of domain-specific languages. This approach delivers a huge amount of control over the nature of each layer. In particular, in Epoch, each layer can sport its own user-defined syntactic extensions, and actually restrict access to other layers via a system I'm personally referring to as "access control lists." Each layer can be defined as a self-contained unit, with well-defined and strictly enforced interface ties to other units in the system.

This encourages rigorous decoupling of unrelated modules, which is obviously a good thing. The other promising aspect of developing in this style is that each module's programming language can be carefully tailored for its target audience. Don't want your AI scripters being able to mess with the graphics engine? Define your AI module behind a closed interface, and use ACLs to block off the graphics modules. Need something that mission/quest implementers can use to write really terse specifications for a mission? Define a set of syntax extensions, so that the code physically looks like the kind of specification needed.

Additionally, this style of development can be used to do some serious blurring of the line between code and data. If you really wanted to, you could do things like define syntax that creates a 3D polygon mesh; write your exporter so that it takes an artist's model and converts it directly into the given blob of code. Executing the model "data" can then actually load the model into memory and prepare it for rendering directly in the engine.

Unlike developing with multiple languages, using meta-programming to define embedded "mini-languages" like this enables you to use a single toolchain for the entire development process. Having a problem with your embedded scripting language? Use the existing Epoch IDE and debugger tools to trace it down. Getting bugs from a highly abstract piece of code that you can't quite track? Use the reflective capabilities of the language/tools and expand that code into exactly what it looks like to the compiler, and debug the expanded version.

To summarize, Epoch is intended to make a major change in the way that we can work on complex software systems. The real bonus, though, is that there's no rule mandating that you have to write software that way in Epoch. You can learn the syntax in a day, tie into existing C/C++ code trivially, interface with basically any C-style APIs available, and code in any mixture of procedural, object-oriented, or functional styles. You won't pay for features you don't use, which means the transition to Epoch should be smooth and comfortable for anyone with the (minimal) requisite programming skills. Best of all, all the powerful goodies of the language will be waiting right under the surface, ready for when you become comfortable enough with the language to start exploiting them.




Well, it's good to see that a lot of the concepts I wanted to achieve with Tangent have high interest. [grin]
Could you please explain a little bit about how this differs from providing interfaces in C++ to block off a given subsection of functionality? I'm no language expert by any stretch, but if you want a particular API for the AI people to use, then why not define a proxy interface that only provides the information that you want them to see?

Of course this wouldn't allow syntax modification, but I don't know that I would want to modify the syntax to something new within the same project... Perhaps there are some situations that would benefit from this that I'm not thinking of???

In any case, this sounds like a very interesting project, and I look forward to seeing what you come up with!
Proxy interfaces become a massive maintenance issue in any nontrivial code base. For instance, say we use a component-based, data-driven model to simulate the game world. We need each subsystem to access certain bits of data from a given component tree in the game graph; AI, general game logic, rendering, audio, possibly user input, and so on. It's easily conceivable that all those subsystems would want to know, say, the hit-points of the creature in question. If I have to define an adapter for each subsystem, the instant the core component model is tweaked, I suddenly have a dozen places that have to be updated. This amounts to massive code duplication, and it's disturbingly easy to forget all the places that have to be adjusted when anything changes. It's also begging for bugs.

Now, consider the alternative: I define a Creature class which is the base component for all my game critters. Creatures can have various sub-components like Weapons and Armor. Say my game logic wants to access the "defense score" of a Creature wearing some Really Shiny Armor.

There are two ways to accomplish this in C++ - you can either traverse the game-graph manually and find the associated Armor component for the given Creature, or you can define a wrapper function in the Creature interface that retrieves that value for you. Clearly, the latter is the more robust. In fact, we specifically want it to be impossible for someone to use the traversal method, because the alternative is so much safer.

In C++, we're screwed; we can't simply make the component-model functions private or protected, because then other aspects of the game that should be modifying the game-graph won't have access. Sure, we could try to fix things with a massive set of friends, but that only goes so far. Say the save-game system that traverses the game-graph should not be permitted to query a Creature's "defense score" (which seems like a fine restriction to me). I can't hide the GetDefenseScore() interface and expose it via friendship, because as soon as I do that, the friends that need access to GetDefenseScore() can now also access everything else!

Epoch fixes this dilemma with the Access Control List concept. Using an ACL, you can take subsets of a class interface and make them available to other bits of code, on a very fine-grained basis. The solution to the above problem would look something like this (expressed in pseudo-C++ for convenience):

class Creature

{
limited(GameGraphNodeBase):
void FrobnicateTheGameGraph();
void AndSoOn();

limited(GameLogicBase):
int GetDefenseScore() const;
};


Anything derived from GameGraphNodeBase (or "tagged" as such, in Epoch's system) can access the stuff in the first "limited" block, but not the second. Similarly, GameLogicBase code can talk to GetDefenseScore(), but not the game-graph logic itself.

In Epoch, ACLs work based on a tagging system rather than inheritance, so it would be possible to tag a free function that can access a given member, etc.


Hope that clarifies the concept a bit [smile]
Do you think that language/platform-integrated code editors will be important for programming Epoch (think Smalltalk IDE, or Eclipse for Java)?
I don't think so; Smalltalk's environment is cool in its own way but makes it a major stumbling block for deploying traditional applications. Java's platform has succeeded largely due to brute force. Either way, I'm really hoping to get away from the VM architecture as soon as possible. I don't want to have to write a platform - just doing a language is already an insane amount of work. I think it makes much more sense to capitalize on the work of other people's VMs and execution environments. For example, I'm working on changes to the compiler architecture that should make it possible to integrate with GCC's back end, or an LLVM emitter, or even a CLR or Java emitter for that matter. I really don't want to duplicate all that kind of low-level work just to bootstrap a language.

July 2014 »

S M T W T F S
  12345
6789101112
13141516171819
2021222324 25 26
2728293031  

Recent Comments

PARTNERS