Secretchronicles / TSC

An open source two-dimensional platform game.
https://secretchronicles.org/
GNU General Public License v3.0
204 stars 50 forks source link

Rewriting the game from scratch (at least in large parts) #534

Closed Quintus closed 6 years ago

Quintus commented 8 years ago

Hi everyone,

when working on the SFML port and now on the CEGUI 0.8 port, I have come over numerous problems of the code in terms of

I am sure the list can be continued. I therefore make the suggestion the rewrite large parts of the game from scratch. The code for the enemies can most likely be kept in large places, but everything else is up for new. As we have more or less discussed redoing many parts of the game in our cycle anyway, I think we should be honest and think about whether starting from a blank slate and with a clean architecture is going to give us less work then trying to fit our ideas of good code architecture into the legacy code base we have. For instance, we have discussed:

If all of these will be implemented, this will be a rewrite of most of TSC anyway. So we can also just start from scratch. Of, course graphics, music, etc would be kept! There is no need to remove that!

I do not mean to start this right now. I will complete the CEGUI 0.8 porting, and we will see a number of releases of the 2.x series. I suggest to consider the rewrite for a 3.x series.

A rewrite will allow us to architect the codebase the way we want it to have, without having to worry about how to fit things into this mess we have inherited. We can completely redo our dependency list, and we will be able to go easier make the changes listed above. Instead of a step-by-step upgrade causing bugs and misbehaviours of the rest of the legacy we have, we can directly do it correctly. As a side effect, we could rethink legal aspects of the codebase if we don’t have any lines left from original SMC. That however is stuff for a new issue in case we decide to go for the rewrite. Even more, if anyone here feels unsatisfied with C++, we could consider a different programming language. Let me however make clear that I am very reluctant to that as I’m pretty happy with C++.

If we get time, I’d like to discuss this a little in the upcoming General Discussion, but I repeat that this is a midterm proposal and nothing for the next release or even the next number of releases. It is not in a votable state. It is an open discussion that we should have here.

This ticket is mainly targetted at the programmers involved into TSC. @Luiji, @brianvanderburg2, @lambda-11235, @datahead8888, what do you think of this? I am sure everyone of you has stood before the legacy code and thought more than once “what a mess it is!”. In my opinion, trying to reform the large legacy codebase with all its flaws is not worth it. Restarting is better and quicker.

Valete, Quintus

refi64 commented 8 years ago

Yeah, I noticed the codebase's...uhh...quality...when porting the audio system to SFML.

Quintus commented 8 years ago

@kirbyfan64 vivid discussion in IRC about this currently: http://chatlogs.secretchronicles.de/htmllogs/2016-03-26.log.html#msg-2016-03-26T18:40:43+00:00

datahead8888 commented 8 years ago

These are the suggestions/concerns I brought up in our IRC discussion:

@sauer2 - changing game mechanics means changing levels -- we will be needing your input as we discuss mechanics changes.

Quintus commented 8 years ago

I don’t want to clutter this issue with legal questions. Let’s focus on the technical side here, and discuss any legal stuff in #535.

Quintus commented 8 years ago

I would probably suggest having the rewrite be a continuation of our existing git history. This allows people who have racked up commits in GitHub to continue having these commits show. This can be useful for job hunting for people; employers are likely only to look at one Secret Chronicles game's commit count and not two different game repos.

On this I agree and suggest to have the first commit in the 3.x branch be a rm -rf *. Anything we want to keep can be re-commited. This allows to retrieve the list of all authors involved in the "new" TSC easily with one Git command (important for legal issues).

We have already slowed feature development down while doing API upgrades and attempting a scene management system before. We need to keep some features rolling in a 2.X series while we start on the new 3.X series for the rewrite. This helps keep people coming back to our game.

To clarify: We will probably develop 2.x and 3.x in parallel, with focus shifting slowly from 2.x to 3.x.

Valete, Quintus

xet7 commented 8 years ago

At IRC discussion, I chatted with @Quintus that it's good to blog about game parts rewrite process etc to our website news section, it shows that project is active.

ghost commented 8 years ago

Good idea, if you actually have the time.

Just keep in mind that cache locality might matter for levels with 1k+ tiles and that many (even "modern") C++ features tend to harm code transparency and maintenance if used more than sparsely. I know this might sound harsh but I often can't help but get the feeling you aim for completely over-software-engineering.

Also - since I skimmed the log - I came across the suggestion to put more parts of the game logic into scripts. Please don't. I've been curious about interpreters - in particular embedded interpreters - for some years now and the universal, repeating story in every domain appears to be that people regret putting more code than really simple event handling into scripts.

@datahead8888 About game mechanics: Is there already a possible change we need to discuss or is there need to discuss something on IRC before the meeting on 9th?

datahead8888 commented 8 years ago

I know this might sound harsh but I often can't help but get the feeling you aim for completely over-software-engineering.

Yes, some design patterns can overcomplicate the architecture, while others will make it more supportable for us. This is another reason to use UML diagrams in order to make up our minds what works well.

We at least want to clean up poor practices like use of global variables, use of internal CEGUI API's, hacky physics systems, etc.

Ive been curious about interpreters - in particular embedded interpreters - for some years now and the universal, repeating story in every domain appears to be that people regret putting more code than really simple event handling into scripts.

I was thinking something along the lines of allowing some game rules or initialization to be overridden with scripts, possibly. We aren't going to do this on a whim, so there should be plenty of chances to raise concerns.

