Spring RTS Engineering Internals

Published May 16, 2013 by Issam Lahlali, posted by IssamLahlali
Do you see issues with this article? Let us know.
Advertisement

Spring is a free open source RTS game engine originally created by Stefan Johansson and Robin Westberg, members of the Swedish Yankspankers game clan. Originally intended to bring the gameplay experience of Total Annihilation into three dimensions, the engine has since evolved to support a plethora of more modern and more flexible features, including built-in high-level extensibility through a Lua scripting interface. Any developer who wants to contribute to Spring RTS or any other open source project needs to understand the existing code, and for that he can read the documentation, explore forums, read comments from code or read the code itself. Another way is to use tools and query the code base. Like SQL for a relational database, CppDepend provides the CQLinq language to request the code and help to understand C/C++ within. It's also free for open source projects. In this article we will analyze Spring RTS with CppDepend to discover some internal design choices.

Paradigm used

C++ is not just an object-oriented language. As Bjarne Stroustrup points out, "C++ is a multi-paradigmed language." It supports many different styles of programs, or paradigms, and object-oriented programming is only one of these. Some of the others are procedural programming and generic programming.

Procedural Paradigm

Global functions Let's search for all global functions inside the Spring engine: from m in Methods where m.IsGlobal && !m.IsThirdParty select m

spring22.png

5092 functions are global, which represents around 20% of all the Spring methods. But what's interesting is that these functions are grouped by namespaces which makes the code modular and easy to understand. Structures In a procedural program, modules interact by reading and writing a state that is stored in shared data structures. Let's search for all data structures defined by Spring: from t in Types where t.IsStructure && t.Methods.Count()==0 select t

spring27.png

Static functions In general, it's better to declare a function as static unless you have a specific need to call it from another source file. from m in Methods where m.IsGlobal && m.IsStatic

spring26.png

Functions candidate to be static from m in Methods where m.IsGlobal && !m.IsStatic && !m.IsThirdParty && m.MethodsCallingMe.Where(a=>a.SourceDecls.FirstOrDefault()!=null && a.SourceDecls.FirstOrDefault().SourceFile.FilePathString!=m.SourceDecls.FirstOrDefault().SourceFile.FilePathString).Count()==0 select m

spring29.png

Object Oriented paradigm

The object-oriented paradigm makes extensive use of virtual functions and polymorphism, let's search for virtual methods. from m in Methods where m.IsVirtual select m

spring2.png

And to have a better idea of existing virtual methods, we can use the Metric View. In the Metric View, the code base is represented through a Treemap. Treemapping is a method for displaying tree-structured data by using nested rectangles. The tree structure used in a CppDepend treemap is the usual code hierarchy:

  • Projects contains namespaces.
  • Namespaces contains types.
  • Types contain methods and fields.

The treemap view provides a useful way to represent the result of a CQLinq request, so we can visually see the types concerned by the request.

spring3.png

As we can observe, virtual methods are not widely used. Abstract classes Low coupling is desirable because a change in one area of an application will require less changes throughout the entire application. In the long run, this could alleviate a lot of time, effort, and cost associated with modifying and adding new features to an application. Using abstract classes can improve the low coupling; let's search for all abstract classes: from t in Types where t.IsAbstract select t

spring4.png

Inheritence In object-oriented programming (OOP), inheritance is a way to establish Is-a relationship between objects. It is often confused as a way to reuse the existing code which is not a good practice because inheritance for implementation reuse leads to Tight Coupling. Re-usability of code is achieved through composition (Composition over inheritance). Let's search for all classes having at least one base class: from t in Types where t.BaseClasses.Count()>0 select t The Metric view shows us that many classes are concerned, and inheritance is mainly used.

spring16.png

Generic Programming C++ provides unique abilities to express the ideas of Generic Programming through templates. Templates provide a form of parametric polymorphism that allows the expression of generic algorithms and data structures. The instantiation mechanism of C++ templates insures that when a generic algorithm or data structure is used, a fully-optimized and specialized version will be created and tailored for that particular use, allowing generic algorithms to be as efficient as their non-generic counterparts. In the C++ world two schools are very popular: Object Oriented Programming and Generic Programming, each approach has their advocates, this article explains the tension between them. Let's search if Spring uses templates: from t in Types where t.IsGeneric && !t.IsThirdParty select t

spring6.png

Spring RTS uses template classes, especially for containers and utility classes. We can also search for generic methods: from m in Methods where m.IsGeneric && !m.IsThirdParty select m

spring7.png

Only some methods are templated.

Spring RTS startup

To understand what happens when the RTS engine starts, let's begin with the dependency graph concerning some methods invoked by the entry point WinMain.

spring9.png

When the engine starts it searches for the existing cores to exploit all the processing machine power, and after that delegates the initialization to the SpringApp class. Let's search what happens when the Initialize method is invoked. from m in Methods where m.IsUsedBy ("SpringApp.Initialize()") && !m.IsThirdParty select new { m }

spring10.png

This method initializes all the other classes with which it collaborates like LuaOpenGL, CMyMath and CGlobalRendering, and after it invokes the startup method. And here's the dependency graph of the startup method.

spring11.png

External frameworks used

When developing a product, it's preferable to not reinvent the wheel and reuse proven and mature frameworks. Let's search for external types used by Spring. from t in Types where t.IsUsedBy ("rts") select new { t }

spring25.png

Spring RTS uses mainly boost, this library is aimed at a wide range of C++ users and application domains. They range from general-purpose libraries like the smart pointer library, to operating system abstractions like Boost FileSystem, to libraries primarily aimed at other library developers and advanced C++ users, like the template metaprogramming (MPL) and domain-specific language (DSL) creation (Proto). For example concerning network communication Spring uses the boost.asio library, which provides developers with a consistent asynchronous model using a modern C++ approach.

Conclusion

For any developer using the Spring RTS, or any new code base, it's better to know sometimes how it works internally, for which the better approach is to go inside the code source. In this case we have found that the Spring RTS code is very simple to understand and evolve.

Cancel Save
0 Likes 4 Comments

Comments

Ben Bowen

Over engineered.

May 24, 2013 06:17 PM
phil_t

I would suggest the title of the article be changed, since this seems to be about CppDepend and not Spring RTS.

May 25, 2013 03:03 AM
Dave Hunt

I would suggest the title of the article be changed, since this seems to be about CppDepend and not Spring RTS.

This.

May 25, 2013 04:14 AM
Kaptein

though im happy to see Spring RTS just being mentioned, huge respect to them!

May 26, 2013 11:43 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Uses an open source RTS engine to demonstrate how the CppDepend tool can be used to break down and understand a game's code design

Advertisement
Advertisement