Real-Time Air Traffic Control Game! // Code Review
ฝัง
- เผยแพร่เมื่อ 7 ก.ย. 2024
- See NordPass Business in action now with a 3-month free trial here (use code thecherno) ► nordpass.com/th...
Patreon ► / thecherno
Instagram ► / thecherno
Twitter ► / thecherno
Discord ► / discord
Code ► github.com/Bog...
Everything ► www.voidtools....
Hazel ► hazelengine.com
🕹️ Play our latest game FREE (made in Hazel!) ► studiocherno.i...
🌏 Need web hosting? ► hostinger.com/...
💰 Links to stuff I use:
⌨ Keyboard ► geni.us/T2J7
🐭 Mouse ► geni.us/BuY7
💻 Monitors ► geni.us/wZFSwSK
This video is sponsored by NordPass Business.
What did you think? Do you want to see a Part 2? 👇
See NordPass Business in action now with a 3-month free trial here (use code thecherno) ► nordpass.com/thecherno
Hi Cherno, can you make a tutorial on how to use c++ modules?
@@da_k8gamer978 just use #import, you don't always need to use the newest things.
BLUD nordvpn has been leaked like sht lmaoo
@@TheCherno sure
Nah, I'm thinking that anyone who's really interested in seeing the network code can download it themselves and go through it. The fact that you demonstrated how to get the code up and running helps everyone with that.
On the other hand, if you take a look at the network code and think there are some particularly interesting critiques you can make, then go for it, of course.
Thanks for reviewing this code. I'm not a pro by any means and I'm probably...not even knowledgeable enough to be SUPER dangerous I guess. I appreciate it anyways. I love code.
was that your code? Seemed a lot like how we approach C++ at UW.
@@agentm10 haha no, I'm not probably capable enough to set something like this up sadly.
@@johnclasing4627 You don't know until you try. Failure is progress.
The wildest part of this video was learning that C++ has NO NETWORKING IN THE STANDARD LIB, IN TODAY'S ECONOMY
18:40 I saw a .idea directory, so i'm assuming they used CLion which redirects console IO into the IDE console, so the windows subsystem was never a problem. Also CLion sets the build directory to be the project directory by default, so that's the reason why the resources couldn't be loaded, because the build directory was "wrong".
We do want part 2 of this.
You are wrong about the singleton via Instance-functions being unusual or not useful!
It is a fairly common pattern in C++ code known as Meyers' Singleton which avoids certain issues with global objects, namely the static initialization order fiasco.
Furthermore, since C++11, it guarantees the initialization happens in a thread safe manner the first time the function is called.
Among others, the C++ Core Guidelines recommend this version (if you need a singleton at all, which I agree you rarely do)
I ended up with same solution after myriads of bugs due to static initialization order fiasco. Its nice to know it has a name ;)
no, he's not wrong. There's a lot of reasons why singleton is called an antipattern sometimes. It's bad for testability and concurrency for example and violates the S of the SOLID principles. So, it's pretty reasonable he has no love for it.
@@tlacmen also that, C++ cannot ensure a specific order of static object initialization
the global variables constructor call orders are not guaranteed. By using the "g_* variables" you're asking for a big trouble which would be very difficult to find and fix. Unless you know what you're doing, you should always use the "enchant singleton pattern".
@@blapis89 I am not saying singleton is great! I personally consider it an antipattern as well, but *if* you want a singleton (or single global object accessible from everywhere) the *way it was done in the code* (Meyers' Singleton) is a well known, common and reasonable way to provide it!
I’ll say, man. You bring a much-needed perspective of a production C++ engineer to TH-cam, in my eyes.
Build problems, missing dlls, things like this…
They aren’t addressed in C++ tutorials or discussions on languages.
They also aren’t really taught anywhere at all, at least not where I know.
So you having a working perspective on C++ is very valuable to me.
I like how we are always talking about "roasting" code, but Cherno is about the most polite "roast master" on the internet.
18:31 In Unix, you can start the program in the terminal using ./path/to/bin and then you would get stsout/stderr into your terminal
Same for Windows. All we needed here was a terminal!
Crazy how much goes into just setting projects up, build steps etc. just to get it running. But love that you do this, because this is so common when trying to get almost every project running locally.
I love programming. I hate configuring build processes. C++ seems to be mostly about the latter 😄
Hi, Cherno!
Thanks a lot for review. Hoping for a part II 😃
18:40 When you launch as an end-user, you do so by tapping the app icon - no console. But as a "developer", you can open CMD and run the game from there, then you receive the logs there.
according to the "/" in the file path, I guess it hasn't been developed on Windows. On linux you usually don't expect someone would need "terminal" on windows.
@@OndrejHolecek I've been using "/" on Windows for my whole life, because "\" is ugly and Windows is absolutely fine with "/"
In windows it doesn't work like that. If you set the subsystem to window and not console, even if you launch from cmd it wouldn't spit logs. In the code you explicitly need to spwan a console using windows api and needs to use windows specific functions that directs the output to the spwaned console.
@@OndrejHolecek I've always been using "/" on Windows, it works just fine.
@@user-ed1nw6vr8n Ok, indeed CMD doesn't show the logs. But Git Bash does! Just checked with .Net6 Win Forms empty project, "Console.WriteLine". I just don't use CMD because I have Git Bash instead, and now I have 1 more reason why it is better.
It took so long coz it downloaded libraries and buildd them for you. Thats why 5 minutes. But then when you have them compiled, it takes up to 10 seconds
It's incredible how different the build process can be on different machines and operating systems. I wanted to test it myself, so I downloaded the code, ran cmake, which told me that I was missing 3 dependencies (OpenAL, Vorbis and FLAC), but only took 40 seconds to run overall. Then I ran make, which took another 50 seconds. And after that, the game was playable with no issues.
Learnt a lot from mistakes of another. Demanding part II.
at 18:38 in linux it is common to have logging that doesn't get seen or saved anywhere unless you run from terminal, many programs are written that way. it is not always necessary to view logging unless there is an issue
This! It's so awesome to launch pretty much any application, even proprietary ones, and get to see problems on the command line. I've missed this feature quite a few times on windows and i barely even use it anymore! IMO this is a perfect example of why Windows is shit for programming and power users!
Never thought I’d see the Cherno review an aviation based game
But I was once an aviator
@@TheCherno And do you have the sunglasses to prove it?
@@TheCherno Review Microsoft flight simulator code next time
@@TheCherno Review linux kernel next time
can you make a cmake guide on building projects using cmake
cmake ..
I meant setting up a project's cmake script how to link libraries and so on😭
I would also like to see a cmake guide for large team-wide projects 🙏 a lot of the resources I see teach only linking a few libraries for a solo developer. I’m still unsure how to structure a cmake project that has, say 5-10 devs working on it, with libraries depending on other libraries, putting that all into version control, and then having a batch script to build everything in case a newcomer downloads the project.
@@nameless191 just Google it and try it yourself, ask chatgpt, let it generate a simple one for you to get started
You learn more by banging your head until it works than watching TH-cam vids
@@TheCherno pls bro, stop being premake elitist XD
I would like a part 2. The code looks like it's not beginner level. The critique you're dishing out is very straightforward and bumps the code up to professional grade. With the active development on it, I can see the dev benefitting from a part 2 just as much as we can.
Love the points about decoupling the "engine" from the "game", fantastic feedback and incredibly valuable to think about how to "modularise" systems and interfaces in this manner.
Of course, for such a small project OR if the developer didn't intend to further use the engine; then I couldn't give a rats ass about the deep coupling.
I don't include build instructions as a litmus test for potential contributors. 🤷♂
Yay! "Everything" search utility is mentioned. Its pretty awesome
Yeah i liked the code in general even tho there were some redundant parts included as you said, but cool to see people having different styles to write codes. Hoping to see part 2 of this video😊
I have to strongly disagree with the access through global variables you suggest. Initialization order is not explicitly defined, but instead implicitly via linking order. Thus if you write code in the game object accessing the resource manager through a global variable, it might not have yet been constructed, and errors ensue. So access through static methods is preferable as it defines an explicit chain of dependencies. Even better obviously is dependency injection via constructor arguments.
So, as someone who isn't very familiar with Visual Studio, I am so FURIOUS to myself that I didn't know about the MP compilation option. I have been wasting literally hours waiting for builds to complete, which could have only taken me a few seconds if they'd be done in parallel. This is why I love code reviews. I learn more about C++ and the surrounding ecosystem than any other video
I would love to see a video on state machines
You can use attribute inline at 21:42 instead making it static by implicit.
Then your object will be created only 1 time not depending in what translation unit it is being used.
I also don't usually split header and source files in my projects. But I think where it makes the most sense to split them is in developing (dynamic/shared) libraries.
Either someone works on the lib itself, in which case they need the headers and source. Or someone just wants to use the library, dll/so, so they don't need the source, they only need the public API headers and the compiled library. In this cases it makes sense to split these headers to a separate folder. But that folder probably shouldn't be called just plain header or include, they should be in a folder named as the name of the library, unless it's a single header library.
Not a C++ guy, so I wouldn't know, but... what's the actual, concrete advantage of having them in different folders? What I know is that if the files are next to each other, they're faster to find while editing, and are easier to review in a merge request.
You said if someone wants to use the library, they "only need" the headers - but do they actually "lose" anything if the source is also there?
This guy haven’t aged a day since 2017
I would love to see a state machine example!
A good part of the frustration sems to be about the building/running of the project. The question is: how can I make sure that my project is going to build & run properly on as many systems as possible if I only have 1 PC at home?
Virtual machines and Docker
@@toyinbode4368 What are my options if I want to test an NVIDIA system but I only have an AMD GPU on my system?
@@adhesive_bagels Some light research shows that NVIDIA has a virtual desktop solution, but it seems all of them business oriemted. It seems you'll need to get another computer with an NVIDIA card or at least temporarily swap out your AMD card with an NVIDIA one.
There may also be the option of rental PC services in your area. I imagine at least a few of them will have the specs you're looking for.
Nice to see a project which is more intermediate. It allows for critique that goes deeper than usual.
The inconsistencies in the code and project structures shows it was heavily done with ChatGPT ie mixing C++ 23 with very old C++
lol that’s ok 😂what matters is the project
I feel very strongly that headers should be kept with their implementations, and grouped by broad category if there get to be more than a dozen or two. If you have a public API, copy those headers as part of the build.
About the dlls, consider configuring with `-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=%cd%` (or replace `%cd` with the absolute build folder) on Windows.
All dlls/exes will be built in one location.
video should be sponsored by Rode given the mic boom always in shot
I was waiting a new video of this section, I love it!
I feel like you should split all your code review videos into 2 parts. the initial impression, being able to compile, build, run etc, and then actually diving into the code. I feel like you just started to get to that point at the end when talking about the resource manager and the state machine.
I'd love to see two things personally
a) an example of doing it without the singleton because people constantly talk about singletons being bad but I feel like it's rare to see a tangible real world example of why on a smaller project. Personally I feel like on a project of this size a singleton is a good solution, but I also recognize that I don't know a lot and would love to learn and be proven wrong.
b) more diving into the code and maybe make some notes about which systems are coupled with game logic as you go, and post those perhaps. Your comments about the state machine and the resource manager were great and I'd love to see you continue a deeper dive into the code and talking about which parts you feel should exist in the "generic/engine/system" layer and which should exist in the "this is the game/implementation" layer
I second this!
Hey, just about the singleton pattern: The problem is that it defeats the whole idea of a constructor ("I give you these things, then you are a complete, valid class"). If somewhere in this class, you use such a singleton, you now have another thing that also needs to be in the right state, so that things happen how you expect them to happen. This actually applies to anything global, but for example for a constant that's not a problem - because the constant is constant, i.e. it's not going to have the wrong value.
Now, add to that the fact that the singleton pattern is one of the most easily understood patterns. So it's usually something people learn early on - but _"if you have a hammer, everything looks like a nail"_. So for these beginners, what SHOULD be the more difficult task of _"I need to rethink the structure of the program so that these two parts can talk to each other"_ instead becomes "Oh I can just use a singleton for this".
Now the compounding problem: They skip the lesson of learning how to do that restructuring all the time. Instead, they build more and more intricate singleton patterns inside a structure that is less and less appropriate, and needs more and more hacky workarounds. This goes on until the program reaches medium complexity and becomes too difficult to debug. If what you're writing is small enough that it's done before you reach that point, it can still work - but obviously that doesn't make it good craftsmanship.
source: Me. It was me. I was "these beginners".
@TheCherno Release Notes v3.0: "Removed console for Windows users"
I think that the std::cout came from old versions with console and after the deactivation no alternative logging system was introduced.
24:55 preloading can be important, so I would rather query every system what resources they would need, pass that to the ResourceManager class.
A part two would be interesting!!!
it's VERY informative to see you run through this. Thank you.
If you do more. I will watch them.
I think those header guards are a default in Jetbrains Clion
Part 2 - definitely!!! This was so nice to follow
Nice, no windows doesn't require that you use h instance WinMain... Maybe with Windows universal platform but not with winrt... Weird things like this are still allowed to happen and somehow he hit every rung on the ladder down
It's actually disconcerting how much trouble it is just to create the solution files, which amount to IDE configuration files, instead of having an actual build configuration file, and the IDE working around that. Compiling software from source should not be this cumbersome: it should be plug and play, so to speak. I hope Zig takes off, like _properly_ takes off.
When I tried quite a while back, it also had some issues. I think Rust's Cargo works well, maybe to well given how many dependencies some Rust projects have.
What do you expect is cpp lmao everyone thinks everyone else knows how to build every project 😂
You should definitely do a Part 2!!
I don't think that you configure parallel build in cmake itself: it is building flag that you can specify (cmake --build number_of_jobs). Obviously, it can be solved by build section in readme, but still.
I definitely want a part 3 and 4 of this. Wait, what was the question?
Speaking of state machines - function pointers. Yum.
9:28
fzf users be like: Look What They Need to Mimic a Fraction of Our Power
fd
Yeah fzf is goat
wait till he discovers rg
Nah, everything is much faster. I am not sure if it is able to access some kind of internal journal/block from the NTFS filesystem or what, but it is able to index entire drives in seconds.
On the other hand, when I have used fzf it works great for small folders with maybe upto 100k files but if I try to use it on an entire drive with tens of millions of files, it takes a long time.
Fzf has other pros though like it is cross platform and more flexible and has customisable tui etc but in terms of speed everything wins.
When it comes to speed locate is the only thing on Linux that comes close to the speed of everything.
fzf just lets you interactively search in standard input, the listing is done via find(1). and let me tell you everything is much, much better than find, fdfind, or any other program linux currently provides.
The coupling is the most common mistake when working with medium-large teams and everything gets really hard to test
Please do a vid on state machine.
Also yes for the state machine and resource manager examples. You're doing God's work in terms of C++ and Software Development
That everything software uses fuzzy finder Algo. Ain't no way it's that fast without it
For a true old school retro game, the Linux terminal game ATC (Air Traffic Control) is pretty fun. Also Trek.
The singleton indirection the user setup allows for more flexibility on how, where and when the singleton is allocated and initialized. Side note: if the game code ever got moved into a dll, that pattern would make things even easier to port over and manage. Having a global variable represent the ResourceManager, the way Cherno describes, would make this a bit more (but not too bad) of a pain, tbh. I prefer the original code.. minus the locally static var of the manager in the Get() call.
6:09 That's a heck of a long build time even for C++. Nice time stamps, btw.
Lol you haven't seen nothing, my boy. ArchiCAD compiles for more than 3 hours, and it's already hacked together to have these precompiled mega-include files to avoid a billion separate includes.
*Summary*
* *(**0:00**)* *Project:* The Cherno reviews the C++ code of an air traffic control game called "Radar Contact" that uses real-world air traffic and weather data.
* *(**1:08**)* *Readme:* The readme is more of a game description/landing page and is missing information on how to build the project from source.
* *(**5:36**)* *Building Issues:* The Cherno encountered problems building the project, including a missing DLL and incorrect working directory settings. He showed how to fix them but noted they should be addressed in the project.
* *(**12:22**)* *Game Play:* The game itself is functional but has some usability issues: a small, non-resizable window and unintuitive controls. An on-screen UI with control explanations and plane information would improve the experience.
* *(**14:35**)* *Code Style:* The Cherno dislikes the separation of header and source files into different directories. He prefers a more unified directory structure.
* *(**19:25**)* *Singletons:* The excessive use of Singletons is criticized. The Cherno suggests more standard C++ approaches would be better.
* *(**22:28**)* *Resource Manager:* The resource manager is heavily coupled to the game's specifics and is doing too much beyond asset loading. The Cherno recommends a more generic, data-driven approach.
* *(**29:20**)* *State Machine:* The game uses a "state machine" but it's implemented as a stack of windows rather than a more traditional state machine pattern.
* *(**31:35**)* *Tight Coupling:* Overall, the code suffers from tight coupling - game-specific data and logic are intertwined within systems that could be more generic and reusable.
*The Cherno suggests a Part 2 to delve into networking and other interesting parts of the code. He encourages viewers to comment on their thoughts and whether they want to see a continuation.*
Summarized by AI model: gemini-1.5-pro-exp-0801
Cost (if I didn't use the free tier): $0.1053
Input tokens: 27747
Output tokens: 781
We want State Machine Video!
About console, I do not know how on windows, but usually you will get cout data when runs from console
I just wrote an asset system for my engine. I need to look it over a cleanup the code but the way I wrote it I scan the assets directory at startup to check for modifications while the editor was closed then efsw takes over file watching while the editor is running. I store the asset's metadata in an SQLite database in the project's root directory and I can load the assets on request. For a second pass of this system will be adding an async load function and I still have a few edge cases to deal with.
18:25 i think a check for windows builds with a MessageBoxA call would do the trick
Complaining about header guards pog!
Do you know what the singleton pattern is actually good for? What if g_whatever needs something initialised in main (or the lame WinMain 🤦) or if it needs the other g_whatever2?. How do you ensure the order of the g_ constructors?
The alternative to singleton is not a global variable, but dependency injection.
The build steps were actually pretty straightforward. I've seen much, much worse and I can always get it to work.
In my opinion if you can't figure out how to run cmake and install a few dependencies when they're missing, then you have nothing to do in my codebase.
Call it gatekeeping, I don't think I'm unreasonable.
In the industry we just call this guys StateMachine a GameFlow.
Hey Cherno, I've been watching your code review playlist, and I noticed you didn't do this yet. So, what about reviewing open source engines made in C++ like Godot? It would be a good opportunity because it's supposed to be professionally made and properly documented. In the case of Godot, it has the node architecture that's different from the Unity-inspired engines people like to make. Could be fun to look at it under the hood.
Also, maybe you did in another series I don't know about. Sorry in advance if that's the case
Would love to see state system example
What color theme is this?
Part 2 might be in Linux 🫡
Does the game freeze when dragging the window? Also was wondering the same for Hazel 2D (because on Windows, poll events doesn't return while dragging or resizing the window, blocking the main loop)
It's a cool idea for a project. Wish I had thought of it.
Resource Manager video follow up would be awesome!
It’s a mistake to start writing a big project immediately and never give up on it: we all write bad code at the beginning of our programming journey. It’s not possible to progress without doing it:)
It’s clear that this guy doesn’t have much experience yet and maintaining this project might get harder and harder, given not the best architecture
P.s. I’d rather suggest to write a first game, not in c++, but Java/C#, because you would be able to focus more on the actual design, than on low level things, which increase cognitive complexity
it's very informative and how can we learn everything cherno is saying about the production like environment?
TheCherno, what do you think about using VCPKG for third party library dependencies and such? For my project, I use it in the manifest mode where I can specify a particular version for each library, which is nice. It requires the code reviewer to clone down VCPKG once, and run 2 onetime bootstrap-type commands and then its all just automatic.
33:10 - Factory Method \ Factory Class Pattern can by proposed here to use... without employing Singletones...
I often use singletons for dependency injection. This way I can inject a Datababe into my classes and swap them for testing purposes.
I would really like to see sth. about the Statemachine, because I think it's a really usefull pattern. Especially in modern C++
You inject a Datababe? Is that safe for work?
@@not_ever now that I tkink about it, it seems we have a very special customer base.
28:20 - this! I don't know how often I see code constructing path names as strings... Leading to all sorts of problems especially on Windows machines because those people typically also don't know that you _can_ use slash instead of backslash on Windows also (welcome to backslash hell...). Nowadays all languages should have libraries dealing with file system paths. Use them!
bro has infinite computer mana
thank you The Cherno, the video is very informative.
alt should be altitude
easy to member
The biggest point here is that strings don't have meaning. Sooooo many applications try to give strings some kind of meaning. C++ is about defining types. Types are abstractions of meaning. You know what a string is? A series of character. Nothing else. If your "string" has any more importance than being a series of characters then make it a type. There's a reason a lot of languages have a "Path" type. Are they just strings under the hood? Sure, but the meaning is now encapsulated into a type/class that can save you from using it incorrectly, or to add additional functionality that makes sense for that type. Ex: "Path p = Path::CurrentDirectory(); p.append(".//\directory");"
Having the ResourceManager handle regions is really weird. Using Windows as states is also weird. Also seems to suffer from Javaitis (aka "everything is a class"). C++ is not in the Kingdom of Nouns! Functions can be top-level declarations!
Why is your pullover talking...? It's mesmerizing! 25:05 😆
I hit play on this video and then ten seconds later Facebook added you to my friend recommendations so if anyone needs me I’ll be fashioning headgear from metallic kitchen supplies
22:08 - it is more 'C language' approach - rather than 'C++ language' to use global variables :D global-level singletons presentation is useful when the proper design is used with service interface (pure abstract class) splitting from various implementations... where e.g. run configuration with E2E\UnitTest can run with one Singleton implementation .. and actual game run with another... lazy load - also can be due to some other reasons - e.g. to show some progress bar window first - before blocking user with heavy resources loading....
I think that format is cool. But i think you should be bit less picky when it comes to code style. I wouldn't be so picky of what style is used rather if it is followed consinstently. (In most environment this is defined by the organisation not by the devs working on a live project.) What i would rather suggest if it is missing add some kind of code anlaysis like linters to enforce coding styles.
This video is a fantastic demo of why Cmake is not very good :)
Thanks for Everything.
Really nice tool :)
Please continue this!
So to make sure I understand it correctly, you prefer a file called source in it there are two files one for headers aka public and one for cpp aka private am I correct❓ and where exactly should I put main.cpp
Hey there! Do I contact you via business review if I want you to review my code? And how complete does a project have to be?
Is one of the uses of the game that you can have multiple regions active at once? Could that explain the window situation?
Many IDE's such as CLion uses #ifndef header guards by default
When i think that i write shitty code and i see that people with higher education than me are writing much worse code... I'm getting relieved that AI will not be enough for them : )
which extension are you using cherno for different colour for different components of code titles?
It looks like a remake of Kennedy Approach.
Curious on why singleton is bad in c++. Could you expand on that?
When I started to watch, I was annoyed by your voice. But holy shit I'm agreeing with everything so far (at 21:06 currently), so you got my support. I also learned about VS, though I don't plan to use it ever again, but still, useful.