Is there already a possible change we need to discuss or is there need to discuss something on IRC before the meeting on 9th?

Not yet - this was just an FYI that it's going to start being discussed more and more soon. You will probably want to keep an eye out. The meeting is going to discuss the life energy system and some associated questions like save states, lives, and dying in pits. Other mechanics discussions, like the jump distance for Alex or power up systems are probably going to start popping up in the tracker and/or mailing list.

ghost commented 8 years ago

OK, thanks for clarification. Well, guess we have to wait for the meeting.

Am 27.03.2016 um 04:20 schrieb Chris Jacobsen:

I know this might sound harsh but I often can't help but get the
feeling you aim for completely over-software-engineering.

Yes, some design patterns can overcomplicate the architecture, while others will make it more supportable for us. This is another reason to use UML diagrams in order to make up our minds what works well.

We at least want to clean up poor practices like use of global variables, use of internal CEGUI API's, hacky physics systems, etc.

Ive been curious about interpreters - in particular embedded
interpreters - for some years now and the universal, repeating
story in every domain appears to be that people regret putting
more code than really simple event handling into scripts.

I was thinking something along the lines of allowing some game rules or initialization to be overridden with scripts, possibly. We aren't going to do this on a whim, so there should be plenty of chances to raise concerns.

Is there already a possible change we need to discuss or is there
need to discuss something on IRC before the meeting on 9th?

Not yet - this was just an FYI that it's going to start being discussed more and more soon. You will probably want to keep an eye out. The meeting is going to discuss the life energy system and some associated questions like save states, lives, and dying in pits. Other mechanics discussions, like the jump distance for Alex or power up systems are probably going to start popping up in the tracker and/or mailing list.

— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/Secretchronicles/TSC/issues/534#issuecomment-201972415

Quintus commented 8 years ago

many (even "modern") C++ features tend to harm code transparency and maintenance if used more than sparsely.

Yes, but the current state of the code base is awful. It is conglomerate of ad-hoc code changes with lots and lots of global variables and no public/protected/private scoping anywhere. I wasn’t meaning to now use C++11 lambdas at every possibility in case you fear that. I am aware that advanced code features must be used sparingly, otherwise the result hurts more than it helps. People using templates for no reason or using exceptions to terminate nested loops don’t do things correctly. Another thing I am particularlyly reluctant of is the auto keyword. I want to know my types. If I don’t want that, I use another programming language.

My top 1 goal is always a good code design that is easy to grasp and the result of logical thinking. I usually sacrifice performance for that goal as I find a bugfree slower program more useful than a buggy performant program. It is a question of maintenance work.

Also - since I skimmed the log - I came across the suggestion to put more parts of the game logic > into scripts. Please don't. I've been curious about interpreters - in particular embedded interpreters - for some years now and the universal, repeating story in every domain appears to be that people regret putting more code than really simple event handling into scripts.

I absolutely agree with you. I want the scripting facility to remain in-level scripting for level design purposes, not part of the game core. Or in other words: It should be possible to conditionally compile TSC without scripting enabled and it should still run (although of course levels won’t have scripting).

Yes, some design patterns can overcomplicate the architecture, while others will make it more supportable for us.

FurballSingletonDelegatorFactoryGeneratorFactory::produceFurball()... Oh wait, I thought we didn’t want to go Java.

Valete, Quintus

ghost commented 8 years ago

That's reasonable I guess. But one further question:

When you wrote

It should be possible to conditionally compile TSC without scripting enabled and it should still run (although of course levels won’t have scripting).

Do you expect those levels to be designed in a way that you can finish them without scripting? Or at least the campaign levels? That would prohibit quite a number of possibilities.

Also, whenever TSC is compiled without mruby it should probably show a message about it. Maybe some banner text, similar to if you open a file manager with administrator rights. Otherwise we might get complaints "your levels are incomplete/your game is broken".

Quintus commented 8 years ago

Do you expect those levels to be designed in a way that you can finish them without scripting? Or at least the campaign levels?

No, I didn’t mean that. It is more of a theoretical thought that should outline the level of integration into the game core. You are absolutely free and encouraged to make levels that require scripting! And of course, those will be included into the main campaign.

Also, whenever TSC is compiled without mruby it should probably show a message about it.

We can have that, but I have doubts anybody would compile it without mruby. As said, this was a theoretical thought.

Vale, Quintus

Bugsbane commented 8 years ago

I'm fine with whether the code is rewritten or not as long as it remains FLOSS. From my perspective, this is an issue for the coders, as you're the ones who'll need to deal with it, so whatever makes the coders happy and encourages future development, has my vote.

brianvanderburg2 commented 8 years ago

I've always felt the code needed an overhaul. I can help out here and there, but sadly rather sparingly. Just so many other obligations, a job that consumes, driving and all, about 12 hours a day, etc.

Bugsbane commented 8 years ago

We just have to be careful though... more than once I've seen a major rewrite of a foss project become so overwhelming that it demotivated the people involved and killed, or just about killed the project (eg. Kdenlive, which seems to have just about recovered, but not before going months without contributions or releases and having the project lead quit, although he eventually came back)

Bugsbane commented 8 years ago

Admittedly, working on kludgy, buggy, hard to understand, peeved together code must also be demotivating, so I guess it's about finding a balance.

brunonymous commented 8 years ago

Please consider SDL2. SFML does not support Wayland. Wayland is more or less "the future" compared to X.

