C#, Angband and Unity

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Dean Anderson
    Adept
    • Nov 2009
    • 193

    C#, Angband and Unity

    I've been looking around and Googling, and I'm surprised at the lack of Angband ports to C# or Java or other languages. There's a half-finished C# port on Google Code, and I've found a few discussions about Java ports but nothing substantial.

    So I'm currently working on an experimental C# port of the Cthangband code myself.

    What I'm basically doing is this:

    1) Take a snapshot of the Cthangband 5.0.0 code (I've done this - it's a simple copy/paste operation!).

    2) Strip out all the horrible hacks that make it work on multiple platforms - all the #ifdef business - so we have a simpler codebase which contains only the code that's actually compiled into the Windows Console version. (I've done that, and it compiles and works as normal.)

    3) Port the code from C to C#, as-is. (I'm in the process of doing this. Obviously I can't keep it completely as-is, since the file handling, memory management and console code are rather different in C# to C, but I've completed those "tricky" bits now.)

    4) Once I have a working C# port, refactor the code to make it properly object oriented. (I'm trying to avoid any of this until I've got the basic port in #3 working, but I'm having to do little bits in order to get the terminal stuff to work - for example instead of using a load of function hooks, the 'term' functions are now in an abstract class which is inherited by the specific 'ConsoleTerm' class containing the Console implementation of a term.)

    5) Once I have a fully object oriented C# version of Cthangband running, I want to swap out the Console display for something flashier. I was thinking that Unity would be good because it's nicely portable and handles C# code natively.
  • Carnivean
    Knight
    • Sep 2013
    • 527

    #2
    Have you checked out the work that the Vanilla maintainers and the NPPAngband crew have been doing? They've taken different approaches to splitting the UI from the engine and refactoring the codebase to simplify thing, and in the case of NPP have a working GUI. You might find you only need to reinvent 1/2 the wheel.

    Comment

    • Derakon
      Prophet
      • Dec 2009
      • 9022

      #3
      "Rewrite Angband in a nicer language" is basically what you're talking about here. In addition to the 4.0 efforts, I also recommend you look at Pyrel, which is a Python-based rewrite with a radically different design aiming for high extensibility and flexibility. Unfortunately Pyrel's on indefinite hiatus since I'm busy with half a billion other things. Sorry, Magnate and d_m and everyone else who contributed to it.

      Comment

      • Dean Anderson
        Adept
        • Nov 2009
        • 193

        #4
        I haven't actually seen their Qt port in action, but I'm aware of it.

        In my case, the thought process wasn't:

        I want a new UI
        => Unity looks good
        => Unity needs C#
        => Therefore I need to port it to C# in order for it to work

        The thought process was actually:

        I've been a professional C# developer since the language came out and the company I work for switched to .NET
        => So why the **** am I messing around doing this in plain old C
        => I should totally convert Cthangband to C# for my own enjoyment/ease of use
        => Cool
        => ...
        => ...
        => Hmmm... I could probably do a graphical front end at some point. I wonder what 2D graphics engines are available for C#?
        => Unity looks good. And it's portable too!

        So while the Qt front end sounds interesting, for me it's the C# port that's the important bit, not the addition of a front end. Also, the 3.x based code that the current dev team are working on is very different from the Zangband 2.1.1-based code that Cthangband is built on - so they're starting from a very different place. Just converting Cthangband to use the new Angband code would be a major task in itself, without doing any porting.

        Comment

        • Dean Anderson
          Adept
          • Nov 2009
          • 193

          #5
          Originally posted by Derakon
          "Rewrite Angband in a nicer language" is basically what you're talking about here. In addition to the 4.0 efforts, I also recommend you look at Pyrel, which is a Python-based rewrite with a radically different design aiming for high extensibility and flexibility. Unfortunately Pyrel's on indefinite hiatus since I'm busy with half a billion other things. Sorry, Magnate and d_m and everyone else who contributed to it.
          Interesting - although I don't know Python from a hole in the ground!

          Comment

          • Derakon
            Prophet
            • Dec 2009
            • 9022

            #6
            Originally posted by Dean Anderson
            Interesting - although I don't know Python from a hole in the ground!
            It has a fairly straightforward syntax, so it's not hard to learn once you wrap your head around the fact that whitespace is significant to the parser. Even if you don't want to work in Python, though, I think its design approach is worth imitating. I put a lot of thought into how to set up a system that could easily be extended without needing to know a lot about the code.

            Comment

            • AnonymousHero
              Veteran
              • Jun 2007
              • 1393

              #7
              The difficulty here isn't so much that it's difficult per se. It's just that it's an enormous amount of sustained effort to convert, say, 200k-300k lines of old-school C to a modern language. Many of the idioms of C aren't even available in $LANGUAGE. (Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)

              These things can be overcome with enough effort, but I think the temptation to start changing things as you're porting has claimed many victims...

              I sincerely wish you the best of luck, and AFAIK you'll be the first to succeed, if you do succeed.

              EDIT: FWIW, I do think your general approach is sensible: Strip down to the bare minimums before starting to port, resist at all cost the temptation to redesign while porting, refactor post-porting -- hopefully in a friendlier language!

              Comment

              • Dean Anderson
                Adept
                • Nov 2009
                • 193

                #8
                Originally posted by AnonymousHero
                The difficulty here isn't so much that it's difficult per se. It's just that it's an enormous amount of sustained effort to convert, say, 200k-300k lines of old-school C to a modern language. Many of the idioms of C aren't even available in $LANGUAGE. (Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)
                Most of the code isn't a big problem when conmverting from C to C#, since the basic syntax is the same.

                The main differences are that most of the stuff in Angband that throws around pointers to structs needs to be changed to throw around references to classes instead.

                Unfortunately, the bits that require the most alteration are:

                1) The terminal/display code (main-xxx.c/Z-term.c)
                2) The code for reading/writing files (init1.c/init2.c/files/c)
                3) The code for initialising the arrays of structs/objects (variable.c/tables.c)

                The reason this is unfortunate is that these are the bits that needed converting first. Converting the LOS code or the code for when someone goes up a level can wait until we're at a point where we need those functions - in other words they can wait until we've got a skeleton program running and starting a game. But those basics need to be run as the program initialises and sets itself up.

                My current progress is that I've fully converted the files listed above, and I'm now at the point where I can compile and run the program and it will do all its initialising and be ready to jump into character creation.

                These things can be overcome with enough effort, but I think the temptation to start changing things as you're porting has claimed many victims...
                I think the biggest issue there is that because some things have to be changed, it's tempting to change more "while I'm here".

                For example, tables.c needed heavily re-writing because you don't initialise big arrays of objects in C# the same way that you do in C. So as well as simply converting the various structs to classes, I also had to write constructors for them so that I could use those constructors to create the big arrays.

                It would be very tempting to start refactoring other bits of the program as I get to them by trying to convert static functions into class methods on those classes.

                I sincerely wish you the best of luck, and AFAIK you'll be the first to succeed, if you do succeed.
                That's a good incentive!

                Comment

                • Nick
                  Vanilla maintainer
                  • Apr 2007
                  • 9637

                  #9
                  I'm reading this thread with the way we've updated the Vanilla codebase in mind. I don't think there is really anywhere we can really help each other - Angband and Cthangband are really too far apart now - but it's very interesting to see the changes you're making and your reasons for them.

                  The V changes I'm hoping will be useful for future variants...
                  One for the Dark Lord on his dark throne
                  In the Land of Mordor where the Shadows lie.

                  Comment

                  • the Invisible Stalker
                    Adept
                    • Jul 2009
                    • 164

                    #10
                    Originally posted by AnonymousHero
                    (Java is particularly terrible here since it has signed bytes. SIGNED bytes! Wtf?)
                    Just be glad it doesn't have signed bits.

                    Comment

                    • Derakon
                      Prophet
                      • Dec 2009
                      • 9022

                      #11
                      Originally posted by the Invisible Stalker
                      Just be glad it doesn't have signed bits.
                      I guess then your options are +0 and -0.

                      Comment

                      • AnonymousHero
                        Veteran
                        • Jun 2007
                        • 1393

                        #12
                        Originally posted by the Invisible Stalker
                        Just be glad it doesn't have signed bits.
                        Well, let's see what they have to say about the inevitable trail of bodies... BITS! I MEAN BITS!.

                        Comment

                        • Dean Anderson
                          Adept
                          • Nov 2009
                          • 193

                          #13
                          I've been making slow but steady progress, and while I've not found anything near a showstopper, here are my top seven (it was going to be a top five but I thought of a couple more) irritations when porting Cthangband from C to C#...
                          1. C# is very finicky about casting. Much more so than C, where you can interchange pretty freely between numbers of different types (byte, int16, int32, signed and unsigned). Where C will generally simply let you copy a value from one type to another and not care if it doesn't fit - you'll just get roll-overs and possibly unexpected results - C# insists on explicit casting. All the time. Not a problem when you're writing new C# code, but a pain when porting old C code.
                          2. C doesn't do strings at all well. You have to fiddle around with arrays of characters, pointers, and null terminators. None of that is necessary in C# but again it's very irritating to convert because in C# you don't (explicitly) use pointers (you do actually use them all the time when using reference types, but they're implicit rather than explicit).
                          3. C lets you do if statements on numbers, for example if i is an int, you can write "if(i)" and the compiler will understand that you mean "if(i != 0)". C# will throw a compiler error because if statements need to look at a bool rather than an int. So you have to manually add the explicit comparison to zero on each one.
                          4. Speaking of bools, C# is very strict that a bool is not a number and a number is not a bool and never the twain shall meet. C doesn't really care, and the C in Angband uses its own preprocessor definitions of TRUE and FALSE which have nothing to do with C#'s true and false.
                          5. Did I mention preprocessor definitions? In C# these are all boolean. Either a term is #defined or it isn't. You can't #define a term as a value and then use the term as a macro for that value elsewhere in your code. Well, you can for value types, but the syntax is different. They're declared as variables but with the "const" decorator. You can't do things like "#define SUM(A,B) (A) + (B)" though.
                          6. In C#, everything is in an encapsulated unit of code. The majority of the time these are classes but you also have enumerations, interfaces and structs (although the latter are mostly there for API compatibility with C and there's no reason to ever use them in native C# code). There's no such thing as a global scope for variables or functions, they have to be declared inside a class or struct. The closest you can get to a global variable is to give a class a static property or even make the whole class static. In C, of course, every function and variable is global. So that variable in the C code that is declared as being external and then used 269 times in the file as if it were local? Every single one of those 269 times need to be prefixed with the name of the class in which you've declared it in C#.
                          7. In C you declare that a variable is an array by decorating the variable's name. In C# you do it by decorating its type. For example in C you have "int x[] = {0, 1, 2, 3, 4}". The equivalent code in C# is "int[] x = new int[] {0, 1, 2, 3, 4}". Similarly where C has "int x[5]", C# has "int[] x = new int[5]". These are functionally identical for the most part, but it's a pain having to change each declaration.


                          And of course, the thing that's really making me twitch is the fact that I know I'm going to be rewriting most of this code in the next stage of the process where I refactor everything. All the initialisation code I've ported? It's going to be in constructors, not static initialisation functions. All the global stuff? It's going to be in a nice object model, not just scattered around a whole bunch of static classes. All the loading and saving code that reads and writes files byte by byte and has to handle every global variable independently? It's going to just serialize and deserialize the object tree in a half dozen lines of code.

                          But I have to port it as-is first and get everything working before I start doing the refactoring. Trying to port and refactor the code in a single pass will just end up in a mess.

                          Comment

                          • the Invisible Stalker
                            Adept
                            • Jul 2009
                            • 164

                            #14
                            It's interesting for me, as someone with no C# experience, to see the list of differences. On the whole I prefer C's design choices, but not in all cases. I'd guess that in the course of dealing with point 1 on your list you'll find some bugs that have gone unnoticed for years because the Intel architecture is so ubiquitous. It's easy to get used to assuming things about integer sizes which aren't mandated by the standard.

                            Comment

                            • Dean Anderson
                              Adept
                              • Nov 2009
                              • 193

                              #15
                              They're not the only differences between the languages, of course; but they're the things that are fiddly to convert.

                              In terms of preference, it's generally a matter of horses for courses. C# is similar to Java in that its designed around you having a virtual machine in which security and safety are paramount. That's why it has so much more emphasis on casting things properly. You can't just throw around pointers and treat any bit of memory as any type of data. Many of the design choices are based around that, including the use of garbage collection based memory management.

                              Personally, I prefer C# to C but that's mostly because I've been using C# every day at work for over a decade, and other than Angband I've not used C in that time. So these days I'm far more familiar with the way C# works than the way C works. Plain C certainly has its advantages when it comes to places where you need to be close to the hardware. And it gives you fine detailed control over memory and the like. But those advantages can all to often become disadvantages at times when you don't need that closeness or control - at which point you tend to still need the detail and that can get really fiddly. And for OO code I definitely prefer C# or Java - both languages designed to be OO from the ground up - than C++, which has always struck me as a bit of a kludge.

                              Comment

                              Working...
                              😀
                              😂
                              🥰
                              😘
                              🤢
                              😎
                              😞
                              😡
                              👍
                              👎