[HN Gopher] Making games in Go for absolute beginners
       ___________________________________________________________________
        
       Making games in Go for absolute beginners
        
       Author : ingve
       Score  : 309 points
       Date   : 2023-11-24 12:04 UTC (1 days ago)
        
 (HTM) web link (threedots.tech)
 (TXT) w3m dump (threedots.tech)
        
       | kubanczyk wrote:
       | Based on ebitengine.
       | 
       | To fix: a hanging sentence "Here's how an example Sprite
       | component can look like"
        
         | m110 wrote:
         | Oops, that was a leftover! Thank you :)
        
           | kubanczyk wrote:
           | Good luck Milosz, I'm a fan!
        
       | NAHWheatCracker wrote:
       | I've been working on a game over the past year in Go using
       | https://github.com/g3n/engine. I picked Go because I like the
       | language and wanted to learn it. I picked g3n-engine because I
       | wanted to work in 3d after making a few 2d games in the past.
       | 
       | Making games is so much more challenging and rewarding than
       | almost all of the work I've done for pay. There's always so much
       | more to learn that doesn't feel like just relearning how to do
       | the same thing except with a different framework of the week.
        
         | mrintegrity wrote:
         | This is great, the dificulty goes up rapidly. I only got to
         | level 3 before quickly dying (playing on mobile Firefox)
        
         | satellite2 wrote:
         | It's really a nice project. It's really too bad that it doesn't
         | compile for mobiles.
         | 
         | It's the one point preventing large adoption of go as a gaming
         | language I think. I haven't found a 3d lib that supports
         | building for mobiles.
         | 
         | And it's really too bad, because go would be perfect for
         | writing games.
        
       | uka wrote:
       | Doing a game with ebitengine myself. Refreshingly nice
       | experience.
        
       | gigafuture wrote:
       | Great tutorial. I really enjoyed it and saved it for later.
        
       | dimgl wrote:
       | I always wondered if Go would be a good language to make a game
       | in due to its concept of goroutines. Maybe one of these days I'll
       | give it a shot.
        
         | cdogl wrote:
         | Unfortunately, garbage-collected languages have historically
         | not been great for real-time action games. Throughput and
         | performance can be excellent, but without control over the
         | garbage collector pauses can wreck the experience.
         | 
         | For any game that doesn't require smooth frames for the
         | experience, the best language is the one you're most
         | comfortable with (modulo some base level of game dev community)
         | 
         | Edit: on the other hand, Go is a superb candidate for a back-
         | end game server. I'd say the same goes for the network client
         | layer, but the language interop story is not ideal.
        
           | pjmlp wrote:
           | Didn't stop Notch to become rich, even with some hiccups.
        
             | anthk wrote:
             | Also lots of games have been written in C#/XNA.
        
               | pjmlp wrote:
               | Indeed, just recently I discovered that Capcom has their
               | own .NET fork, and used it on Devil May Cry for the
               | PlayStation.
               | 
               | Check their developer channel on YouTube.
        
           | beautron wrote:
           | I'm making a game in Go, and it runs perfectly smooth on my
           | 240hz monitor (and should run perfectly smooth on 1000hz
           | monitors once we get there).
           | 
           | The garbage collector has not been an issue, because I use
           | memory the same way as if I were doing it in the C language:
           | Allocate all memory used by the game at program launch. I
           | find this scheme easier to work with in Go than in C, because
           | Go's slice type is a natural fit for partitioning these
           | launch-allocated blocks of memory (as they get used and
           | reused during the game).
           | 
           | The garbage collector doesn't get called if you don't
           | allocate. And for the most part1, it's easy to reason about
           | where Go allocates (if you know C). So my rule is: Always
           | know where your memory is; never allocate.
           | 
           | This may sound like it's going against the grain of Go, but I
           | find Go to be handily amenable to this style. I had my
           | concerns going in, but it just hasn't been a problem. My game
           | runs silky smooth.
           | 
           | At this point, my only concern about using Go to make a game
           | is that bringing the game to consoles does not have a well-
           | trodden path.
           | 
           | 1 So far, the only thing that has surprised me is that
           | assigning a value type (like an int) to an interface
           | allocates! (So another rule is to only assign pointers to
           | interfaces [pointers to memory allocated at launch].) And
           | while this was a surprise to me, I was pleased with how quick
           | the debugging went: I noticed frames were dropping, so I ran
           | go's pprof tool, which led me directly to the (freshly coded)
           | interface assignment that was stealth allocating. (Also, this
           | allocation becomes less surprising the more I think about how
           | interfaces are implemented.)
        
             | Xeoncross wrote:
             | Thank you for sharing, I've never considered allocating the
             | memory for an entire Go program at launch.
             | 
             | One neat thing that Go has is built-in benchmarking (part
             | of the built-in test package) which shows you allocations
             | per-invocation making it easy to spot the functions which
             | need more work.
        
             | nappy-doo wrote:
             | Side note: Go's GC will still run without allocations,
             | typically every 10 minutes. But, in your case, it might
             | never find memory to collect. If you truly want to disable
             | the GC, use SetGCPercent or GOGC.
        
           | klabb3 wrote:
           | > Unfortunately, garbage-collected languages have
           | historically not been great for real-time action games.
           | 
           | I've heard this a lot and even parroted it myself, but in all
           | honesty I would be surprised if this is true today. First, GC
           | does not mean "no control at all" over GC pauses. Secondly,
           | GC pauses are incredibly short in some languages, unless you
           | have to deallocate a lot.
           | 
           | Without first-hand experience, I would assume that GC is not
           | an issue for games today, given that you are aware of how
           | allocations work, and you have access to a good profiler if
           | necessary.
           | 
           | As usual, the best language is most likely the one with the
           | best ecosystem (libs, tooling, docs) around the domain that
           | you're working in.
        
           | d-z-m wrote:
           | Much work has gone into making Go GC pauses as quick as
           | possible, so it might be in a better position relative to
           | other GC'd languages.
           | 
           | Also, you might be able to toggle the GC off, and invoke
           | manually with runtime.GC() at opportune moments.
        
         | leetrout wrote:
         | The way you have to do math with vectors without operator
         | overloading available in Go is quite cumbersome.
         | 
         | I am a huge Go fan and I figured this out working on the
         | raytracer in a weekend project using Go.
        
         | angra_mainyu wrote:
         | It's extremely good.
         | 
         | Goroutines and channels make it very straightforward to set up
         | an ECS engine hence you can actually run a lot of the engine in
         | parallel and not have to stick to the "game loop" structure.
         | Build your systems similar to the actor model with a mailbox,
         | have an event/messaging bus, and you basically have a very
         | performant engine.
         | 
         | I've yet to run into issues with the GC. The only bit of tuning
         | I had to do was be careful with interfaces in hot paths and
         | batch all graphics calls.
        
           | tslocum wrote:
           | Speaking of, I created an ECS framework for use with
           | Ebitengine:
           | 
           | https://code.rocket9labs.com/tslocum/gohan
        
       | asda22sdasd wrote:
       | While ebiten is fun, it absolutely will be limiting for your
       | larger project.
       | 
       | Consider options and research properly before choosing tech.
       | 
       | In the case of ebiten + golang, you will suffer greatly from the
       | FFI overhead when calling external graphics API:s.
        
         | nehal3m wrote:
         | Beware the factory factory factory.
         | 
         | https://factoryfactoryfactory.net/
        
           | endofreach wrote:
           | as much as i hate the overhead of frameworks & all the
           | tooling that is (seemingly) required for simple things
           | nowadays, the analogy that is attempted to be made, doesn't
           | really fit well. The author seems to have been too focused on
           | writing a "classic analogy" that has it's own URL rather than
           | writing a good & fitting one for the case he/she dislikes -
           | and especially, putting real thought into the analogy to make
           | it a really good one.
        
             | LegibleCrimson wrote:
             | > The author seems to have been too focused on writing a
             | "classic analogy" that has it's own URL
             | 
             | That doesn't track, given the sources at the beginning. One
             | person wrote the essay, then somebody else started hosting
             | it at that URL. There doesn't seem to be a direct
             | connection between the writer and the URL at all.
        
         | Thaxll wrote:
         | This is not true, for 2d game the performance of Ebiten is
         | good, you don't suffer from ffi.
         | 
         | Some games have been shiped on that engine, even running on the
         | Nintendo switch.
        
           | JoeyJoJoJr wrote:
           | At least from my own tests, I also found Ebiten _extremely_
           | smooth and performant when building for the WASM target.
           | There seems to be some people spouting a bit of nonsense in
           | this thread without having actually given Ebiten a chance.
        
       | nikki93 wrote:
       | Nice! Ebiten is a super nice API for Go. Lots there to be
       | inspired by in API design. Another API I like a lot is Love for
       | Lua (which also actually can be used from C++).
       | 
       | Re: the comments on here about the GC etc. -- I've posted about
       | this a couple times before but I've been using a custom Go
       | (subset / some changes) -> C++ compiler for hobby gamedev, which
       | helps with perf, gives access to C/C++ APIs (I've been using
       | Raylib and physics engines etc.) and also especially has good
       | perf in WebAssembly. Another nice thing is you can add in some
       | reflection / metaprogramming stuff for eg. serializing structs or
       | inspector UI for game entity properties. I was briefly
       | experimenting with generating GLSL from Go code too so you can
       | write shaders in Go and pass data to them with shared structs
       | etc.
       | 
       | The compiler: https://github.com/nikki93/gx (it uses the Go
       | parser and semantic analysis phases from the standard library so
       | all it has to do is output C++) (it's a subset / different from
       | regular Go -- no coroutines, no GC; just supporting what I've
       | needed in practice -- typechecks as regular Go so editor
       | autocomplete works etc.)
       | 
       | A game made with it a while ago for raylib game jam along with
       | the raylib bindings and other engine stuff:
       | https://github.com/nikki93/raylib-5k
       | 
       | Very old video of a longer term game project I'm using it on
       | which also shows state-preserving reload on rebuilds and also the
       | editor inspector UI: https://youtu.be/8He97Sl9iy0?si=IJaO0wegyu-
       | nzDRm (you can see it reload preserving state across null pointer
       | crashes too...)
        
       | tjpnz wrote:
       | Is size still an issue with Go if you wanted to compile to WASM
       | and target the browser?
        
         | pjmlp wrote:
         | If the browser is the main target, I would just use browser
         | native engines like Pixijs.
        
           | angra_mainyu wrote:
           | shout out to babylon.js, incredibly good 3d game engine for
           | the browser
        
             | pjmlp wrote:
             | Definitely, my example was for 2D games.
             | 
             | Currently the one with best WebGPU support.
        
               | Narishma wrote:
               | What would a 2D game need WebGPU for?
        
               | pjmlp wrote:
               | BabylonJS is not for 2D games, read the thread properly.
        
           | JoeyJoJoJr wrote:
           | I would recommend against anyone using PixiJS. From a code
           | perspective it feels dated and inconsistent. It can be very
           | difficult finding correct information for the latest version,
           | and you will very easily hit performance problems if you want
           | to create a full screen game. You will need to apply the
           | bitmap caching feature to yield best performance, and to me
           | it seemed riddled with rendering and interaction bugs, or at
           | the very least highly unintuitive behaviour from its usage. I
           | didn't discover these problems until I was too far into
           | development of my game.
        
             | pjmlp wrote:
             | And the alternative 2D game engine designed for the Web
             | with GPU acceleration is?
        
               | JoeyJoJoJr wrote:
               | It's not ideal, but I'd go with something non-JavaScript.
               | Heaps or Kha in the Haxe ecosystem would be a far more
               | worthwhile choice IMO.
        
         | euroderf wrote:
         | There's tinygo, which is still missing bits here & there,
         | including reflection, which makes encoding/decoding (json, xml)
         | problematic.
        
         | bdhcuidbebe wrote:
         | There has been work to get ebiten running with tinygo, which
         | will yield smaller and maybe faster wasm. Not sure about
         | status, just try yourself with tinygo command instead of go.
        
         | tslocum wrote:
         | Boxcars[0], a web-based client for playing backgammon powered
         | by Ebitengine, compiles to a 20MB WASM binary[1], and as long
         | as the server and browser are gzip-enabled, this actual
         | transfer size is reduced to a little under a third of this.
         | 
         | 0. https://code.rocketnine.space/tslocum/boxcars
         | 
         | 1. https://play.bgammon.org
        
       | angra_mainyu wrote:
       | I've used raylib-go (raylib bindings for Go) and ebiten. Both are
       | excellent libraries.
       | 
       | Personally I tend to use raylib-go much more, as it can readily
       | supports 3D and has a strong community around it.
        
       | Kiro wrote:
       | This is great. I feel it's missing something about delta time
       | though.
        
         | m110 wrote:
         | Thanks! I had a paragraph about delta time but since Ebitengine
         | works based on the TPS, I eventually decided to drop it to not
         | make it confusing.
         | 
         | Here's a good summary, I think I'll link it in my post:
         | https://ebitencookbook.vercel.app/blog/2022/04/16/TPS
        
       | susam wrote:
       | While writing games in any programming language is a very
       | rewarding experience, I have found HTML5 Canvas and JavaScript to
       | be very effective for writing games as absolute beginners too. If
       | sound is needed, there is Web Audio too, all available right
       | within the web browser.
       | 
       | It is quite possible to write simple games without using any
       | external JS libraries by sticking to first principles only, e.g.,
       | drawing simple shapes with fillRect(), simple collision detection
       | algorithms, generating simple tones using OscillatorNode, etc.
       | Here is one such game I wrote sometime back:
       | https://susam.net/invaders.html
       | 
       | Also, if the entire game is written as a single HTML file, the
       | game becomes immediately distributable too. All one needs to do
       | is host the HTML file somewhere or send the file to someone in
       | order to share the game.
        
         | klabb3 wrote:
         | Seconded! Especially if you only have keyboard controls. Once
         | you have mouse events there is no mapping between mouse
         | position and an object in game, which makes it a bit harder. If
         | so, you may wanna use svg. It makes it easy to handle mouse
         | events, rendering is "free" and you can also do smooth
         | animations with CSS. The main downside imo is that svg has more
         | browser inconsistencies for some reason.
         | 
         | Example: https://loz.payload.app/
        
         | marcosdumay wrote:
         | The web is becoming a really good OS.
         | 
         | We just need make web assembly better for higher level
         | languages, because neither javascript not rust are an end-all
         | great language for small games.
        
       | capl wrote:
       | I am a fan of Go due to the focus on simplicity in the
       | language... but why Go for game dev? The purpose of the language
       | was to tackle backend services previously written in C++ focusing
       | on junior developers, not game dev.
       | 
       | It's not really a popular tool for the job: Python, which is a
       | simpler language, has a much larger community of gamedev noobs.
       | Or maybe better yet: start with Godot and GDScript
        
         | susam wrote:
         | > ... but why Go for game dev? The purpose of the language was
         | to tackle backend services previously written in C++ focusing
         | on junior developers, not game dev.
         | 
         | I'd say using any language for game development should be
         | embraced, not discouraged.
         | 
         | Imagine if William Crowther had thought, "Why Fortran for game
         | development? The purpose of the language is to tackle
         | scientific computing." Thankfully, he did not think like that.
         | 
         | All Crowther had with him was PDP-10, Fortran, a teletype that
         | probably looked like
         | https://en.wikipedia.org/wiki/File:ASR-33_at_CHM.agr.jpg, some
         | sheets of paper, and a sense of fun! Without being bothered
         | about what these tools were really meant for, he went ahead and
         | developed Colossal Cave Adventure thereby introducing the genre
         | of text-based adventure games into the field of computing. A
         | player had to type their input into the teletypewriter and wait
         | for the computer to print the output on, well, physical paper!
         | Still it was fun. It is nice to have fun and write games with
         | whatever technology we have got.
        
         | veqq wrote:
         | > Python, which is a simpler language
         | 
         | This seems crazy to me. How so?
        
           | superkuh wrote:
           | If you never use anyone elses packages and only stick to
           | vanilla python available on your system it's a great learning
           | language. Many universities still use it to teach programming
           | today. But this is mostly inertia...
           | 
           | ...because python has changed in the last decade so that
           | anything ever so slightly complex requires learning all it's
           | dependency manager manager setups (conda, pyenv, etc) to set
           | up an entire custom python implementation with it's own
           | dependency manager (pip, whatever, etc) just to be able to
           | run a single python application or use a python lib. Trying
           | to do it with the actual python your system runs will always
           | end in tragedy.
           | 
           | So yeah... Python is a simple language. But using python
           | these days is so complex I actually chose C++ over it to
           | _avoid_ complexity. And that 's really saying something. That
           | said, there's no such thing as system 'go' at all so it
           | brings in plenty of complexity too despite being 'simple' in
           | it's own way.
        
         | llm_nerd wrote:
         | Using the origin-story* of a language to argue against what the
         | language can be used for _now_ is never a good argument.
         | 
         | Go is a powerful, accessible language. It yields highly
         | optimized, tight binary output targeting a wide variety of
         | platforms and systems. Why _not_ Go?
         | 
         | Python is basically a non-starter in this space. Your other
         | example is a scripting language _inside_ an engine. These seem
         | orthogonal.
         | 
         | * - Which is oft heavily editorialized and fictitious, such as
         | in this case
        
       | appstorelottery wrote:
       | I tend to write fireworks or simple particle simulations in a new
       | language / graphics api to test for performance as my first step
       | (aka learning how to place a pixel on the screen, double buffer
       | or whatever, do some simple logic / math, and learn the
       | compilation pipeline). It's always a fun task and an easy
       | benchmark: how many pixels can I move per frame?
       | 
       | I did this in 2021 in GO and Ebitengine and I was blown away by
       | the performance the screen was mostly white with pixels, how easy
       | it was to get the context running, and how many pixels I could
       | move on the screen at 60fps... I was truly impressed. I've been
       | using this approach for the last 30 years I guess ;-) We're a
       | long way from moving a few hundred pixels on a 16 bit CPU per
       | frame... _greybeard out_
        
         | giancarlostoro wrote:
         | I always do a web project because it typically shows you
         | language features that you wont see in "Hello World". It also
         | shows me what the database drivers look like, if its painful to
         | setup, and all that fun stuff.
        
           | mamcx wrote:
           | I do the same, but different kinds of projects exercise
           | different stacks.
           | 
           | A web project does not prepare you for a bad UI history, for
           | example...
        
         | JoeyJoJoJr wrote:
         | I also created a performance test with Ebitengine, like I have
         | done for many 2d engines/rendering libraries in the past.
         | Typically I like to test performance with the web/WASM target.
         | More often than not, even just a few rotating sprites will
         | result in some dropped frames. I was extremely surprised at
         | just how insanely smooth Ebitengine performed. For my purposes
         | it was the second most performant engine/rendering library,
         | second only to Kha. Being new to Go I was also blown away how
         | simple it was to just copy memory around and create Braid-like
         | time travel, as well as the exceptionally simple build process.
         | Rust and Bevy get a lot of mentions on Hacker News, but I think
         | Go and Ebitengine will be my next choice to build my next game.
        
       | tslocum wrote:
       | I have been a happy user of Ebitengine for several years. If you
       | are interested in seeing the source of some games created with
       | it, check out the awesome-ebitengine list.
       | 
       | https://github.com/sedyh/awesome-ebitengine
       | 
       | All of the games listed on my itch.io profile were created using
       | Ebitengine, and they are all open source.
       | 
       | https://rocketnine.itch.io
        
       | rTX5CMRXIfFG wrote:
       | I'm going to react just to the first part of the article since
       | the rest isn't really of interest to me.
       | 
       | If you're experiencing the kind of burnout that the author
       | described, I'd suggest an alternative to doubling down and
       | "reclaiming" your hobby: make your interests evolve. It could
       | very well be your body (specifically your brain) telling you that
       | you're stagnating. It doesn't have to be different getting
       | programming, but perhaps you've reached a point that it is time
       | to go deeper, possibly into CS theory.
        
       | its_kenta wrote:
       | Not a Go dev, but a long time viewer of one YouTuber/Twitch
       | streamer that makes his games in Go for a long while now. He's
       | creating a small MMO in Go which you can test out here:
       | https://mythfall.com
       | 
       | UnitOfTime is his YT & Twitch handle
       | https://www.youtube.com/@UnitOfTimeYT Uses his own binding to
       | OpenGL and WASM to WebGL2 for Web version.
       | 
       | Used to use Go myself, but fell out with it. But I can understand
       | why people would enjoy making games with it. Just like SolarLune
       | himself uses Golang for his gamedev. A lot of people seem to ask
       | the same question tho: "Why make it in Go? Doesn't it have GC?"
       | From my understanding Go's GC is quite robust as well.
       | 
       | In a nutshell, nice to see Go having more usage outside the norm
       | that devs expect it to only be used in.
        
       | Seb-C wrote:
       | Nice to see such a topic come up.
       | 
       | I am the developer of Astral Divide, which is entirely written in
       | Go: https://store.steampowered.com/app/2597060/Astral_Divide/
       | 
       | When I started two years ago, I basically had to choose a
       | language and a package/framework, so I'd like to share my
       | experience about it.
       | 
       | First, about the package, there are two major ones for 2D games,
       | which are Ebiten(gine) and Pixel.
       | 
       | I initially chose Pixel, because it seemed more documented at the
       | time and had a bigger community. Overall, it was good as long as
       | the game was simple, but unfortunately I regretted this choice
       | later.
       | 
       | Don't get me wrong, I am not here to discredit the author,
       | because there is a phenomenal amount of open source work that was
       | done, and I am thankful for it. But as my game grew more complex,
       | the engine started to cause big architectural challenges, memory
       | leaks (it does not correctly garbage collect OpenGL's objects for
       | example), and it's multi-threading model is flawed. I also
       | discovered that it was in fact not maintained. Overall it lacks
       | too much maturity for a serious game.
       | 
       | In the end, I decided to ditch it, because it was easier, and my
       | proposals for help on GitHub never got any answer. It took some
       | effort, but having my own engine based on OpenGL was the best
       | choice in this case.
       | 
       | When it comes to Ebiten, I have only used the sound library
       | called Oto, but overall my opinion of the whole package now is
       | that it is well maintained and architected.
       | 
       | Now when it comes to choosing a language, I hesitated between:
       | 
       | - Go, which I had a lot of experience with and seemed good
       | enough, even if the typing system is a bit too weak
       | 
       | - Rust, which I was (and still is) interested in, but had no
       | experience, and unsure if it was worth it in terms of
       | productivity (because of the additional time spent on worrying
       | code, and the compilation time itself). Also, it didn't have game
       | engines which seemed as mature as in Go at this time (I wish I
       | had known that I would end-up with my own engine).
       | 
       | - C++, which is the industry standard, but on which I have very
       | limited experience. Also, I just didn't want to bother with the
       | header files and the compilation stack.
       | 
       | I chose Go, and too be honest it is a choice that I now regret,
       | but also I have to live with it because I cannot afford a rewrite
       | at this point.
       | 
       | Go certainly had a lot of productivity benefits, but also major
       | flaws when it comes to game development:
       | 
       | - The lack of packages related to data structures. In games, we
       | need a lot of specific sets, trees, queues, tries and maps, but
       | those are almost non existent in Go. The best one is "gods",
       | which still does not support generics. Overall it is always
       | difficult to find quality and experienced packages, even for
       | broadly used and fundamental algorithms.
       | 
       | - The fact that dependency cycles are handled on a package basis,
       | and not per-class or per-file. This is a major source of
       | headaches for video games, because contrarily to web servers,
       | there are not a limited number of predefined layers: everything
       | needs to depend on almost everything else, and Go makes it even
       | worse in this case.
       | 
       | - The lack of data structures is made even worse by the native
       | map, because it forcefully randomizes the iteration order, which
       | causes a lot of bugs and makes it unusable 90% of the time. The
       | authors wanted to prevent people from using it as if the key
       | order was guaranteed, but their fix also broke the natural
       | consistency of iteration order. This means for example that the
       | drawing order of objects is different every frame, even when the
       | data didn't change at all.
       | 
       | - When it comes to modding, there are not much options, because
       | Go does not have a very easy or natural compatibility with FFI
       | (cgo is awkward to use, and not really usable with dynamic
       | linking), the native plugin package is experimental (and not even
       | working in windows), and the support for WebAssembly is still
       | quite poor (but getting better).
        
       | mortallywounded wrote:
       | I love Go and have been thinking of using it for a game project,
       | but I can't help but feel like the web itself is a far better
       | stack.
       | 
       | The web browser gives you a rich collection of things you need
       | out of the box and fairly "cross platform" by nature. You get
       | things like graphics, audio, networking, debugging utilities,
       | access to webgl / webgpu, etc.
       | 
       | As much as I hate to admit it.. it seems the web is far more
       | suited for it than Go. I'm sure the game dev purists out there
       | are pointing and laughing at the idea though :)
        
       | atleastoptimal wrote:
       | The question is: is Go a good platform to write big games in? Are
       | there enough libraries and performance to justify using it over
       | C#?
        
       ___________________________________________________________________
       (page generated 2023-11-25 23:01 UTC)