So if you look here you will see the supported SDL2 platforms:

Current SFML supported platforms:

Quintus commented 8 years ago

Please consider SDL2. SFML does not support Wayland. Wayland is more or less "the future" compared to X.

The "future" is going to take some time still, and SFML is under active development. We have just (1 month ago) switched from SDL (1) to SFML and nearly all devs of the team found its API to be much nicer to code against than SDL’s, so it is very unlikely we switch back.

more than once I've seen a major rewrite of a foss project become so overwhelming that it demotivated the people involved and killed, or just about killed the project (eg.

I can understand this concern, but I think we can handle it. We are going to maintain 2.x for now while we start into a planning phase for 3.x first. I.e. we will not directly jump into coding tomorrow, but plan some of the architecture prior to doing so, which should allow for a more quick implementation (in theory).

The thing is, the current code’s quality is awful. Once simple does not want to work with it. Every time I touch it I feel highly motivated to restart from scratch instead, and would rather stop contributing to the old code base than not starting from scratch.

KDenlive is a project much more complex than our little game. While a game’s architecture in code is usually similar in most games, one can’t really say that from programs intended for production of things. If a few of us will support the rewrite, we can definitely do it. If it comes out during the planning phase that only me alone will ever commit a line of code, it is rather unlikely (but not impossible).

Valete, Quintus

datahead8888 commented 8 years ago

We just have to be careful though... more than once I've seen a major rewrite of a foss project become so overwhelming that it demotivated the people involved and killed, or just about killed the project (eg. Kdenlive

We might do an approximate effort estimate after better determining requirements. We can't really do a time to completion estimate for certain, since this is volunteer and because we cannot be sure of people's availability. How much time people have actually is the greater issue.

Since we're essentially going to make a new game, anyways, it could be viewed as starting on a new game, albeit with the same name. It would be healthy, however, to compare the effort roughly between the two options of modifying the existing code base and starting a brand new one.

Please consider SDL2. SFML does not support Wayland. Wayland is more or less "the future" compared to X.

We can't just switch back and forth all time time, even for the rewrite. Quintus seems to have a valid point on the future.

Quintus commented 8 years ago

@brunonymous I did you a favour and requested Wayland support in SFML, which is what you should have done instead of asking downstream projects to boycot it. https://github.com/SFML/SFML/issues/1082

Quintus commented 8 years ago

Time to get things up and running! We should first agree on a general architecture, rather than digging up random UML diagrams.

Let us start with a brainstorm phase. Could everybody please give his thoughts for what he would like the TSC code base to look like? We’ll later collect the best ideas and decide on a a design that will hopefully fulfill as much of them as possible.

There are no bad ideas in brainstorming. Bring forward everything you can think of, as long as it is related to the code architecture (so not things like "I think the graphics should look different").

Happy brainstorming, Quintus

refi64 commented 8 years ago

Here's my initial thoughts (I've been trying to make an RPG in Pascal for fun, and I'm using a similar architecture):

We would have a class pertaining to a window. This window class would sort of be the over-arching class that manages the SFML window and draws objects onto it via a method void draw(sf::Drawable& obj);. It could also potentially manage things like sound and saving.

The window would contain a (smart) pointer to a "screen" object. This could be an ABC; the actual derived classes would be things like LoadingScreen, MainMenuScreen, WorldMapScreen, and LevelScreen. This would encapsulate the actual state of the game itself. When the window object picks up a key press, it passes it down to the actual screen via a virtual method, passing the window object. So the methods could be like virtual void handle_key_press(Window& w, SomeType key);.

Each screen would also have a method virtual void draw(Window& w);, which would draw the screen onto the window. The screen's drawing internals would go much like one would expect. Maybe there could be some kind of mixin-style class for managing gradual state, or things that change over a time period.

The LevelScreen would contain a Level object, corresponding to...a level. Just the map and enemy positions. When the LevelScreen is given a key press, it could use some sort of abstract interface with the level to determine how to move Alex around. The level screen would also manage the camera. I'm thinking the level's abstract interface could have methods for getting the Entity at a certain coordinate and such. The level, when initially constructed, could be maybe be passed some sort of object for managing the gradual state, maybe via callbacks.

That's the furthest my ideas have gotten, though.

ghost commented 8 years ago

There are no bad ideas in brainstorming. Bring forward everything you can think of, as long as it is related to the code architecture (so not things like "I think the graphics should look different").

OK, here goes nothing:

You could implement object types that may appear in big numbers, like particles or static tiles as data oriented as feasible. SMC struck me as a little heavy on the CPU (that said, maybe also on the GPU side, but I think it became slower as the average level contained more and more tiles), more than it should. As a bonus, depending on how it's done it may be easier to bind mruby.

Maybe some LevelGeometry class could take care of drawing/handling the tiles in it, if that fits into @kirbyfan64 s schema.

Another aspect, but I'm not sure if it's related to architecture: It probably should be easier to add custom tiles if one makes third party levels. Maybe it could be solved similar the way C-Dogs SDL implements it:

Quintus commented 8 years ago

Here’s what I was imagining all the time when I talked about "the scene system". It is actually pretty close to kirbyfan’s suggestion. Forgive me the theatre terminology, I couldn’t resist it in the face of the term "scene system" and the discussed storylines.

There’s a Play singleton class that acts as the the one (! only one!) global object in our codebase. Any global information we need goes into this object and nowhere else. This singleton object has a stack of scenes, where the topmost scene is considered the active or "current" one, i.e. the one the player is playing at a moment. So, when the game starts, the first scene pushed onto the stack is the TitleMenu scene. The player then selects a world. This pushes a WorldScene onto the stack, making it the topmost and hence current scene. In the world, the player then selects a level. This pushes a LevelScene onto the stack., making it the current scene. The player plays the level, finishes it. When finishing the level, the topmost scene (the LevelScene) is popped from the stack and destroyed. Since the next scene below it is the WorldScene again (it wasn’t removed, it’s a stack!) the player automatically gets shown the WorldScene again as it’s now the topmost scene. When exiting the world, the WorldScene gets popped off the stack and then the topmost scene is the TitleMenu scene again. When the player then selects "exit" on the title sceen, the TitleMenu scene is also popped off -- leaving the stack empty. The game’s main loop will terminate when it detects an empty scene stack.

The nice thing is that for all of these scenes, we can have a base class Scene which comes with two methods:

class Scene
{
  // ...
  virtual void update();
  virtual void draw(sf::RenderWindow stage) const;
  // ...
};

Each scene class can built on these two methods, while the main loop only needs to know about these two. So when a LevelScene is on top of the scene stack, it’s update() and draw() methods will be called by the main loop automatically. A LevelScene then includes information from a specific level that it will display, including all actors in this level (Alex, the enemies, anything that moves). Even the scenery can be included in such a level scene, and the best thing is: The rest of the game (all other scenes) do not need to care about how the LevelScene manages its internals. So there’s room for the LevelScene containing for example a single object for the entire static scenery (this is from sauer2's suggestion). The TitleMenu doesn’t need to know this. Current TSC architecture passes global information around everywhere, which is scary...

Of course, this concept can be applied to anything that needs to be passed from the window system to the game, like keypresses as kirbyfan has suggested. The flow of information always goes this way:

(OS) -> sf::RenderWindow -> TSC::Play singleton -> Scene on top of the scene stack

This scene system has returning to previous scenes automatically built in by means of the stack. There’s no worrying about how to get the player back to the title scene after he entered a world or a level. Even more, if he uses the direct "enter level" functionality skipping the world layer, this system doesn’t have to do anything special. There will be simply no WorldScene in the stack, meaning that when the LevelScene gets popped off the stack the next active scene will be the TitleMenu scene. Likewise, an InGameMenu scene can simply be pushed onto the stack when the player presses the [ESC] key. As the current scene it will be the only one updated and drawn by the game’s main loop. When he exits the in-game menu, the InGameMenu scene gets popped off the stack, and, voilà, the next scene on top is the LevelScene with the level he just left.

The system is completely dynamic and agnostic of which scenes are on the stack. New scenes can be added without the need to edit any of the other scenes other than pushing it onto the stack somewhere. Want to add a credits scene? Write one and when finishing the last level, pop the level off the stack and instead push the Credits scene onto it. Result: After exiting the Credits scene the player returns to the TitleMenu scene. Want to add a Shop scene for in-level use? Write it and simply push it onto the stack when a certain item is queried in the level. When the ShopScene closes, it gets popped off the stack, and, again, what’s next on the top of the scene stack is the LevelScene again so the player is back at where he came from.

How does that sound?

Valete, Quintus

refi64 commented 8 years ago

@Quintus The stack is a really neat idea! However, take the case of post-level cutscenes. Would the level push the cutscene "scene" onto the stack, even though the level is already technically complete, or would it somehow replace itself with the cutscene?

Quintus commented 8 years ago

@kirbyfan64 I haven’t even thought about the cutscenes, but the term makes it obvious they should be a scene. I would implement a post-level cutscene the same way I suggested for the credits scene: Pop off the level scene from the stack and push the cut scene onto it. The active scene does not change the very moment when you push something new onto the stack, but only on the next iteration of the main loop when the top of the scene stack is inspected again. Doing things like this is thus fine:

void on_level_finish() // hypothetical method executed at end of level
{
  delete gp_play->pop_scene(); // Pop off the level scene
  gp_play->push_scene(new CutScene("cutscenefile");
  // Next iteration of main loop will encounter the CutScene instance on top of the stack
}

Vale, Quintus

ghost commented 8 years ago

@Quintus That sounds neat and flexible enough to provide modding capabilities but how do you handle levels where you warp into another level? Same as with scenes? But what if you warp back? How do you preserve the previous level state? Is it even planned? Do you put it on another stack that gets deleted as soon as you leave to an overworld/menu?

Also, would there be a queue that contains levels/scenes/menus the player has to complete?

Quintus commented 8 years ago

That sounds neat and flexible enough to provide modding capabilities but how do you handle levels where you warp into another level? Same as with scenes?

There is no need to specifically preserve something. The old level’s scene simply remains on the stack in the state it was left in. When you enter a sublevel, simply a new LevelScene is pushed onto the stack, making that one current. When the player leaves the sublevel, the sublevel’s scene gets popped and the player is exactly where he left off. The scene stack system has state preserval built in by definition.

More problematic with regard to sublevels is when they have effect on the outer level (scene), like when you should come out somewhere else than you got in (as it is for example in the current lvl_1). We’ll need to establish some kind of communication API here.

Also, would there be a queue that contains levels/scenes/menus the player has to complete?

Is there a reason to move away from our current campaign/world/level structure?

Vale, Quintus

ghost commented 8 years ago

@Quintus

That's actually what I had in mind.

How would that look like?

Now, that I think about it, no.

EDIT: Markdown somehow isn't displayed right.

EDIT2: For now, nobody has mentioned the editor. Any thoughts?

Quintus commented 8 years ago

We’ll need to establish some kind of communication API here.

How would that look like?

Each level has its own notion of the level player, i.e. an own instance of the Player class (in contrast to what TSC has currently, where only a single global level player object is used). Since the level scenes are available on the scene stack, it should be possible for a sublevel’s exit to examine the levels below it in the stack and adjust the player’s position in the level before it hands control over to the the main loop again and thus the next scene. For example:

void LevelExit::activate()
{
  LevelScene* p_this_scene = static_cast<LevelScene*>(gp_play->pop_scene());
  LevelScene* p_below_scene = dynamic_cast<LevelScene*>(gp_play->get_current_scene());

  // p_below_scene is NULL if the scene below us in the stack is for
  // some reason not a level scene (for example if the sublevel was
  // selected directly in the menu).
  if (p_below_scene && !m_target_entry_name.empty()) {
    LevelEntry* p_target_entry = p_below_scene->get_level_entry(m_target_entry_name);
    p_target_entry->set_active();
  }

  // Free the scene afterwards, because this will also
  // annihilate the 'this' pointer.
  delete p_this_scene;
}

// ...

void LevelEntry::update()
{
  if (m_set_active) { // set if returning from another level
    activate();
    m_set_active = false;
  }
  // ...
}

void LevelEntry::activate()
{
  // Do camera adjustment
  // Play the entering animation on the player
  // Warp the player
}

This is only a rough idea. I’m open for better suggestions. It also most likely has problems with nested sublevels.

For now, nobody has mentioned the editor. Any thoughts?

The editor could be made its own scene, but that would prevent changes made to be active in the level currently played. A reload of the level would be required. If we want to maintain the current facility of "live editing" a level, we’re going to have to incorporate it somehow into the level scene.

Vale, Quintus

Quintus commented 8 years ago

Shall we keep the in-game editor? Or extract it to a separate application? It complicates the internal architecture, so I’d be in favour of externalising it.

Valete, Quintus

datahead8888 commented 8 years ago

Shall we keep the in-game editor? Or extract it to a separate application? It complicates the internal architecture, so I’d be in favour of externalising it.

As we discussed a while back, having the editor in-game was a major selling point of SMC, since you could start editing levels at any time while playing them if you really wanted. I remember @Luiji wasn't happy when the editor was removed from the main Super Tux game; Super Tux has since started working on adding it back.

ghost commented 8 years ago

I think it is less of a problem, if the external editor can run a level for testing purposes, gets build as part of the project and doesn't require additional dependencies. It probably helps if the level game part is a library that can be reused in the editor, but be aware that you don't fall into the same trap as https://github.com/cxong/cdogs-sdl where the game settings have influence on the editor.

Since that is probably complicated as well, I'm with @datahead8888 .

Quintus commented 8 years ago

Okay, then let’s keep the editor and keep it in-game. I’m fine with either way, just exploring ideas. And please, if anyone of you has suggestions on whatever code architecture topic, please participate!

Something other and probably controversial. During my porting to CEGUI 0.8.x of the current TSC branch I have worked a little with CEGUI and I have revised my opinion about it. The API is indeed nice, but the documentation is rather bad. Since the API is well designed, it mostly works still, especially since the CEGUI devs are finally revising their tutorial documentation and provide a collection of samples (however, many samples are too complicated — a sample should usually be of the structure "task - solution", not "look what super-cool and complex UI you can do!"). From the knowledge one gains from taking the introductory tutorials one can usually guess the meaning of the functions in use. One still has to wildly guess the names of the widgets available, though. It’s irritating that CEGUI has no StaticText widget by iself, but simply uses an ordinary Window with a specific skin.

CEGUI works fine for standard tasks and standard GUI’s, which is most of what TSC uses. The problem I have with that is that for achieving these standard tasks, the procedure is quite bloated, involving several XML configuration files. The XML config file system is, again, well tought-through, but prohibits a quick understanding of how things work. This is then also where the real problem starts. Once you need to do something nonstandard — which in our case is the list of objects in the editor — things get incredibly complicated. For reasons I don’t understand, CEGUI has no widget to simply display an image from a file, scaling and rotating it as required. Simply displaying a nonscaled image is difficult enough; you need to create an imageset first, make the image the only member of it, and then attach it to a StaticImage, which actually is a Window with a special skin (like StaticText above). That doesn’t allow to scale or rotate the image. I would be fine with that if I could simply create my own custom widget, but for doing that, you need to comprehensively understand the full internal CEGUI stack. CEGUI’s internal structure is very complicated and has no documentation; you need to read the source code. And CEGUI has a lot of source code.

For task #10 I’ll find some workaround, maybe by adjusting the editor UI to not need rotated/scaled images from files. What I’m trying to say here is that CEGUI has very flexible skinning, but buys that flexibility with a really deep complexity. They managed to make simple tasks simple by good API design, but anything more complicated will drawn you down into a rabbithole of badly documented internal structure. This structure might be nicely thought through still, but in the time needed to understand it one can write a custom simple GUI system fulfilling the tasks we need for TSC.

That is, if we were a game studio producing a lot of games which all need UIs, it might warrant to dive into CEGUI’s deep internal structure and profit from its quite flexible system once one has understood it. However, since that takes a lot of time and source code reading of CEGUI (to such an extent that one can actually contribute to CEGUI itself when done) I would suggest to not do it and write up a custom UI instead. That is going to be flexible enough to not hard depend on SFML (like SFGUI) and saves us from diving into CEGUI’s internals, while at the same time fitting our needs.

If I remember correctly, this was basically @Luiji’s position before I said that I find CEGUI nice. After having fiddled with the editor, I don’t find it that nice anymore. CEGUI’s internal complexity is overwhelming. Other APIs have similar complexity, but CEGUI’s documentation does not comprehensively cover everything and thus make it difficult to use. An even bigger problem is that a lot of seemingly useful information on the Internet is simply outdated. Many pages still refer to CEGUI 0.7.x or even earlier.

Valete, Quintus

Quintus commented 8 years ago

Postinfo: I found a way to scale an image in CEGUI.

ghost commented 8 years ago

I would suggest to not do it and write up a custom UI instead. That is going to be flexible enough to not hard depend on SFML (like SFGUI) and saves us from diving into CEGUI’s internals, while at the same time fitting our needs.

@Quintus Makes sense to me. One thing though, how do you want to implement the source code editor in the level editor? It's probably a non-trivial amount of work.

One possibility would of course be not at all, and since people that script levels most likely have their favorite text editor anyway this might work out well. For example, the person that creates the script could simply put it into an *.rb file with the same name as the level file into the world folder I mentioned earlier.

Other ideas?

Quintus commented 8 years ago

how do you want to implement the source code editor in the level editor? It's probably a non-trivial amount of work.

That is actually something that is impossible with CEGUI itself anyway (I asked about RTF editing in the CEGUI forums, the devs told me it’s not possible -- not to mention things like automatic indentation and such). There is no choice but writing it ourselves. Except if we externalise the level editor, then we can recur to use of standard source highlighting widgets.

For example, the person that creates the script could simply put it into an *.rb file with the same name as the level file into the world folder I mentioned earlier.

That is an interesting idea and would release us from providing a sophisticated code editor. It would however force the user to do the level editing in windowed mode and not in fullscreen. Due to TSC lacking a file selector dialog, this is the de facto standard anyway currently, so it’s probably not too bad. Simply shelling out to $EDITOR is definitely a possibility. In good old unix style, we’ll launch vi if $EDITOR is undefined — in fullscreen of course :-P

Vale, Quintus

ghost commented 8 years ago

@Quintus @datahead8888 @Luiji That reminds me to ask: How do you want to handle translation of level texts?

It could be handled in a similar way to scripting files that there is a translation file with the same name as the level file. If no such file was found, the game assumes the strings in the level file are the strings to display...

Quintus commented 8 years ago

That reminds me to ask: How do you want to handle translation of level texts?

I have not yet thought about it, but making level texts translatable is indeed something useful. Since Gettext is required by the game anyway, we could try to allow to just drop .MO (compiled .PO) files into the same directory as the level files. We could also invent our own system, though. I don’t think I have much of a preference here.

Vale, Quintus

ghost commented 8 years ago

BTW: Will the new engine use multithreading?

Quintus commented 8 years ago

sauer2 notifications@github.com writes:

BTW: Will the new engine use multithreading?

If someone makes a useful code-architectural suggestion.

Vale, Quintus

Blog: http://www.guelkerdev.de GPG-Schlüssel: F1D8799FBCC8BC4F

ghost commented 8 years ago

@Quintus I'm no expert on multithreading, so here goes nothing:

lambda-11235 commented 8 years ago

I'm not an expert in multithreading, but I don't believe it will necessarily produce a performance increase. Multithreading usually works best when the actions taking place in separate threads are losely related, and where concurrency would make sense without multithreading. One example of this is HTTP server requests, where each request needs to be ran in parallel, but individual requests don't communicate with each other, only the with the main server process.

Multithreading can also be a bad idea if the threads have to do a lot of communication, or share a few resources, as they will have to constantly pause and wait for each other, which can in turn slow the system down. So multithreading the script system may be a bad idea, as it is an integral part of level execution, and therefore separating it out may be difficult and actually slow down execution speed.

If it is decided to use multithreading, I would recommend using STM or an actor system. Locking systems are notoriously hard to create and maintain. To be honest, I've only ever used STM in small toy projects, although I did try to learn how to use locking based systems and failed.

As I said, I'm not very knowledgeable in this area, but multithreading can be the worst kind of premature optimization. It is best applied to systems that have some inherently concurrent traits. Hopefully what I say is mostly correct, but you may want to research these issues in your own time.

I''l leave you with these links.

Quintus commented 8 years ago

Multithreading usually works best when the actions taking place in separate threads are losely related, and where concurrency would make sense without multithreading.

I agree with this. TSC does not really do much things in parallel, though some things can probably be externalised into separate threads. I am especially with sauer2 for this feature:

Looking back at the level loading times it might be useful to have non-blocking ressource loading.

I have earlier suggested to preload levels, which would happen in separate threads. This is certainly possible.

separated threads for game logic and rendering

FluXy attempted something like this, but never drove it to completion. The TSC code has #ifdef statements commenting out code that refers explicitely to separate rendering and game logic threads, but it never compiled to my knowledge. I will have to think through the consequences of such an approach and will need to look at some tutorial implementations of that to get a better idea how that looks on the code design side and if it is a feasable possibility for TSC.

If it is decided to use multithreading, I would recommend using STM or an actor system. Locking systems are notoriously hard to create and maintain.

As often, it depends on what you want to target. I have played with celluloid, which is an implementation of the Actor model in Ruby (but never coded something serious), so I know what you’re talking about. It is an interesting approach, but requires a completely different thinking while developing the application. If we decide to go heavily multithreaded, I will look after it, but if 98% of our code is sequential than there is no point in using such a complicated programming model and a few mutex locks will do. lambda however is right when he points out that lock systems at a large scale are a bad idea.

Can (at least some) scripts run in their own thread?

Only if they do not interact with elements in the level. If for instance for some reason you need to do an expensive calculation in your level script that will burn the CPU on 100% on one core for a while and only need the result in an indefinite time without needing to wait for it, you could externalise this into a separate thread. As of now, script code does not run in a separate thread; only if you employ a timer the timer waits in a separate thread before trying to intercept the main loop when it’s done. The execution of the timer’s associated code happens in sequence in the main loop.

A single instance of mruby cannot span multiple threads currently without very proper isolation, i.e. locking. See the mruby developer’s discussion and this feature request. There’s an MGEM (mruby extension) that makes use of a global interpreter lock (GIL) to enable threads in mruby available. However, it is completely safe and possible to run separate instances of mruby in each C(++) thread. They just can’t communicate with one another, i.e. each of them is its own isolated Ruby world then.

Valete, Quintus

ghost commented 8 years ago

Ok, so I take it the only reasonable benefit of multi threading right now would non-blocking ressource loading.

BTW: When I quoted "High frequency (250Hz) joystick sampling"... I'm starting to see why one would do this:

I was looking earlier why the wait_event functions of SDL2 and SFML are crippled by using polling, some source comment claims that joystick input only can be gathered by polling.

datahead8888 commented 8 years ago

I'm not an expert in multithreading, but I don't believe it will necessarily produce a performance increase. Multithreading usually works best when the actions taking place in separate threads are losely related, and where concurrency would make sense without multithreading.

TSC with its current features, if written well, shouldn't be "that" demanding performance-wise. By the same logic, however, it might be better to use only scripting languages rather than C++ for more rapid development. At a certain point you have to ask what we want to do. We decided to use C++ because we like it. If the programmers like a multi threaded model, it makes sense to use it. I suspect a lot of the programmers will benefit from C++ multi threading experience, which looks good on resumes. This alone is enough reason to use it in my opinion.

If written properly, multi threading certainly can improve performance. Excessive locks would slow down performance, to @lambda-11235's point. We don't want to throw threads at absolutely everything blindly. If we add things like an AI system or other features that require more processing, multi threading may actually bring more benefits. As noted above, preloading levels in a different thread is a logical use of threads to optimize our loading time. There may be other places where threads make sense; we'll have to discuss these for a while, since they heavily affect the architecture.

We probably will end up with multi threading bugs here and there, but this is life. If we carefully review the critical sections of code where we put locks, we can, however, reduce the risk.

I have some other suggestions on the architecture. I will post them when I find some time.

Quintus commented 8 years ago

Okay, I think we can then conclude that we will not use a full-parallel approach using the Actor model. We will use the classic single main-thread approach and use threads at certain points where they seem useful. As a last thing to this point, please let us use C++11 threads and not boost::thread.

I want to point out that if I remain the only person coding on TSC, it is unlikely that any large change will ever happen, especially a rewrite of the game as discussed in this ticket. I would restrict myself to keeping things up to date (dependencies!) and only adding minor features. It would just be too much work for me. I know datahead is pretty busy currently -- which is okay --, but what about all the others here? Is anybody willing to help in this rewriting effort? @kirbyfan64 @lambda-11235 @brianvanderburg2 ?

I will continue on the CEGUI 0.8.x porting for now, which will eventually result in a TSC 2.1.0 being released once the major bugs are squashed after the merge. Then we can maintain the 2.1.x series at a lower amount of work as it runs, and only extend it with some minor new features (like new enemies and such). Main focus should shift on the rewriting effort as discussed in this ticket. But for that to happen, it is time to make some decisions. There is no fixed timeline in relation to the 2.1.0 release, but I would like to slowly proceed here to get the plans into a state where we can start implementing.

The scene system has already been approved (or?), and it has been decided we will further provide an in-game editor and will use a classic single-thread design with exceptions where appropriate. I think it is a good time now to talk about our dependencies. Since we are starting from scratch, we can freely rethink everything here.

The point has been brought forward that SFML is not going to support Wayland in the future. I forwarded that concern to the SFML devs at https://github.com/SFML/SFML/issues/1082, where that decision was confirmed. I expect that things will change once X11 drops out of support (or can be expected to), so I still don't think this argument has a real weight. Also, X11 is going to be there for quite a while still (with all its ugliness, I know). However, there is another point. @datahead8888 and me wanted to look into more lowlevel graphics programming. TSC as a large project is a nice fit for that, so I would like to suggest to use GLFW instead. I realise that we have switched TSC 2 just over to SFML, but let me remind you that we're going to start from scratch and we would be unable to copy TSC's current flawed input and graphics handling (which is a lot of spaghetti) anyway if we want to establish a proper structure for the program. Thus this does not really create an additional problem. With TSC 2.1 using SFML, the 2.1 branch can also just continue using SFML. Still, as this is a fairly groundlevel decision, I think it is important that all coders agree on the base library. @kirbyfan64, @lambda-11235, @brianvanderburg2, @datahead8888 do you think using GLFW is okay?

Then I noticed some unhappiness with the current XML parser, libxml++, because it pulls in a number of GNOME libraries and has been proven to be difficult to build on Windows. Thus using another one is desirable. In an effort to reduce our amount of dependencies, it would be good to use a standalone XML parsing library. Unless of course we decide to do away with XML altogether, but I think that the format serves us well. Saving a level in an INI or YAML file does not make sense to me.

On the topic of GUI libraries, my relation to CEGUI is ambilavent. The library does cover most needs indeed. Its just that one always has the feeling it is needlessly complex in its configuration, scattering XML files all around. Once one has understood how the different XML files relate to one another, it gets easier, but it still feels uncomfortable. Its API documentation is improving, and its tutorial implementation as well (in this regard I filed some issues on their tracker, and I think the devs are going to improve here). In its C++ API, the different XML files are mirrored in several managers, which is fairly overwhelming when first learning it. I still do not entirely understand how things work together. However, once that is set up, the core functionality of creating and managing windows and widgets is nicely solved and mostly intuitive to use. That they rewrote their example code so that it uses a common library does not appeal me, I prefer minimal self-contained examples ideally in one single source file.

The problem with CEGUI is that there are basically no alternatives other than writing the GUI handling ourselves. @Luiji once mentioned this is not that difficult. I think I will try to write a simple example to be able to approximate the real complexity. As of now, I can't make a qualified decision on whether or not to use CEGUI.

As I like Ruby, I want to keep mruby for the scripting facilities. Is that okay?

I want to drop Boost as a dependency. It is just huge, and C++11 has made most of its uses obsolete.

For handling of the clipboard and Unicode pathnames, I shamelessly suggest to use my own libraries, tinyclipboard and pathie-cpp, which are both standalone (no further dependencies). tinyclipboard is already part of the upcoming TSC 2.1.0. I have to say it is a shame that Unicode pathnames are still not standardised in C++.

Valete, Quintus

refi64 commented 8 years ago

Here's my 2c:

  1. I don't see something like GLFW as necessary. SFML not only covers everything but also provides some nice extras, such as 3D sounds, views (for camera control), and font handling. TSC isn't necessarily an AAA game like Final Fantasy XV that needs to squeeze out every extra frame and do crazy GPU stuff. IMO GLFW would be really overkill and hinder development progress.
  2. I would encourage looking in to Protocol Buffers for data storage and loading instead of XML. It sounds really, really weird, but remember: all the files are likely going to be written by TSC anyway. This is really fast and auto-generates a nice API for data access.
  3. If you're looking into a CEGUI alternative, maybe you'd like KIUI? I'm not sure how hard it is to use it with SFML, though...
  4. I'd love to help! I'm actually trying to make my own game, too, so I think I would be able to get around a bit more easily now.
refi64 commented 8 years ago

Also, if you're really into XML, maybe you'd like pugixml or maybe even CppExpat (the bias is real on this one ;).

Quintus commented 8 years ago

I don't see something like GLFW as necessary.

It is not. The point I was making was referring to learning how it works (I for my part have never written something with glfw, but it interests me and might prove useful later in other projects). If you do not find that a good idea, we will use something else. SFML's API is nice, we already know it.

overkill and hinder development progress.

Which is a very valid point. If coding with it is that complex indeed, I don't think we should use it. @datahead8888, what do you think?

I would encourage looking in to Protocol Buffers for data storage and loading instead of XML.

I hear of this the first time. Are you referring to this Google library? The resulting format appears to be a binary blob. XML is a well-known format, and I have to confess that I have made changes to my levels by hand-editing XML files when I found the TSC editor to have shortcomings (for example, this was for a long time the only way to get blue flyons into your levels -- for worlds, it is still the only way to set the background colour). It appears difficult to do that with a binary format. How long will Google support this? Are they going to ditch it as soon as something cooler comes around, as its somehow normal this days with certain companies? Also, it generates code I would rather write myself. I am quite reluctant to code generators (which btw is one of the reasons why I dislike RubyOnRails quite a bit).; why not just provide a library as everyone else does? Its license looks a little surprising on the first glance (they appear to redistribute RedHat code which license I was unable to find while glancing over the repository), but that can be worked out probably. Do we really need something with support for a dozen programming languages in the format? The code itself also appears to have a number of further dependencies, whereas I would like to have a slim no-further-dependencies library.

If you're looking into a CEGUI alternative, maybe you'd like KIUI?

This looks interesting. Is it still developed or has it gone out of maintenance?

I'm not sure how hard it is to use it with SFML, though...

I'll have to take a look at some docs and examples for that, but the README state's its renderer-agnostic, so it should work with everything, just as CEGUI does.

I'd love to help! I'm actually trying to make my own game, too, so I think I would be able to get around a bit more easily now.

Great, this will make things easier. Now let's wait how the others reply. Also, on datahead, he is going to (re)join the efforts when he has more time again. He has just moved, which has kept him busy.

even CppExpat (the bias is real on this one ;).

I think the author of this library was involved into one of my projects. Wait, I have to think about it... :-) Let's see what others think. I have not yet made my mind up about which libraries to use, but I just noticed there are problems with libxml++ as noted already. If we go for expat, then your wrapper might indeed be useful.

Vale, Quintus